/ src / oprf.c
oprf.c
  1  /*
  2      @copyright 2022, Stefan Marsiske toprf@ctrlc.hu
  3      This file is part of liboprf.
  4  
  5      liboprf is free software: you can redistribute it and/or
  6      modify it under the terms of the GNU Lesser General Public License
  7      as published by the Free Software Foundation, either version 3 of
  8      the License, or (at your option) any later version.
  9  
 10      liboprf is distributed in the hope that it will be useful,
 11      but WITHOUT ANY WARRANTY; without even the implied warranty of
 12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13      GNU Lesser General Public License for more details.
 14  
 15      You should have received a copy of the License
 16      along with liboprf. If not, see <http://www.gnu.org/licenses/>.
 17  */
 18  
 19  #include <stdint.h>
 20  #include <stdio.h>
 21  #include <string.h>
 22  #include <arpa/inet.h>
 23  #include "oprf.h"
 24  #include "utils.h"
 25  #include "toprf.h"
 26  
 27  #ifdef CFRG_TEST_VEC
 28  #ifdef CFRG_OPRF_TEST_VEC
 29  #include "tests/cfrg_oprf_test_vector_decl.h"
 30  #else
 31  #include "tests/cfrg_test_vector_decl.h"
 32  #endif
 33  #endif
 34  
 35  #define VOPRF "OPRFV1"
 36  
 37  /**
 38   * This function generates an OPRF private key.
 39   *
 40   * This is almost the KeyGen OPRF function defined in the RFC: since
 41   * this lib does not implement V oprf, we don't need a pubkey and so
 42   * we don't bother with all that is related.
 43   *
 44   * @param [out] kU - the per-user OPRF private key
 45   */
 46  void oprf_KeyGen(uint8_t kU[crypto_core_ristretto255_SCALARBYTES]) {
 47  #if (defined CFRG_TEST_VEC && defined oprf_key_len)
 48    memcpy(kU,oprf_key,oprf_key_len);
 49  #else
 50    crypto_core_ristretto255_scalar_random(kU);
 51  #endif
 52  }
 53  
 54  /**
 55   * This function computes the OPRF output using input x, N, and domain separation
 56   * tag info.
 57   *
 58   * This is the Finalize OPRF function defined in the RFC.
 59   *
 60   * @param [in] x - a value used to compute OPRF (the same value that
 61   * was used as input to be blinded)
 62   * @param [in] x_len - the length of param x in bytes
 63   * @param [in] N - a serialized OPRF group element, a byte array of fixed length,
 64   * an output of oprf_Unblind
 65   * @param [in] info - a domain separation tag
 66   * @param [in] info_len - the length of param info in bytes
 67   * @param [out] y - an OPRF output
 68   * @return The function returns 0 if everything is correct.
 69   */
 70  int oprf_Finalize(const uint8_t *x, const uint16_t x_len,
 71                           const uint8_t N[crypto_core_ristretto255_BYTES],
 72                           uint8_t rwdU[OPRF_BYTES]) {
 73    // according to paper: hash(pwd||H0^k)
 74    // acccording to voprf IRTF CFRG specification: hash(htons(len(pwd))||pwd||
 75    //                                              htons(len(H0_k))||H0_k|||
 76    //                                              htons(len("Finalize-"VOPRF"-\x00-ristretto255-SHA512"))||"Finalize-"VOPRF"-\x00-ristretto255-SHA512")
 77    crypto_hash_sha512_state state;
 78    if(-1==sodium_mlock(&state,sizeof state)) {
 79      return -1;
 80    }
 81    crypto_hash_sha512_init(&state);
 82    // pwd
 83    uint16_t size=htons(x_len);
 84    crypto_hash_sha512_update(&state, (uint8_t*) &size, 2);
 85    crypto_hash_sha512_update(&state, x, x_len);
 86  #if (defined TRACE || defined CFRG_TEST_VEC)
 87    dump(x,x_len,"finalize input");
 88  #endif
 89    // H0_k
 90    size=htons(crypto_core_ristretto255_BYTES);
 91    crypto_hash_sha512_update(&state, (uint8_t*) &size, 2);
 92    crypto_hash_sha512_update(&state, N, crypto_core_ristretto255_BYTES);
 93    //const uint8_t DST[]="Finalize-"VOPRF"-\x00\x00\x01";
 94    const uint8_t DST[]="Finalize";
 95    const uint8_t DST_size=sizeof DST -1;
 96    //size=htons(DST_size);
 97    //crypto_hash_sha512_update(&state, (uint8_t*) &size, 2);
 98    crypto_hash_sha512_update(&state, DST, DST_size);
 99  
100    crypto_hash_sha512_final(&state, rwdU);
101    sodium_munlock(&state, sizeof state);
102  
103    return 0;
104  }
105  
106  /* expand_loop
107   10.    b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
108   */
109  static void expand_loop(const uint8_t *b_0, const uint8_t *b_i, const uint8_t i, const uint8_t *dst_prime, const uint8_t dst_prime_len, uint8_t *b_ii) {
110    uint8_t xored[crypto_hash_sha512_BYTES];
111    unsigned j;
112    for(j=0;j<sizeof xored;j++) xored[j]=b_0[j]^b_i[j];
113    // 8.  b_1 = H(b_0 || I2OSP(1, 1) || DST_prime)
114    crypto_hash_sha512_state state;
115    crypto_hash_sha512_init(&state);
116    crypto_hash_sha512_update(&state, xored, sizeof xored);
117    crypto_hash_sha512_update(&state,(const uint8_t*) &i, 1);
118    crypto_hash_sha512_update(&state, dst_prime, dst_prime_len);
119    crypto_hash_sha512_final(&state, b_ii);
120    sodium_memzero(&state,sizeof state);
121  }
122  
123  /*
124   * oprf_expand_message_xmd(msg, DST, len_in_bytes)
125   * as defined by https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/master/draft-irtf-cfrg-hash-to-curve.md#expand_message_xmd-hashtofield-expand-xmd
126   *
127   * Parameters:
128   * - H, a hash function (see requirements above).
129   * - b_in_bytes, b / 8 for b the output size of H in bits.
130   *   For example, for b = 256, b_in_bytes = 32.
131   * - r_in_bytes, the input block size of H, measured in bytes (see
132   *   discussion above). For example, for SHA-256, r_in_bytes = 64.
133   *
134   * Input:
135   * - msg, a byte string.
136   * - DST, a byte string of at most 255 bytes.
137   *   See below for information on using longer DSTs.
138   * - len_in_bytes, the length of the requested output in bytes.
139   *
140   * Output:
141   * - uniform_bytes, a byte string.
142   *
143   * Steps:
144   * 1.  ell = ceil(len_in_bytes / b_in_bytes)
145   * 2.  ABORT if ell > 255
146   * 3.  DST_prime = DST || I2OSP(len(DST), 1)
147   * 4.  Z_pad = I2OSP(0, r_in_bytes)
148   * 5.  l_i_b_str = I2OSP(len_in_bytes, 2)
149   * 6.  msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime
150   * 7.  b_0 = H(msg_prime)
151   * 8.  b_1 = H(b_0 || I2OSP(1, 1) || DST_prime)
152   * 9.  for i in (2, ..., ell):
153   * 10.    b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
154   * 11. uniform_bytes = b_1 || ... || b_ell
155   * 12. return substr(uniform_bytes, 0, len_in_bytes)
156   */
157  int oprf_expand_message_xmd(const uint8_t *msg, const uint16_t msg_len, const uint8_t *dst, const uint8_t dst_len, const uint8_t len_in_bytes, uint8_t *uniform_bytes) {
158    // 1.  ell = ceil(len_in_bytes / b_in_bytes)
159    const unsigned ell = (len_in_bytes + crypto_hash_sha512_BYTES-1) / crypto_hash_sha512_BYTES;
160  #ifdef TRACE
161    fprintf(stderr, "ell %d\n", ell);
162    dump(msg, msg_len, "msg");
163    dump(dst, dst_len, "dst");
164  #endif
165  
166    // 2.  ABORT if ell > 255
167    if(ell>255) return -1;
168    // 3.  DST_prime = DST || I2OSP(len(DST), 1)
169    if(dst_len==255) return -1;
170    uint8_t dst_prime[dst_len+1];
171    memcpy(dst_prime, dst, dst_len);
172    dst_prime[dst_len] = dst_len;
173  #ifdef TRACE
174    dump(dst_prime, sizeof dst_prime, "dst_prime");
175  #endif
176    // 4.  Z_pad = I2OSP(0, r_in_bytes)
177    //const uint8_t r_in_bytes = 128; // for sha512
178    uint8_t z_pad[128 /*r_in_bytes*/] = {0}; // supress gcc error: variable-sized object may not be initialized
179  #ifdef TRACE
180    dump(z_pad, sizeof z_pad, "z_pad");
181  #endif
182    // 5.  l_i_b_str = I2OSP(len_in_bytes, 2)
183    const uint16_t l_i_b = htons(len_in_bytes);
184    const uint8_t *l_i_b_str = (const uint8_t*) &l_i_b;
185    // 6.  msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime
186    uint8_t msg_prime[sizeof z_pad + msg_len + sizeof l_i_b + 1 + sizeof dst_prime];
187    uint8_t *ptr = msg_prime;
188    memcpy(ptr, z_pad, sizeof z_pad);
189    ptr += sizeof z_pad;
190    memcpy(ptr, msg, msg_len);
191    ptr += msg_len;
192    memcpy(ptr, l_i_b_str, sizeof l_i_b);
193    ptr += sizeof l_i_b;
194    *ptr = 0;
195    ptr++;
196    memcpy(ptr, dst_prime, sizeof dst_prime);
197  #ifdef TRACE
198    dump(msg_prime, sizeof msg_prime, "msg_prime");
199  #endif
200    // 7.  b_0 = H(msg_prime)
201    uint8_t b_0[crypto_hash_sha512_BYTES];
202    crypto_hash_sha512(b_0, msg_prime, sizeof msg_prime);
203  #ifdef TRACE
204    dump(b_0, sizeof b_0, "b_0");
205  #endif
206    // 8.  b_1 = H(b_0 || I2OSP(1, 1) || DST_prime)
207    uint8_t b_i[crypto_hash_sha512_BYTES];
208    crypto_hash_sha512_state state;
209    crypto_hash_sha512_init(&state);
210    crypto_hash_sha512_update(&state, b_0, sizeof b_0);
211    crypto_hash_sha512_update(&state,(const uint8_t*) &"\x01", 1);
212    crypto_hash_sha512_update(&state, dst_prime, (long long unsigned int) sizeof dst_prime);
213    crypto_hash_sha512_final(&state, b_i);
214  #ifdef TRACE
215    dump(b_i, sizeof b_i, "b_1");
216  #endif
217    // 9.  for i in (2, ..., ell):
218    unsigned left = len_in_bytes;
219    uint8_t *out = uniform_bytes;
220    unsigned clen = (left>sizeof b_i)?sizeof b_i:left;
221    memcpy(out, b_i, clen);
222    out+=clen;
223    left-=clen;
224    uint8_t b_ii[crypto_hash_sha512_BYTES];
225    for(uint8_t i=2;i<=ell;i+=2) {
226      // 11. uniform_bytes = b_1 || ... || b_ell
227      // 12. return substr(uniform_bytes, 0, len_in_bytes)
228      // 10.    b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
229      expand_loop(b_0, b_i, i, dst_prime, (uint8_t) (sizeof dst_prime), b_ii);
230      clen = (left>sizeof b_ii)?sizeof b_ii:left;
231      memcpy(out, b_ii, clen);
232      out+=clen;
233      left-=clen;
234      // unrolled next iteration so we don't have to swap b_i and b_ii
235      expand_loop(b_0, b_ii, i+1, dst_prime, (uint8_t) (sizeof dst_prime), b_i);
236      clen = (left>sizeof b_i)?sizeof b_i:left;
237      memcpy(out, b_i, clen);
238      out+=clen;
239      left-=clen;
240    }
241    return 0;
242  }
243  
244  /* hash-to-ristretto255 - as defined by  https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/master/draft-irtf-cfrg-hash-to-curve.md#hashing-to-ristretto255-appx-ristretto255
245   * Steps:
246   * -1. context-string = \x0 + htons(1) // contextString = I2OSP(modeBase(==0), 1) || I2OSP(suite.ID(==1), 2)
247   * 0. dst="HashToGroup-OPRFV1-\x00-ristretto255-SHA512")
248   * 1. uniform_bytes = expand_message(msg, DST, 64)
249   * 2. P = ristretto255_map(uniform_bytes)
250   * 3. return P
251   */
252  int voprf_hash_to_group(const uint8_t *msg, const uint16_t msg_len, uint8_t p[crypto_core_ristretto255_BYTES]) {
253    const uint8_t dst[] = "HashToGroup-"VOPRF"-\x00-ristretto255-SHA512";
254    const uint8_t dst_len = (sizeof dst) - 1;
255    uint8_t uniform_bytes[crypto_core_ristretto255_HASHBYTES]={0};
256    if(0!=sodium_mlock(uniform_bytes,sizeof uniform_bytes)) {
257      return -1;
258    }
259    if(0!=oprf_expand_message_xmd(msg, msg_len, dst, dst_len, crypto_core_ristretto255_HASHBYTES, uniform_bytes)) {
260      sodium_munlock(uniform_bytes,sizeof uniform_bytes);
261      return -1;
262    }
263  #if (defined TRACE || defined CFRG_TEST_VEC)
264    dump(uniform_bytes, sizeof uniform_bytes, "uniform_bytes");
265  #endif
266    crypto_core_ristretto255_from_hash(p, uniform_bytes);
267    sodium_munlock(uniform_bytes,sizeof uniform_bytes);
268  #if (defined TRACE || defined CFRG_TEST_VEC)
269    dump(p, crypto_core_ristretto255_BYTES, "hashed-to-curve");
270  #endif
271    return 0;
272  }
273  
274  /**
275   * This function converts input x into an element of the OPRF group, randomizes it
276   * by some scalar r, producing blinded, and outputs (r, blinded).
277   *
278   * This is the Blind OPRF function defined in the RFC.
279   *
280   * @param [in] x - the value to blind (for OPAQUE, this is pwdU, the user's
281   * password)
282   * @param [in] x_len - the length of param x in bytes
283   * @param [out] r - an OPRF scalar value used for randomization
284   * @param [out] blinded - a serialized OPRF group element, a byte array of fixed length,
285   * the blinded version of x, an input to oprf_Evaluate
286   * @return The function returns 0 if everything is correct.
287   */
288  int oprf_Blind(const uint8_t *x, const uint16_t x_len,
289                 uint8_t r[crypto_core_ristretto255_SCALARBYTES],
290                 uint8_t blinded[crypto_core_ristretto255_BYTES]) {
291  #if (defined TRACE || defined CFRG_TEST_VEC)
292    dump(x, x_len, "input");
293  #endif
294    uint8_t H0[crypto_core_ristretto255_BYTES];
295    if(0!=sodium_mlock(H0,sizeof H0)) {
296      return -1;
297    }
298    // sets α := (H^0(pw))^r
299    if(0!=voprf_hash_to_group(x, x_len, H0)) return -1;
300  #if (defined TRACE || defined CFRG_TEST_VEC)
301    dump(H0,sizeof H0, "H0");
302  #endif
303  
304    // U picks r
305  #ifdef CFRG_TEST_VEC
306    static int vecidx=0;
307    const unsigned char *rtest[2] = {blind_registration, blind_login};
308    const unsigned int rtest_len = 32;
309    memcpy(r,rtest[vecidx++ % 2],rtest_len);
310  #else
311    crypto_core_ristretto255_scalar_random(r);
312  #endif
313  
314  #ifdef TRACE
315    dump(r, crypto_core_ristretto255_SCALARBYTES, "r");
316  #endif
317    // H^0(pw)^r
318    if (crypto_scalarmult_ristretto255(blinded, r, H0) != 0) {
319      sodium_munlock(H0,sizeof H0);
320      return -1;
321    }
322    sodium_munlock(H0,sizeof H0);
323  #if (defined TRACE || defined CFRG_TEST_VEC)
324    dump(blinded, crypto_core_ristretto255_BYTES, "blinded");
325  #endif
326    return 0;
327  }
328  
329  /**
330   * This function evaluates input element blinded using private key k, yielding output
331   * element Z.
332   *
333   * This is the Evaluate OPRF function defined in the RFC.
334   * 
335   * @param [in] k - a private key (for OPAQUE, this is kU, the user's OPRF private
336   * key)
337   * @param [in] blinded - a serialized OPRF group element, a byte array of fixed length,
338   * an output of oprf_Blind (for OPAQUE, this is the blinded pwdU, the user's
339   * password)
340   * @param [out] Z - a serialized OPRF group element, a byte array of fixed length,
341   * an input to oprf_Unblind
342   * @return The function returns 0 if everything is correct.
343   */
344  int oprf_Evaluate(const uint8_t k[crypto_core_ristretto255_SCALARBYTES],
345                    const uint8_t blinded[crypto_core_ristretto255_BYTES],
346                    uint8_t Z[crypto_core_ristretto255_BYTES]) {
347    return crypto_scalarmult_ristretto255(Z, k, blinded);
348  }
349  
350  /**
351   * This function removes random scalar r from Z, yielding output N.
352   *
353   * This is the Unblind OPRF function defined in the RFC.
354   *
355   * @param [in] r - an OPRF scalar value used for randomization in oprf_Blind
356   * @param [in] Z - a serialized OPRF group element, a byte array of fixed length,
357   * an output of oprf_Evaluate
358   * @param [out] N - a serialized OPRF group element with random scalar r removed,
359   * a byte array of fixed length, an input to oprf_Finalize
360   * @return The function returns 0 if everything is correct.
361   */
362  int oprf_Unblind(const uint8_t r[crypto_core_ristretto255_SCALARBYTES],
363                          const uint8_t Z[crypto_core_ristretto255_BYTES],
364                          uint8_t N[crypto_core_ristretto255_BYTES]) {
365  #ifdef TRACE
366    dump(r, crypto_core_ristretto255_SCALARBYTES, "r ");
367    dump(Z, crypto_core_ristretto255_BYTES, "Z ");
368  #endif
369  
370    // (a) Checks that β ∈ G ∗ . If not, outputs (abort, sid , ssid ) and halts;
371    if(crypto_core_ristretto255_is_valid_point(Z) != 1) return -1;
372  
373    // (b) Computes rw := H(pw, β^1/r );
374    // invert r = 1/r
375    uint8_t ir[crypto_core_ristretto255_SCALARBYTES];
376    if(-1==sodium_mlock(ir, sizeof ir)) return -1;
377    if (crypto_core_ristretto255_scalar_invert(ir, r) != 0) {
378      sodium_munlock(ir, sizeof ir);
379      return -1;
380    }
381  #ifdef TRACE
382    dump((uint8_t*) ir, sizeof ir, "r^-1 ");
383  #endif
384  
385    // H0 = β^(1/r)
386    // beta^(1/r) = h(pwd)^k
387    if (crypto_scalarmult_ristretto255(N, ir, Z) != 0) {
388      sodium_munlock(ir, sizeof ir);
389      return -1;
390    }
391  #ifdef TRACE
392    dump(N, crypto_core_ristretto255_BYTES, "N ");
393  #endif
394  
395    sodium_munlock(ir, sizeof ir);
396    return 0;
397  }