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 }