1 // Written in the D programming language. 2 3 module wrapper.sodium.crypto_scalarmult; 4 5 import wrapper.sodium.core; // assure sodium got initialized 6 7 public 8 import deimos.sodium.crypto_scalarmult : crypto_scalarmult_BYTES, 9 crypto_scalarmult_bytes, 10 crypto_scalarmult_SCALARBYTES, 11 crypto_scalarmult_scalarbytes, 12 crypto_scalarmult_PRIMITIVE; 13 // crypto_scalarmult_primitive, 14 // crypto_scalarmult_base, 15 // crypto_scalarmult; 16 17 import wrapper.sodium.crypto_generichash : crypto_generichash_state, 18 crypto_generichash_BYTES, 19 crypto_generichash_init, 20 crypto_generichash_update, 21 crypto_generichash_final, 22 crypto_generichash_BYTES_MIN, 23 crypto_generichash_BYTES_MAX, 24 crypto_generichash_multi; 25 26 import nogc.exception: enforce; 27 28 string crypto_scalarmult_primitive() @nogc nothrow pure @trusted 29 { 30 import std.string : fromStringz; 31 static import deimos.sodium.crypto_scalarmult; 32 const(char)[] c_arr; 33 try 34 c_arr = fromStringz(deimos.sodium.crypto_scalarmult.crypto_scalarmult_primitive()); // strips terminating \0 35 catch (Exception t) { /* known not to throw */ } 36 return c_arr; 37 } 38 39 /** 40 * This function may be part of the keyexchange procedure: 41 * It takes over from the arguments given, invokes crypto_scalarmult und results in the hashed our_sharedkey as proposed in the documentaion. 42 * There is no heap allocation for secrets, yet enforce may require heap memory, thus inhibiting the attribute @nogc. 43 * It may throw in error conditions. 44 */ 45 void sharedkey_hashed(scope ubyte[] our_sharedkey, 46 const ubyte[crypto_scalarmult_SCALARBYTES] my_secretkey, 47 const ubyte[crypto_scalarmult_BYTES] my_publickey, 48 const bool my_pubkey_hashed_first, 49 const ubyte[crypto_scalarmult_BYTES] other_publickey) @nogc @trusted 50 { 51 /* I derive a shared key from my secret key and the other's public key */ 52 /* shared key = hash(q || client_publickey || server_publickey) */ 53 ubyte[crypto_scalarmult_BYTES] our_scalarmult_q; // = new ubyte[crypto_scalarmult_BYTES]; 54 enforce( crypto_scalarmult(our_scalarmult_q, my_secretkey, other_publickey), "sharedkey_hashed: Error result from calling crypto_scalarmult"); 55 /+ are there security concerns to pass our_scalarmult_q through RAM/cache ? 56 import wrapper.sodium.utils : sodium_memzero; 57 ubyte[][] MESSAGE_multi = [ 58 our_scalarmult_q, 59 (my_pubkey_hashed_first? my_publickey.dup : other_publickey.dup), 60 (my_pubkey_hashed_first? other_publickey.dup : my_publickey.dup) 61 ]; 62 scope(exit) sodium_memzero(MESSAGE_multi[0]); 63 crypto_generichash_multi(our_sharedkey, MESSAGE_multi); 64 +/ 65 enforce(our_sharedkey.length >= crypto_generichash_BYTES_MIN && our_sharedkey.length <= crypto_generichash_BYTES_MAX, "wrong length allocated for hash"); 66 crypto_generichash_state state; 67 crypto_generichash_init (&state, null, 0, our_sharedkey.length); 68 crypto_generichash_update(&state, our_scalarmult_q.ptr, our_scalarmult_q.length); 69 if (my_pubkey_hashed_first) { 70 crypto_generichash_update(&state, my_publickey.ptr, my_publickey.length); 71 crypto_generichash_update(&state, other_publickey.ptr, other_publickey.length); 72 } 73 else { 74 crypto_generichash_update(&state, other_publickey.ptr, other_publickey.length); 75 crypto_generichash_update(&state, my_publickey.ptr, my_publickey.length); 76 } 77 crypto_generichash_final (&state, our_sharedkey.ptr, our_sharedkey.length); 78 } 79 80 alias crypto_scalarmult_base = deimos.sodium.crypto_scalarmult.crypto_scalarmult_base; 81 82 pragma(inline, true) 83 bool crypto_scalarmult_base(out ubyte[crypto_scalarmult_BYTES] q, const ubyte[crypto_scalarmult_SCALARBYTES] n) @nogc pure @trusted 84 { 85 return crypto_scalarmult_base(q.ptr, n.ptr) == 0; 86 } 87 88 alias crypto_scalarmult = deimos.sodium.crypto_scalarmult.crypto_scalarmult; 89 90 pragma(inline, true) 91 bool crypto_scalarmult(out ubyte[crypto_scalarmult_BYTES] q, const ubyte[crypto_scalarmult_SCALARBYTES] n, const ubyte[crypto_scalarmult_BYTES] p) @nogc nothrow pure @trusted 92 { 93 return crypto_scalarmult(q.ptr, n.ptr, p.ptr) == 0; 94 } 95 96 97 @system 98 unittest 99 { 100 import std.stdio : writeln; 101 import wrapper.sodium.randombytes : randombytes; 102 import std.algorithm.comparison : equal; 103 import std.string : fromStringz; // @system 104 105 debug writeln("unittest block 1 from sodium.crypto_scalarmult.d"); 106 107 assert(crypto_scalarmult_bytes() == crypto_scalarmult_BYTES); 108 assert(crypto_scalarmult_scalarbytes() == crypto_scalarmult_SCALARBYTES); 109 assert(crypto_scalarmult_primitive() == crypto_scalarmult_PRIMITIVE); 110 111 112 //test keyexchange procedure 113 114 ubyte[crypto_scalarmult_SCALARBYTES] client_secretkey, server_secretkey; 115 ubyte[crypto_scalarmult_BYTES] client_publickey, server_publickey; 116 /+ +/ 117 ubyte[crypto_scalarmult_BYTES] scalarmult_q_by_client; 118 ubyte[crypto_scalarmult_BYTES] scalarmult_q_by_server; 119 crypto_generichash_state h; 120 /+ +/ 121 ubyte[crypto_generichash_BYTES] sharedkey_by_client, sharedkey_by_server; 122 123 /* Create client's secret and public keys */ 124 randombytes(client_secretkey); 125 crypto_scalarmult_base(client_publickey.ptr, client_secretkey.ptr); 126 /* Create server's secret and public keys */ 127 randombytes(server_secretkey); 128 crypto_scalarmult_base(server_publickey, server_secretkey); 129 130 /* The client derives a shared key from its secret key and the server's public key */ 131 /* shared key = h(q || client_publickey || server_publickey) */ 132 assert(crypto_scalarmult(scalarmult_q_by_client, client_secretkey, server_publickey)); 133 crypto_generichash_init (&h, null, 0U, /*crypto_generichash_BYTES*/ sharedkey_by_client.length); 134 crypto_generichash_update(&h, scalarmult_q_by_client.ptr, scalarmult_q_by_client.length); 135 crypto_generichash_update(&h, client_publickey.ptr, client_publickey.length); 136 crypto_generichash_update(&h, server_publickey.ptr, server_publickey.length); 137 crypto_generichash_final (&h, sharedkey_by_client.ptr, sharedkey_by_client.length); 138 139 /* The server derives a shared key from its secret key and the client's public key */ 140 /* shared key = h(q || client_publickey || server_publickey) */ 141 assert(crypto_scalarmult(scalarmult_q_by_server, server_secretkey, client_publickey)); 142 assert(scalarmult_q_by_client == scalarmult_q_by_server); 143 crypto_generichash_init (&h, null, 0U, /*crypto_generichash_BYTES*/ sharedkey_by_server.length); 144 crypto_generichash_update(&h, scalarmult_q_by_server.ptr, scalarmult_q_by_server.length); 145 crypto_generichash_update(&h, client_publickey.ptr, client_publickey.length); 146 crypto_generichash_update(&h, server_publickey.ptr, server_publickey.length); 147 crypto_generichash_final (&h, sharedkey_by_server.ptr, sharedkey_by_server.length); 148 149 /* sharedkey_by_client and sharedkey_by_server are identical */ 150 assert(sharedkey_by_client == sharedkey_by_server); 151 ubyte[crypto_generichash_BYTES] sharedkey_by_client_saved = sharedkey_by_client; 152 153 sharedkey_by_client[] = ubyte.init; 154 sharedkey_by_server[] = ubyte.init; 155 // same as before with less user code 156 sharedkey_hashed(sharedkey_by_client, client_secretkey, client_publickey, true, server_publickey); 157 sharedkey_hashed(sharedkey_by_server, server_secretkey, server_publickey, false, client_publickey); 158 assert(sharedkey_by_client == sharedkey_by_server); 159 assert(sharedkey_by_client == sharedkey_by_client_saved); 160 }