liboprf/0000755000175000017500000000000014507270171012455 5ustar joostvbjoostvbliboprf/oprf.c0000644000175000017500000004002614507270063013571 0ustar joostvbjoostvb/* @copyright 2022, Stefan Marsiske toprf@ctrlc.hu This file is part of liboprf. liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the License along with liboprf. If not, see . */ #include #include #include #include #include "oprf.h" #include "utils.h" #include "toprf.h" #ifdef CFRG_TEST_VEC #include "tests/cfrg_test_vector_decl.h" #endif #ifndef HAVE_SODIUM_HKDF #include "aux/crypto_kdf_hkdf_sha512.h" #endif #define VOPRF "OPRFV1" static toprf_cfg proxy_cfg={0}; /** * This function generates an OPRF private key. * * This is almost the KeyGen OPRF function defined in the RFC: since * this lib does not implement V oprf, we don't need a pubkey and so * we don't bother with all that is related. * * @param [out] kU - the per-user OPRF private key */ void oprf_KeyGen(uint8_t kU[crypto_core_ristretto255_SCALARBYTES]) { #ifdef CFRG_TEST_VEC memcpy(kU,oprf_key,oprf_key_len); #else if(proxy_cfg.keygen) proxy_cfg.keygen(kU); else crypto_core_ristretto255_scalar_random(kU); #endif } /** * This function computes the OPRF output using input x, N, and domain separation * tag info. * * This is the Finalize OPRF function defined in the RFC. * * @param [in] x - a value used to compute OPRF (the same value that * was used as input to be blinded) * @param [in] x_len - the length of param x in bytes * @param [in] N - a serialized OPRF group element, a byte array of fixed length, * an output of oprf_Unblind * @param [in] info - a domain separation tag * @param [in] info_len - the length of param info in bytes * @param [out] y - an OPRF output * @return The function returns 0 if everything is correct. */ int oprf_Finalize(const uint8_t *x, const uint16_t x_len, const uint8_t N[crypto_core_ristretto255_BYTES], uint8_t rwdU[OPRF_BYTES]) { // according to paper: hash(pwd||H0^k) // acccording to voprf IRTF CFRG specification: hash(htons(len(pwd))||pwd|| // htons(len(H0_k))||H0_k||| // htons(len("Finalize-"VOPRF"-\x00-ristretto255-SHA512"))||"Finalize-"VOPRF"-\x00-ristretto255-SHA512") crypto_hash_sha512_state state; if(-1==sodium_mlock(&state,sizeof state)) { return -1; } crypto_hash_sha512_init(&state); // pwd uint16_t size=htons(x_len); crypto_hash_sha512_update(&state, (uint8_t*) &size, 2); crypto_hash_sha512_update(&state, x, x_len); #if (defined TRACE || defined CFRG_TEST_VEC) dump(x,x_len,"finalize input"); #endif // H0_k size=htons(crypto_core_ristretto255_BYTES); crypto_hash_sha512_update(&state, (uint8_t*) &size, 2); crypto_hash_sha512_update(&state, N, crypto_core_ristretto255_BYTES); //const uint8_t DST[]="Finalize-"VOPRF"-\x00\x00\x01"; const uint8_t DST[]="Finalize"; const uint8_t DST_size=sizeof DST -1; //size=htons(DST_size); //crypto_hash_sha512_update(&state, (uint8_t*) &size, 2); crypto_hash_sha512_update(&state, DST, DST_size); // - concat(y, Harden(y, params)) uint8_t concated[2*crypto_hash_sha512_BYTES]; uint8_t *y=concated, *hardened=concated+crypto_hash_sha512_BYTES; if(-1==sodium_mlock(&concated,sizeof concated)) { sodium_munlock(&state, sizeof state); return -1; } crypto_hash_sha512_final(&state, y); sodium_munlock(&state, sizeof state); #if (defined TRACE || defined CFRG_TEST_VEC) dump((uint8_t*) y, crypto_hash_sha512_BYTES, "output "); #endif #ifdef CFRG_TEST_VEC // testvectors use identity as MHF memcpy(hardened, y, crypto_hash_sha512_BYTES); #else // salt - according to the irtf draft this could be all zeroes // TODO parametrize via params and fall back to default uint8_t salt[crypto_pwhash_SALTBYTES]={0}; if (crypto_pwhash(hardened, crypto_hash_sha512_BYTES, (const char*) y, crypto_hash_sha512_BYTES, salt, crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, crypto_pwhash_ALG_DEFAULT) != 0) { /* out of memory */ sodium_munlock(concated, sizeof(concated)); return -1; } #endif #if (defined TRACE|| defined CFRG_TEST_VEC) dump(concated, sizeof concated, "concated"); #endif crypto_kdf_hkdf_sha512_extract(rwdU, NULL, 0, concated, sizeof concated); sodium_munlock(concated, sizeof(concated)); #if (defined TRACE|| defined CFRG_TEST_VEC) dump((uint8_t*) rwdU, OPRF_BYTES, "rwdU "); #endif return 0; } /* expand_loop 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) */ 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) { uint8_t xored[crypto_hash_sha512_BYTES]; unsigned j; for(j=0;j 255 * 3. DST_prime = DST || I2OSP(len(DST), 1) * 4. Z_pad = I2OSP(0, r_in_bytes) * 5. l_i_b_str = I2OSP(len_in_bytes, 2) * 6. msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime * 7. b_0 = H(msg_prime) * 8. b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) * 9. for i in (2, ..., ell): * 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) * 11. uniform_bytes = b_1 || ... || b_ell * 12. return substr(uniform_bytes, 0, len_in_bytes) */ int expand_message_xmd(const uint8_t *msg, const uint8_t msg_len, const uint8_t *dst, const uint8_t dst_len, const uint8_t len_in_bytes, uint8_t *uniform_bytes) { // 1. ell = ceil(len_in_bytes / b_in_bytes) const uint8_t ell = (len_in_bytes + crypto_hash_sha512_BYTES-1) / crypto_hash_sha512_BYTES; #ifdef TRACE fprintf(stderr, "ell %d\n", ell); dump(msg, msg_len, "msg"); dump(dst, dst_len, "dst"); #endif // 2. ABORT if ell > 255 if(ell>255) return -1; // 3. DST_prime = DST || I2OSP(len(DST), 1) uint8_t dst_prime[dst_len+1]; memcpy(dst_prime, dst, dst_len); dst_prime[dst_len] = dst_len; #ifdef TRACE dump(dst_prime, sizeof dst_prime, "dst_prime"); #endif // 4. Z_pad = I2OSP(0, r_in_bytes) //const uint8_t r_in_bytes = 128; // for sha512 uint8_t z_pad[128 /*r_in_bytes*/] = {0}; // supress gcc error: variable-sized object may not be initialized #ifdef TRACE dump(z_pad, sizeof z_pad, "z_pad"); #endif // 5. l_i_b_str = I2OSP(len_in_bytes, 2) const uint16_t l_i_b = htons(len_in_bytes); const uint8_t *l_i_b_str = (uint8_t*) &l_i_b; // 6. msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime uint8_t msg_prime[sizeof z_pad + msg_len + sizeof l_i_b + 1 + sizeof dst_prime], *ptr = msg_prime; memcpy(ptr, z_pad, sizeof z_pad); ptr += sizeof z_pad; memcpy(ptr, msg, msg_len); ptr += msg_len; memcpy(ptr, l_i_b_str, sizeof l_i_b); ptr += sizeof l_i_b; *ptr = 0; ptr++; memcpy(ptr, dst_prime, sizeof dst_prime); #ifdef TRACE dump(msg_prime, sizeof msg_prime, "msg_prime"); #endif // 7. b_0 = H(msg_prime) uint8_t b_0[crypto_hash_sha512_BYTES]; crypto_hash_sha512(b_0, msg_prime, sizeof msg_prime); #ifdef TRACE dump(b_0, sizeof b_0, "b_0"); #endif // 8. b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) uint8_t b_i[crypto_hash_sha512_BYTES]; crypto_hash_sha512_state state; crypto_hash_sha512_init(&state); crypto_hash_sha512_update(&state, b_0, sizeof b_0); crypto_hash_sha512_update(&state,(uint8_t*) &"\x01", 1); crypto_hash_sha512_update(&state, dst_prime, sizeof dst_prime); crypto_hash_sha512_final(&state, b_i); #ifdef TRACE dump(b_i, sizeof b_i, "b_1"); #endif // 9. for i in (2, ..., ell): unsigned left = len_in_bytes; uint8_t *out = uniform_bytes; unsigned clen = (left>sizeof b_i)?sizeof b_i:left; memcpy(out, b_i, clen); out+=clen; left-=clen; int i; uint8_t b_ii[crypto_hash_sha512_BYTES]; for(i=2;i<=ell;i+=2) { // 11. uniform_bytes = b_1 || ... || b_ell // 12. return substr(uniform_bytes, 0, len_in_bytes) // 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) expand_loop(b_0, b_i, i, dst_prime, sizeof dst_prime, b_ii); clen = (left>sizeof b_ii)?sizeof b_ii:left; memcpy(out, b_ii, clen); out+=clen; left-=clen; // unrolled next iteration so we don't have to swap b_i and b_ii expand_loop(b_0, b_ii, i+1, dst_prime, sizeof dst_prime, b_i); clen = (left>sizeof b_i)?sizeof b_i:left; memcpy(out, b_i, clen); out+=clen; left-=clen; } return 0; } /* 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 * Steps: * -1. context-string = \x0 + htons(1) // contextString = I2OSP(modeBase(==0), 1) || I2OSP(suite.ID(==1), 2) * 0. dst="HashToGroup-OPRFV1-\x00-ristretto255-SHA512") * 1. uniform_bytes = expand_message(msg, DST, 64) * 2. P = ristretto255_map(uniform_bytes) * 3. return P */ int voprf_hash_to_group(const uint8_t *msg, const uint8_t msg_len, uint8_t p[crypto_core_ristretto255_BYTES]) { const uint8_t dst[] = "HashToGroup-"VOPRF"-\x00-ristretto255-SHA512"; const uint8_t dst_len = (sizeof dst) - 1; uint8_t uniform_bytes[crypto_core_ristretto255_HASHBYTES]={0}; if(0!=sodium_mlock(uniform_bytes,sizeof uniform_bytes)) { return -1; } if(0!=expand_message_xmd(msg, msg_len, dst, dst_len, crypto_core_ristretto255_HASHBYTES, uniform_bytes)) { sodium_munlock(uniform_bytes,sizeof uniform_bytes); return -1; } #if (defined TRACE || defined CFRG_TEST_VEC) dump(uniform_bytes, sizeof uniform_bytes, "uniform_bytes"); #endif crypto_core_ristretto255_from_hash(p, uniform_bytes); sodium_munlock(uniform_bytes,sizeof uniform_bytes); #if (defined TRACE || defined CFRG_TEST_VEC) dump(p, crypto_core_ristretto255_BYTES, "hashed-to-curve"); #endif return 0; } /** * This function converts input x into an element of the OPRF group, randomizes it * by some scalar r, producing blinded, and outputs (r, blinded). * * This is the Blind OPRF function defined in the RFC. * * @param [in] x - the value to blind (for OPAQUE, this is pwdU, the user's * password) * @param [in] x_len - the length of param x in bytes * @param [out] r - an OPRF scalar value used for randomization * @param [out] blinded - a serialized OPRF group element, a byte array of fixed length, * the blinded version of x, an input to oprf_Evaluate * @return The function returns 0 if everything is correct. */ int oprf_Blind(const uint8_t *x, const uint16_t x_len, uint8_t r[crypto_core_ristretto255_SCALARBYTES], uint8_t blinded[crypto_core_ristretto255_BYTES]) { #if (defined TRACE || defined CFRG_TEST_VEC) dump(x, x_len, "input"); #endif uint8_t H0[crypto_core_ristretto255_BYTES]; if(0!=sodium_mlock(H0,sizeof H0)) { return -1; } // sets α := (H^0(pw))^r if(0!=voprf_hash_to_group(x, x_len, H0)) return -1; #if (defined TRACE || defined CFRG_TEST_VEC) dump(H0,sizeof H0, "H0 "); #endif // U picks r #ifdef CFRG_TEST_VEC static int vecidx=0; const unsigned char *rtest[2] = {blind_registration, blind_login}; const unsigned int rtest_len = 32; memcpy(r,rtest[vecidx++ % 2],rtest_len); #else crypto_core_ristretto255_scalar_random(r); #endif #ifdef TRACE dump(r, crypto_core_ristretto255_SCALARBYTES, "r"); #endif // H^0(pw)^r if (crypto_scalarmult_ristretto255(blinded, r, H0) != 0) { sodium_munlock(H0,sizeof H0); return -1; } sodium_munlock(H0,sizeof H0); #if (defined TRACE || defined CFRG_TEST_VEC) dump(blinded, crypto_core_ristretto255_BYTES, "blinded"); #endif return 0; } /** * This function evaluates input element blinded using private key k, yielding output * element Z. * * This is the Evaluate OPRF function defined in the RFC. If the * internal proxy_cfg variable has been set using oprf_set_evalproxy() then * the Evaluation will be a threshold computation. * * @param [in] k - a private key (for OPAQUE, this is kU, the user's OPRF private * key) - if proxy_cfg is set, than this value will be ignored! * @param [in] blinded - a serialized OPRF group element, a byte array of fixed length, * an output of oprf_Blind (for OPAQUE, this is the blinded pwdU, the user's * password) * @param [out] Z - a serialized OPRF group element, a byte array of fixed length, * an input to oprf_Unblind * @return The function returns 0 if everything is correct. */ int oprf_Evaluate(const uint8_t k[crypto_core_ristretto255_SCALARBYTES], const uint8_t blinded[crypto_core_ristretto255_BYTES], uint8_t Z[crypto_core_ristretto255_BYTES]) { if(proxy_cfg.eval) return proxy_cfg.eval(k, blinded, Z); return crypto_scalarmult_ristretto255(Z, k, blinded); } /** * This function removes random scalar r from Z, yielding output N. * * This is the Unblind OPRF function defined in the RFC. * * @param [in] r - an OPRF scalar value used for randomization in oprf_Blind * @param [in] Z - a serialized OPRF group element, a byte array of fixed length, * an output of oprf_Evaluate * @param [out] N - a serialized OPRF group element with random scalar r removed, * a byte array of fixed length, an input to oprf_Finalize * @return The function returns 0 if everything is correct. */ int oprf_Unblind(const uint8_t r[crypto_core_ristretto255_SCALARBYTES], const uint8_t Z[crypto_core_ristretto255_BYTES], uint8_t N[crypto_core_ristretto255_BYTES]) { #ifdef TRACE dump((uint8_t*) r, crypto_core_ristretto255_SCALARBYTES, "r "); dump((uint8_t*) Z, crypto_core_ristretto255_BYTES, "Z "); #endif // (a) Checks that β ∈ G ∗ . If not, outputs (abort, sid , ssid ) and halts; if(crypto_core_ristretto255_is_valid_point(Z) != 1) return -1; // (b) Computes rw := H(pw, β^1/r ); // invert r = 1/r uint8_t ir[crypto_core_ristretto255_SCALARBYTES]; if(-1==sodium_mlock(ir, sizeof ir)) return -1; if (crypto_core_ristretto255_scalar_invert(ir, r) != 0) { sodium_munlock(ir, sizeof ir); return -1; } #ifdef TRACE dump((uint8_t*) ir, sizeof ir, "r^-1 "); #endif // H0 = β^(1/r) // beta^(1/r) = h(pwd)^k if (crypto_scalarmult_ristretto255(N, ir, Z) != 0) { sodium_munlock(ir, sizeof ir); return -1; } #ifdef TRACE dump((uint8_t*) N, crypto_core_ristretto255_BYTES, "N "); #endif sodium_munlock(ir, sizeof ir); return 0; } int oprf_set_evalproxy(const toprf_evalcb eval, const toprf_keygencb keygen) { if(eval == NULL) return 1; if(keygen == NULL) return 1; proxy_cfg.eval=eval; proxy_cfg.keygen=keygen; return 0; } void oprf_clear_evalproxy(void) { proxy_cfg.eval=0; proxy_cfg.keygen=0; } liboprf/LICENSE0000644000175000017500000001674414507270063013476 0ustar joostvbjoostvb GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. liboprf/aux/0000755000175000017500000000000014507270063013252 5ustar joostvbjoostvbliboprf/aux/crypto_kdf_hkdf_sha512.h0000644000175000017500000000257114507270063017653 0ustar joostvbjoostvb#ifndef crypto_kdf_hkdf_sha512_H #define crypto_kdf_hkdf_sha512_H #include #include #include #include #ifdef __cplusplus # ifdef __GNUC__ # pragma GCC diagnostic ignored "-Wlong-long" # endif extern "C" { #endif #define crypto_kdf_hkdf_sha512_KEYBYTES crypto_auth_hmacsha512_BYTES SODIUM_EXPORT size_t crypto_kdf_hkdf_sha512_keybytes(void); #define crypto_kdf_hkdf_sha512_BYTES_MIN 0U SODIUM_EXPORT size_t crypto_kdf_hkdf_sha512_bytes_min(void); #define crypto_kdf_hkdf_sha512_BYTES_MAX (0xff * crypto_auth_hmacsha512_BYTES) SODIUM_EXPORT size_t crypto_kdf_hkdf_sha512_bytes_max(void); SODIUM_EXPORT int crypto_kdf_hkdf_sha512_extract(unsigned char prk[crypto_kdf_hkdf_sha512_KEYBYTES], const unsigned char *salt, size_t salt_len, const unsigned char *ikm, size_t ikm_len) __attribute__ ((nonnull(1))); SODIUM_EXPORT void crypto_kdf_hkdf_sha512_keygen(unsigned char prk[crypto_kdf_hkdf_sha512_KEYBYTES]) __attribute__ ((nonnull)); SODIUM_EXPORT int crypto_kdf_hkdf_sha512_expand(unsigned char *out, size_t out_len, const char *ctx, size_t ctx_len, const unsigned char prk[crypto_kdf_hkdf_sha512_KEYBYTES]) __attribute__ ((nonnull(1))); #ifdef __cplusplus } #endif #endif liboprf/aux/kdf_hkdf_sha512.c0000644000175000017500000000605114507270063016243 0ustar joostvbjoostvb#include #include #include "sodium/crypto_auth_hmacsha512.h" #include "sodium/crypto_kdf.h" #include "crypto_kdf_hkdf_sha512.h" #include "sodium/randombytes.h" #include "sodium/utils.h" int crypto_kdf_hkdf_sha512_extract( unsigned char prk[crypto_kdf_hkdf_sha512_KEYBYTES], const unsigned char *salt, size_t salt_len, const unsigned char *ikm, size_t ikm_len) { crypto_auth_hmacsha512_state st; crypto_auth_hmacsha512_init(&st, salt, salt_len); crypto_auth_hmacsha512_update(&st, ikm, ikm_len); crypto_auth_hmacsha512_final(&st, prk); sodium_memzero(&st, sizeof st); return 0; } void crypto_kdf_hkdf_sha512_keygen(unsigned char prk[crypto_kdf_hkdf_sha512_KEYBYTES]) { randombytes_buf(prk, crypto_kdf_hkdf_sha512_KEYBYTES); } int crypto_kdf_hkdf_sha512_expand(unsigned char *out, size_t out_len, const char *ctx, size_t ctx_len, const unsigned char prk[crypto_kdf_hkdf_sha512_KEYBYTES]) { crypto_auth_hmacsha512_state st; unsigned char tmp[crypto_auth_hmacsha512_BYTES]; size_t i; size_t left; unsigned char counter = 1U; if (out_len > crypto_kdf_hkdf_sha512_BYTES_MAX) { errno = EINVAL; return -1; } for (i = (size_t) 0U; i + crypto_auth_hmacsha512_BYTES <= out_len; i += crypto_auth_hmacsha512_BYTES) { crypto_auth_hmacsha512_init(&st, prk, crypto_kdf_hkdf_sha512_KEYBYTES); if (i != (size_t) 0U) { crypto_auth_hmacsha512_update(&st, &out[i - crypto_auth_hmacsha512_BYTES], crypto_auth_hmacsha512_BYTES); } crypto_auth_hmacsha512_update(&st, (const unsigned char *) ctx, ctx_len); crypto_auth_hmacsha512_update(&st, &counter, (size_t) 1U); crypto_auth_hmacsha512_final(&st, &out[i]); counter++; } if ((left = out_len & (crypto_auth_hmacsha512_BYTES - 1U)) != (size_t) 0U) { crypto_auth_hmacsha512_init(&st, prk, crypto_kdf_hkdf_sha512_KEYBYTES); if (i != (size_t) 0U) { crypto_auth_hmacsha512_update(&st, &out[i - crypto_auth_hmacsha512_BYTES], crypto_auth_hmacsha512_BYTES); } crypto_auth_hmacsha512_update(&st, (const unsigned char *) ctx, ctx_len); crypto_auth_hmacsha512_update(&st, &counter, (size_t) 1U); crypto_auth_hmacsha512_final(&st, tmp); memcpy(&out[i], tmp, left); sodium_memzero(tmp, sizeof tmp); } sodium_memzero(&st, sizeof st); return 0; } size_t crypto_kdf_hkdf_sha512_keybytes(void) { return crypto_kdf_hkdf_sha512_KEYBYTES; } size_t crypto_kdf_hkdf_sha512_bytes_min(void) { return crypto_kdf_hkdf_sha512_BYTES_MIN; } size_t crypto_kdf_hkdf_sha512_bytes_max(void) { return crypto_kdf_hkdf_sha512_BYTES_MAX; } liboprf/dkg.h0000644000175000017500000000231214507270063013371 0ustar joostvbjoostvb#ifndef DKG_H #define DKG_H #include #include typedef struct { uint8_t index; uint8_t value[crypto_core_ristretto255_SCALARBYTES]; } __attribute((packed)) TOPRF_Share; int dkg_start(const uint8_t n, const uint8_t threshold, uint8_t commitments[threshold][crypto_core_ristretto255_BYTES], TOPRF_Share shares[n][2]); int dkg_verify_commitments(const uint8_t n, const uint8_t threshold, const uint8_t self, const uint8_t commitments[n][threshold][crypto_core_ristretto255_BYTES], const TOPRF_Share shares[n][2], uint8_t complaints[n], uint8_t *complaints_len); void dkg_finish(const uint8_t n, const uint8_t qual[n], const TOPRF_Share shares[n][2], const uint8_t self, TOPRF_Share *xi, TOPRF_Share *x_i); void dkg_reconstruct(const size_t response_len, const TOPRF_Share responses[response_len][2], uint8_t result[crypto_scalarmult_ristretto255_BYTES]); #endif // DKG_H liboprf/dkg.c0000644000175000017500000003071114507270063013370 0ustar joostvbjoostvb#include #include #include #include "toprf.h" #include "utils.h" typedef struct { uint8_t index; uint8_t value[crypto_core_ristretto255_SCALARBYTES]; } __attribute((packed)) TOPRF_Share; // nothing up my sleeve generator H, generated with: // hash_to_group((uint8_t*)"DKG Generator H on ristretto255", 32, H) static const uint8_t H[crypto_core_ristretto255_BYTES]= { 0x66, 0x4e, 0x4c, 0xb5, 0x89, 0x0e, 0xb3, 0xe4, 0xc0, 0xd5, 0x48, 0x02, 0x74, 0x8a, 0xb2, 0x25, 0xf9, 0x73, 0xda, 0xe5, 0xc0, 0xef, 0xc1, 0x68, 0xf4, 0x4d, 0x1b, 0x60, 0x28, 0x97, 0x8f, 0x07}; #ifdef UNIT_TEST extern int debug; #endif //UNIT_TEST static void polynom(const uint8_t j, const uint8_t threshold, const uint8_t a[threshold][crypto_core_ristretto255_SCALARBYTES], TOPRF_Share *result) { //f(z) = a_0 + a_1*z + a_2*z^2 + a_3*z^3 + ⋯ + (a_t)*(z^t) result->index=j; // f(z) = result = a[0] +..... memcpy(result->value, a[0], crypto_core_ristretto255_SCALARBYTES); // z = j uint8_t z[crypto_core_ristretto255_SCALARBYTES]={j}; // z^t -> for(int t=1;tvalue, result->value, tmp); } } #ifdef UNIT_TEST static int test_dkg_start(const uint8_t n, const uint8_t a[crypto_core_ristretto255_SCALARBYTES], const uint8_t b[crypto_core_ristretto255_SCALARBYTES], const TOPRF_Share shares[n][2]); #endif // UNIT_TEST int dkg_start(const uint8_t n, const uint8_t threshold, uint8_t commitments[threshold][crypto_core_ristretto255_BYTES], TOPRF_Share shares[n][2]) { uint8_t a[threshold][crypto_core_ristretto255_SCALARBYTES]; uint8_t b[threshold][crypto_core_ristretto255_SCALARBYTES]; for(int k=0;kvalue, 0, crypto_core_ristretto255_SCALARBYTES); memset(x_i->value, 0, crypto_core_ristretto255_SCALARBYTES); for(int i=0;qual[i] && ivalue, xi->value, shares[qual[i]-1][0].value); //dump((uint8_t*)&shares[qual[i]-1][0], sizeof(TOPRF_Share), "s[%d,%d] ", qual[i], self); crypto_core_ristretto255_scalar_add(x_i->value, x_i->value, shares[qual[i]-1][1].value); //dump((uint8_t*)&shares[qual[i]-1][1], sizeof(TOPRF_Share), "S[%d,%d] ", qual[i], self); } //dump(xi->value, crypto_core_ristretto255_SCALARBYTES, "x[%d] ", self); //dump(x_i->value, crypto_core_ristretto255_SCALARBYTES, "x'[%d] ", self); } void dkg_reconstruct(const size_t response_len, const TOPRF_Share responses[response_len][2], uint8_t result[crypto_scalarmult_ristretto255_BYTES]) { uint8_t lpoly[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t tmp[crypto_scalarmult_ristretto255_SCALARBYTES]; memset(result,0,crypto_scalarmult_ristretto255_BYTES); result[0]=0; uint8_t indexes[response_len]; for(size_t i=0;iindex=s->index; crypto_scalarmult_ristretto255_base(r->value, s->value); } static int test_dkg_start(const uint8_t n, const uint8_t a[crypto_core_ristretto255_SCALARBYTES], const uint8_t b[crypto_core_ristretto255_SCALARBYTES], const TOPRF_Share shares[n][2]) { const size_t response_len = 3; uint8_t responses[response_len][TOPRF_Part_BYTES]; uint8_t result[crypto_scalarmult_ristretto255_BYTES]; uint8_t v[crypto_scalarmult_ristretto255_BYTES]; topart((TOPRF_Part *) responses[0], &shares[4][0]); topart((TOPRF_Part *) responses[1], &shares[2][0]); topart((TOPRF_Part *) responses[2], &shares[0][0]); if(toprf_thresholdmult(response_len, responses, result)) return 1; crypto_scalarmult_ristretto255_base(v, a); if(memcmp(v,result,sizeof v)!=0) { fprintf(stderr,"\e[0;31mmeh!\e[0m\n"); return 1; } topart((TOPRF_Part *) responses[0], &shares[4][1]); topart((TOPRF_Part *) responses[1], &shares[2][1]); topart((TOPRF_Part *) responses[2], &shares[0][1]); if(toprf_thresholdmult(response_len, responses, result)) return 1; crypto_scalarmult_ristretto255_base(v, b); if(memcmp(v,result,sizeof v)!=0) { fprintf(stderr,"\e[0;31mfailed to verify shares from dkg_start!\e[0m\n"); return 1; } return 0; } static int test_dkg_finish(const uint8_t n, const TOPRF_Share shares[n][2]) { const size_t response_len = 3; uint8_t responses[response_len][TOPRF_Part_BYTES]; uint8_t v0[crypto_scalarmult_ristretto255_BYTES]={0}; uint8_t v1[crypto_scalarmult_ristretto255_BYTES]={0}; dump((uint8_t*) &shares[4][0], sizeof(TOPRF_Share), "&shares[4][0] "); topart((TOPRF_Part *) responses[0], &shares[4][0]); topart((TOPRF_Part *) responses[1], &shares[2][0]); topart((TOPRF_Part *) responses[2], &shares[0][0]); //topart((TOPRF_Part *) responses[3], &shares[1][0]); //topart((TOPRF_Part *) responses[4], &shares[3][0]); if(toprf_thresholdmult(response_len, responses, v0)) return 1; dump(v0,sizeof v0, "v0 "); topart((TOPRF_Part *) responses[0], &shares[3][0]); topart((TOPRF_Part *) responses[1], &shares[1][0]); topart((TOPRF_Part *) responses[2], &shares[0][0]); //topart((TOPRF_Part *) responses[3], &shares[2][0]); //topart((TOPRF_Part *) responses[4], &shares[4][0]); if(toprf_thresholdmult(response_len, responses, v1)) return 1; dump(v1,sizeof v1, "v1 "); if(memcmp(v0,v1,sizeof v1)!=0) { fprintf(stderr,"\e[0;31mfailed to verify shares from dkg_finish!\e[0m\n"); return 1; } return 0; } int main(void) { uint8_t n=5, threshold=3; uint8_t commitments[n][threshold][crypto_core_ristretto255_BYTES]; TOPRF_Share shares[n][n][2]; for(int i=0;i #include "oprf.h" #include "toprf.h" /* @copyright 2023, Stefan Marsiske toprf@ctrlc.hu This file is part of liboprf. liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the License along with liboprf. If not, see . */ // implements TOPRF from https://eprint.iacr.org/2017/363 // quote from page 9 (first line is last on page 8) // The underlying PRF, fk(x) = H2(x, (H1(x))k), remains unchanged, but the // key k is shared using Shamir secret-sharing across n servers, where server Si // stores the key share ki. The initialization of such secret-sharing can be done via // a Distributed Key Generation (DKG) for discrete-log-based systems, e.g. [16], // and in Figure 2 we assume it is done with a UC functionality FDKG which we // discuss further below. For evaluation, given any subset SE of t + 1 servers, the // user U sends to each of them the same message a = (H′(x))r for random r, // exactly as in the single-server OPRF protocol 2HashDH. If each server Si in SE // returned bi = aki then U could reconstruct the value ak using standard Lagrange // interpolation in the exponent, i.e. ak = � i∈SE bλi i with the Lagrange coefficients // λi computed using the indexes of servers in SE. After computing ak, the value // of fk(x) is computed by U by deblinding ak exactly as in the case of protocol // 2HashDH. Note that this takes a single exponentiation for each server and two // exponentiations for the user (to compute a and to deblind ak) plus one multi- // exponentiation by U to compute the Lagrange interpolation on the bi values. // run with // gcc -o toprf -g -Wall toprf.c -lsodium liboprf.a typedef struct { uint8_t index; uint8_t value[crypto_core_ristretto255_SCALARBYTES]; } __attribute((packed)) TOPRF_Share; typedef struct { uint8_t index; uint8_t value[crypto_core_ristretto255_BYTES]; } __attribute((packed)) TOPRF_Part; void coeff(const int index, const int peers_len, const uint8_t peers[peers_len], uint8_t *result) { uint8_t iscalar[crypto_scalarmult_ristretto255_SCALARBYTES]={0}; iscalar[0]=index; uint8_t divident[crypto_scalarmult_ristretto255_SCALARBYTES]={0}; divident[0]=1; uint8_t divisor[crypto_scalarmult_ristretto255_SCALARBYTES]={0}; divisor[0]=1; for(int j=0;jvalue, lpoly); TOPRF_Part *Z=(TOPRF_Part*) _Z; if(oprf_Evaluate(kl,blinded, Z->value)) return 1; return 0; } void toprf_thresholdcombine(const size_t response_len, const uint8_t _responses[response_len][TOPRF_Part_BYTES], uint8_t result[crypto_scalarmult_ristretto255_BYTES]) { const TOPRF_Part *responses=(TOPRF_Part*) _responses; memset(result,0,crypto_scalarmult_ristretto255_BYTES); for(size_t i=0;i #include #include "oprf.h" #include "toprf.h" int main(void) { // setup // todo use dkg const unsigned peers = 3, threshold = 2; uint8_t k[crypto_core_ristretto255_SCALARBYTES]; crypto_core_ristretto255_scalar_random(k); // split k into shares uint8_t shares[peers][TOPRF_Share_BYTES]; toprf_create_shares(k, peers, threshold, shares); // start the OPRF const uint8_t password[8]="password"; uint8_t r[crypto_core_ristretto255_SCALARBYTES]; uint8_t alpha[crypto_core_ristretto255_BYTES]; // we blind once if(oprf_Blind(password, sizeof password, r, alpha)) return 1; // until here all is like with the non-threshold version // calculate points of shares // this really happens at each peer separately uint8_t xresps[peers][TOPRF_Part_BYTES]; for(size_t i=0;i #include #include "toprf.h" #define OPRF_BYTES 64 /** * This function generates an OPRF private key. * * This is almost the KeyGen OPRF function defined in the RFC: since * this lib does not implement V oprf, we don't need a pubkey and so * we don't bother with all that is related. * * @param [out] kU - the per-user OPRF private key */ void oprf_KeyGen(uint8_t kU[crypto_core_ristretto255_SCALARBYTES]); /** * This function computes the OPRF output using input x, N, and domain separation * tag info. * * This is the Finalize OPRF function defined in the RFC. * * @param [in] x - a value used to compute OPRF (the same value that * was used as input to be blinded) * @param [in] x_len - the length of param x in bytes * @param [in] N - a serialized OPRF group element, a byte array of fixed length, * an output of oprf_Unblind * @param [in] info - a domain separation tag * @param [in] info_len - the length of param info in bytes * @param [out] y - an OPRF output * @return The function returns 0 if everything is correct. */ int oprf_Finalize(const uint8_t *x, const uint16_t x_len, const uint8_t N[crypto_core_ristretto255_BYTES], uint8_t rwdU[OPRF_BYTES]); /** * This function converts input x into an element of the OPRF group, randomizes it * by some scalar r, producing blinded, and outputs (r, blinded). * * This is the Blind OPRF function defined in the RFC. * * @param [in] x - the value to blind (for OPAQUE, this is pwdU, the user's * password) * @param [in] x_len - the length of param x in bytes * @param [out] r - an OPRF scalar value used for randomization * @param [out] blinded - a serialized OPRF group element, a byte array of fixed length, * the blinded version of x, an input to oprf_Evaluate * @return The function returns 0 if everything is correct. */ int oprf_Blind(const uint8_t *x, const uint16_t x_len, uint8_t r[crypto_core_ristretto255_SCALARBYTES], uint8_t blinded[crypto_core_ristretto255_BYTES]); /** * This function evaluates input element blinded using private key k, yielding output * element Z. * * This is the Evaluate OPRF function defined in the RFC. If the * internal proxy_cfg variable has been set using oprf_set_evalproxy() then * the Evaluation will be a threshold computation. * * @param [in] k - a private key (for OPAQUE, this is kU, the user's OPRF private * key) - if proxy_cfg is set, than this value will be ignored! * @param [in] blinded - a serialized OPRF group element, a byte array of fixed length, * an output of oprf_Blind (for OPAQUE, this is the blinded pwdU, the user's * password) * @param [out] Z - a serialized OPRF group element, a byte array of fixed length, * an input to oprf_Unblind * @return The function returns 0 if everything is correct. */ int oprf_Evaluate(const uint8_t k[crypto_core_ristretto255_SCALARBYTES], const uint8_t blinded[crypto_core_ristretto255_BYTES], uint8_t Z[crypto_core_ristretto255_BYTES]); /** * This function removes random scalar r from Z, yielding output N. * * This is the Unblind OPRF function defined in the RFC. * * @param [in] r - an OPRF scalar value used for randomization in oprf_Blind * @param [in] Z - a serialized OPRF group element, a byte array of fixed length, * an output of oprf_Evaluate * @param [out] N - a serialized OPRF group element with random scalar r removed, * a byte array of fixed length, an input to oprf_Finalize * @return The function returns 0 if everything is correct. */ int oprf_Unblind(const uint8_t r[crypto_core_ristretto255_SCALARBYTES], const uint8_t Z[crypto_core_ristretto255_BYTES], uint8_t N[crypto_core_ristretto255_BYTES]); /** * Implements the hash to curve CFRG IRTF https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ * function needed for the OPRF implementation * * @param [in] msg: the input to hash to a ristretto255 point * @param [in] msg_len: the length of the input * @param [out] p: the resulting ristretto255 point */ int voprf_hash_to_group(const uint8_t *msg, const uint8_t msg_len, uint8_t p[crypto_core_ristretto255_BYTES]); /** * A utility function from the hash to curve CFRG IRTF draft/spec * * uses the input parameters msg/msg_len and dst/dst_len (dst stands * for domain separation tag), and produces a high entropy output in * uniform_bytes of length: len_in_bytes */ int expand_message_xmd(const uint8_t *msg, const uint8_t msg_len, const uint8_t *dst, const uint8_t dst_len, const uint8_t len_in_bytes, uint8_t *uniform_bytes); /** * Clears the internal variable that stores the configuration for * proxying to a threshold oprf. */ void oprf_clear_evalproxy(void); /** * Sets the configuration of the proxy theshold evaluator * * @param [in] eval: a callback function that has the same parameters * as oprf_Evaluate. This is provided, so * implementers can provide their own means to * contact the shareholders and communicate with * them and can decide if they want to do * toprf_thresholdmult or toprf_thresholdcombine * based evaluation. Also this can be subverted for * providing static values for testvectors. * * @param [in] keygen: a callback function that takes an index, the * blinded operand, and returns an partly evaluated * element. This is provided, so implementers can * provide their own means to contact the * shareholders and communicate with them. */ int oprf_set_evalproxy(const toprf_evalcb eval, const toprf_keygencb keygen); #ifdef __EMSCRIPTEN__ /** * if compiling to webassembly, there is no sodium_m(un)?lock and thus we suppress that with the following */ // Per // https://emscripten.org/docs/compiling/Building-Projects.html#detecting-emscripten-in-preprocessor, // "The preprocessor define __EMSCRIPTEN__ is always defined when compiling // programs with Emscripten". For why we are replacing sodium_m(un)?lock, see // common.c for more details. #define sodium_mlock(a,l) (0) #define sodium_munlock(a,l) (0) #endif //__EMSCRIPTEN__ #endif liboprf/utils.c0000644000175000017500000000161614507270063013765 0ustar joostvbjoostvb#include #include #include #include const int debug = 0; void debian_rng_scalar(uint8_t *scalar) { static uint8_t rng_i=2; memset(scalar,0,crypto_core_ristretto255_SCALARBYTES); scalar[0]=rng_i++; //static uint16_t rng_i=0; //uint16_t tmp[64 / sizeof(uint16_t)]; //for(unsigned j=0;j<(64/ sizeof(uint16_t));j++) { // tmp[j]=rng_i++; //} //crypto_core_ristretto255_scalar_reduce(scalar,(uint8_t*)tmp); } void dump(const uint8_t *p, const size_t len, const char* msg, ...) { if(!debug) return; va_list args; va_start(args, msg); vfprintf(stderr,msg, args); va_end(args); for(size_t i=0;i #include void debian_rng_scalar(uint8_t *scalar); void dump(const uint8_t *p, const size_t len, const char* msg, ...); void fail(char* msg, ...); #endif // TOPRF_UTILS_H liboprf/README.md0000644000175000017500000000045014507270063013733 0ustar joostvbjoostvb* liboprf This library implements the basic OPRF(ristretto255, SHA-512) variant from the IRTF CFRG Draft: https://github.com/cfrg/draft-irtf-cfrg-voprf/ Additionally it implements a threshold OPRF based on https://eprint.iacr.org/2017/363 by Krawczyk et al. This library depends on libsodium. liboprf/toprf.h0000644000175000017500000001301014507270063013753 0ustar joostvbjoostvb/* @copyright 2023, Stefan Marsiske toprf@ctrlc.hu This file is part of liboprf. liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the License along with liboprf. If not, see . */ #ifndef TOPRF_H #define TOPRF_H #include #include #define TOPRF_Share_BYTES crypto_core_ristretto255_SCALARBYTES+1 #define TOPRF_Part_BYTES crypto_core_ristretto255_BYTES+1 /** * This function calculates a lagrange coefficient based on the index * and the indexes of the other contributing shareholders. * * @param [in] index - the index of the shareholder whose lagrange * coefficient we're calculating * * @param [in] peers_len - the number of shares in peers * * @param [in] peers - the shares that contribute to the reconstruction * * @param [out] result - the lagrange coefficient */ void coeff(const int index, const int peers_len, const uint8_t peers[peers_len], uint8_t *result); /** * This function creates shares of secret in a (threshold, n) scheme * over the curve ristretto255 * * @param [in] secret - the scalar value to be secretly shared * * @param [in] n - the number of shares created * * @param [in] threshold - the threshold needed to reconstruct the secret * * @param [out] shares - n shares * * @return The function returns 0 if everything is correct. */ void toprf_create_shares(const uint8_t secret[crypto_core_ristretto255_SCALARBYTES], const uint8_t n, const uint8_t threshold, uint8_t shares[n][TOPRF_Share_BYTES]); /** * This function recovers the secret in the exponent using lagrange interpolation * over the curve ristretto255j * * The shareholders are not aware if they are contributing to a * threshold or non-threshold oprf evaluation, from their perspective * nothing changes in this approach. * * @param [in] responses - is an array of shares (k_i) multiplied by a * point (P) on the r255 curve * * @param [in] responses - is an array of shares (k_i) multiplied by a * * @param [in] responses_len - the number of elements in the response array * * @param [out] result - the reconstructed value of P multipled by k * * @return The function returns 0 if everything is correct. */ int toprf_thresholdmult(const size_t response_len, const uint8_t responses[response_len][TOPRF_Part_BYTES], uint8_t result[crypto_scalarmult_ristretto255_BYTES]); /** * This function is the efficient threshold version of oprf_Evaluate. * * This function needs to know in advance the indexes of all the * shares that will be combined later in the toprf_thresholdcombine() function. * by doing so this reduces the total costs and distributes them to the shareholders. * * @param [in] k - a private key (for OPAQUE, this is kU, the user's * OPRF private key) * * @param [in] blinded - a serialized OPRF group element, a byte array * of fixed length, an output of oprf_Blind (for OPAQUE, this * is the blinded pwdU, the user's password) * * @param [in] self - the index of the current shareholder * * @param [in] indexes - the indexes of the all the shareholders * contributing to this oprf evaluation, * * @param [in] index_len - the length of the indexes array, * * @param [out] Z - a serialized OPRF group element, a byte array of fixed length, * an input to oprf_Unblind * * @return The function returns 0 if everything is correct. */ int toprf_Evaluate(const uint8_t k[TOPRF_Share_BYTES], const uint8_t blinded[crypto_core_ristretto255_BYTES], const uint8_t self, const uint8_t *indexes, const uint16_t index_len, uint8_t Z[TOPRF_Part_BYTES]); /** * This function is combines the results of the toprf_Evaluate() * function to recover the shared secret in the exponent. * * @param [in] responses - is an array of shares (k_i) multiplied by a point (P) on the r255 curve * * @param [in] responses_len - the number of elements in the response array * * @param [out] result - the reconstructed value of P multipled by k * * @return The function returns 0 if everything is correct. */ void toprf_thresholdcombine(const size_t response_len, const uint8_t _responses[response_len][TOPRF_Part_BYTES], uint8_t result[crypto_scalarmult_ristretto255_BYTES]); /** * This struct type is used as a parameter to toprf_evalproxy() * * it provides a threshold configuration and a callback through which * a caller can provide a callback that handles communication with the * shareholders. * * @param [] * */ typedef int (*toprf_evalcb)(const uint8_t k[crypto_core_ristretto255_SCALARBYTES], const uint8_t alpha[crypto_core_ristretto255_BYTES], uint8_t beta[crypto_core_ristretto255_BYTES]); typedef int (*toprf_keygencb)(uint8_t k[crypto_core_ristretto255_SCALARBYTES]); typedef struct { toprf_evalcb eval; toprf_keygencb keygen; } toprf_cfg; #endif // TOPRF_H