liboprf/ 0000755 0001750 0001750 00000000000 14507270171 012455 5 ustar joostvb joostvb liboprf/oprf.c 0000644 0001750 0001750 00000040026 14507270063 013571 0 ustar joostvb joostvb /*
@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/LICENSE 0000644 0001750 0001750 00000016744 14507270063 013476 0 ustar joostvb joostvb 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/ 0000755 0001750 0001750 00000000000 14507270063 013252 5 ustar joostvb joostvb liboprf/aux/crypto_kdf_hkdf_sha512.h 0000644 0001750 0001750 00000002571 14507270063 017653 0 ustar joostvb joostvb #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.c 0000644 0001750 0001750 00000006051 14507270063 016243 0 ustar joostvb joostvb #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.h 0000644 0001750 0001750 00000002312 14507270063 013371 0 ustar joostvb joostvb #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.c 0000644 0001750 0001750 00000030711 14507270063 013370 0 ustar joostvb joostvb #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