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 }