Skip to content
Snippets Groups Projects
Commit 25a741f4 authored by Charles Wright's avatar Charles Wright
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
Makefile 0 → 100644
default: bsspeke
monocypher.o: monocypher.c monocypher.h
cc -c monocypher.c
bsspeke.o: bsspeke.c bsspeke.h monocypher.h
cc -c bsspeke.c
bsspeke: bsspeke.o monocypher.o
cc -o bsspeke bsspeke.o monocypher.o
# BS-SPEKE
This repo contains an early prototype implementation of the BS-SPEKE protocol
for augmented password authenticated key exchange (aPAKE).
BS-SPEKE is described by Steve Thomas in his blog post [SRP is Now Deprecated](https://tobtu.com/blog/2021/10/srp-is-now-deprecated/)
and in a [Github Gist](https://gist.github.com/Sc00bz/e99e48a6008eef10a59d5ec7b4d87af3).
The implementation contained here is built with [Monocypher](https://monocypher.org/)
and uses D.J. Bernstein's [Curve25519](https://cr.yp.to/ecdh.html).
For now, please use this code for educational purposes only.
### Disclaimer
This software is provided AS-IS, WITHOUT WARRANTY OF ANY KIND.
BS-SPEKE
BS-SPEKE is a modified B-SPEKE with blind salt (OPRF). Modified B-SPEKE is a
similar change from SPEKE as from SPAKE2 to SPAKE2+ to make it augmented. Doing
this saves a scalar point multiply vs original B-SPEKE with blind salt. BS-SPEKE
is the best augmented PAKE that I know of. Only problem is there are no proofs,
but it's not hard to take the SPEKE proof, add the OPAQUE proof for OPRF, and
it's obvious that the augmented change makes it augmented. So if anyone knows
how to formally state that in a proof, that would be awesome to have. BS-SPEKE
defined on multiplicative groups can be found here:
https://gist.github.com/Sc00bz/ec1f5fcfd18533bf0d6bf31d1211d4b4
Costs per step
C: H*i ffI*iH***[iii]
S: f**[ii] f**[ii]
*: Scalar point multiply
H: Hash to point which is Elligator or SWU (depending on implementation this may
require a field invert if scalar point multiply needs affine points)
i: Field invert
I: Scalar invert
f: From bytes (field square root or free for Montgomery curves)
[ii...]: Field inverts that can be combined. Cost is 1 field invert and 3*(n-1)
field multiplications.
Properties
Forward secrecy: Yes
Not fragile: Yes
Quantum annoying: Yes
No precomputation: Yes
Both have:
idS = server identity
hashToPoint() is Elligator or SWU
Client has:
idC = client identity
Server has these for "idC":
salt
settings
P = hashToPoint(p)
V = v * P
C: r = random()
C: R = r * hashToPoint(H(password, idC, idS))
C->S: idC, R
S: b = random()
S: B = b * P
S: R' = H(salt) * R
C<-S: B, R', settings
C: BlindSalt = (1/r) * R'
C: p || v = pwKdf(password, BlindSalt, idC, idS, settings)
C: P = hashToPoint(p)
C: a = random()
C: A = a * P
C: K_c = H(idC, idS, A, B, a * B, v * B)
C: verifierC = H(K_c, verifyCModifier)
C->S: A, verifierC[, encryptedDataC]
S: K_s = H(idC, idS, A, B, b * A, b * V)
S: Checks verifierC == H(K_s, verifyCModifier)
S: verifierS = H(K_s, verifySModifier)
C<-S: verifierS[, encryptedDataS]
C: Checks verifierS == H(K_c, verifySModifier)
On success K_c == K_s, thus derived verifiers and encryption keys are the same.
When receiving a point, you must check it is valid and not a low order point.
When using random() to generate a scalar, you should generate a larger value and
modulo by one less than the order then add 1 or generate a value [1, order) with
rejection sampling. This makes sure it is uniformly distributed and not zero.
Similar should be done for H() when generating fields to avoid bad values. For
generating a scalar for X25519 you could just use X25519's clamping. Note that a
clamped scalar doesn't have full coverage and there is a theoretical info
leakage. If you observe 2^100 successful exchanges, then with 2^255 memory and
2^223 work you can eliminate less than 1 in 200 million password guesses
offline. Thus every 200 million online guesses you get one free (ie never will
happen, impossible, and you get a worthless attack). To "fix" this you could
generate a random number like this:
order = 2^252 + 27742317777372353535851937790883648493
r = 8 * random([1, (order - 1) / 2])
or
r = random([8, 8 * ((order - 1) / 2) + 7]) & ~7
For X25519, you'll want to have r be a multiple of 8 and swap r and 1/r. This is
so you multiply the point out of your control by a multiple of 8. So you can
check for zero. If the only functions available always clamp before scalar
multiplication then generate r and 1/r ("rInv") like this:
order = 2^252 + 27742317777372353535851937790883648493
r = clamp(random())
rInvPos = (r ^ (order - 2)) % (8 * order)
rInvNeg = 8 * order - rInvPos
rInv = ((rInvPos >> 254) & 1) ? rInvPos : rInvNeg // do bit select
if ((rInv >> 254) & 1) == 0 // "rInv != clamp(rInv)"
// This happens <1 in 2^100
// When both rInvPos and rInvNeg's 254th bit are 0
restart
Note "H(salt) * R" vs "salt * R", this is so you don't need to store a large
salt. A salt of just 128 to 256 bits is fine once expanded to the required
length.
bsspeke.c 0 → 100644
/*
* bsspeke.c - BS-SPEKE over Curve25519
*
* Author: Charles V. Wright <cvwright@futo.org>
*
* Copyright (c) 2022 FUTO Holdings, Inc.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "monocypher.h"
#include "bsspeke.h"
void
bsspeke_client_init(bsspeke_client_ctx *ctx,
const char* client_id, const size_t client_id_len,
const char* server_id, const size_t server_id_len,
const char* password, const size_t password_len
) {
ctx->client_id = (uint8_t *)client_id;
ctx->client_id_len = client_id_len;
ctx->server_id = (uint8_t *)server_id;
ctx->server_id_len = server_id_len;
ctx->password = (uint8_t *)password;
ctx->password_len = password_len;
}
void
bsspeke_client_generate_message1(bsspeke_msg1_t *msg1,
bsspeke_client_ctx *client
) {
// 1. Hash the client's password, client_id, server_id to a point on the curve
uint8_t scalar_hash[32];
uint8_t curve_point[32];
{
crypto_blake2b_ctx bctx;
// Give us a 256 bit (32 byte) hash; Don't use a key
crypto_blake2b_general_init(&bctx, 32, NULL, 0);
// Add the client id, server id, and the password to the hash
crypto_blake2b_update(&bctx,
(const uint8_t *)(client->password),
client->password_len);
crypto_blake2b_update(&bctx,
(const uint8_t *)(client->client_id),
client->client_id_len);
crypto_blake2b_update(&bctx,
(const uint8_t *)(client->server_id),
client->server_id_len);
// Write the digest value into `scalar_hash`
crypto_blake2b_final(&bctx, scalar_hash);
}
// Now use Elligator to map our scalar hash to a point on the curve
crypto_hidden_to_curve(curve_point, scalar_hash);
// 2. Generate random r
// * Actually generate 1/r first, and clamp() it
// That way we know it will always lead us back to a point on the curve
// * Then use the inverse of 1/r as `r`
// FIXME: On second thought, monocypher seems to handle all of this complexity for us. Let's see what happens if we just do things the straightforward way for now...
arc4random_buf(client->r, 32);
crypto_x25519_clamp(client->r);
// 3. Multiply our curve point by r
crypto_x25519_scalarmult(msg1->blind, client->r, curve_point, 256);
return;
}
void
bsspeke_server_init(bsspeke_server_ctx *ctx,
const char* server_id
) {
ctx->server_id = server_id;
}
void
bsspeke_server_generate_message2(bsspeke_msg2_t *msg2,
const bsspeke_msg1_t *msg1,
const uint8_t *salt, const size_t salt_len,
uint8_t P[32], uint8_t V[32],
uint32_t phf_blocks,
uint32_t phf_iterations,
bsspeke_server_ctx *server_ctx
) {
// Hash the salt
uint8_t H_salt[32];
crypto_blake2b_general(H_salt, 32, NULL, 0, salt, salt_len);
// Use clamp() to ensure we stay on the curve in the multiply below
crypto_x25519_clamp(H_salt);
// Multiply H(salt) by msg1->blind, save into msg2->blind_salt
crypto_x25519_scalarmult(msg2->blind_salt, H_salt, msg1->blind, 256);
// Generate random ephemeral private key b, save it in ctx->b
arc4random_buf(server_ctx->b, 32);
crypto_x25519_clamp(server_ctx->b);
// Compute public key B = b * P, save it in msg2->B
crypto_x25519_scalarmult(server_ctx->B, server_ctx->b, P, 256);
// Copy the public key into the outgoing message as well
memcpy(msg2->B, server_ctx->B, 32);
// Copy the PHF params too
msg2->phf_blocks = phf_blocks;
msg2->phf_iterations = phf_iterations;
return;
}
int
bsspeke_client_generate_message3(bsspeke_msg3_t *msg3,
const bsspeke_msg2_t *msg2,
bsspeke_client_ctx *client_ctx
) {
// Sanity checks first, before we do any work
if( msg2->phf_blocks < BSSPEKE_MIN_PHF_BLOCKS ) {
return -1;
}
if( msg2->phf_iterations < BSSPEKE_MIN_PHF_ITERATIONS ) {
return -1;
}
uint8_t oblivious_salt[32];
// Multiply the blinded salt by 1/r to get the oblivious salt
// Here we rely on Monocypher to do the heavy lifting for us
crypto_x25519_inverse(oblivious_salt, client_ctx->r, msg2->blind_salt);
// Hash the oblivious salt together with the id's to create the salt for the PHF
uint8_t password_hash[64];
uint8_t phf_salt[32];
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx, oblivious_salt, 32);
crypto_blake2b_update(&hash_ctx,
client_ctx->client_id,
client_ctx->client_id_len);
crypto_blake2b_update(&hash_ctx,
client_ctx->server_id,
client_ctx->server_id_len);
crypto_blake2b_final(&hash_ctx, phf_salt);
}
void *work_area;
if ((work_area = malloc(msg2->phf_blocks * 1024)) == NULL) {
return -1;
}
crypto_argon2i(password_hash, 64, work_area,
msg2->phf_blocks, msg2->phf_iterations,
client_ctx->password, client_ctx->password_len,
phf_salt, 32);
free(work_area);
// p || v = pwKdf(password, BlindSalt, idC, idS, settings)
uint8_t *p = &(password_hash[0]);
uint8_t *v = &(password_hash[32]);
// Hash p onto the curve to get this user's base point P
uint8_t P[32];
crypto_hidden_to_curve(P, p);
// Generate a random ephemeral private key a, store it in ctx->a
arc4random_buf(client_ctx->a, 32);
crypto_x25519_clamp(client_ctx->a);
// Generate the ephemeral public key A = a * P, store it in msg3->A
crypto_x25519_scalarmult(msg3->A, client_ctx->a, P, 256);
// Compute the two Diffie-Hellman shared secrets
// DH shared secret from a * B
uint8_t a_B[32];
crypto_x25519(a_B, client_ctx->a, msg2->B);
// DH shared secret from v * B
uint8_t v_B[32];
crypto_x25519(v_B, v, msg2->B);
// Hash everything we know so far to generate our key, save it in ctx->K_c
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx,
client_ctx->client_id,
client_ctx->client_id_len);
crypto_blake2b_update(&hash_ctx,
client_ctx->server_id,
client_ctx->server_id_len);
crypto_blake2b_update(&hash_ctx, msg3->A, 32);
crypto_blake2b_update(&hash_ctx, msg2->B, 32);
crypto_blake2b_update(&hash_ctx, a_B, 32);
crypto_blake2b_update(&hash_ctx, v_B, 32);
crypto_blake2b_final(&hash_ctx, client_ctx->K_c);
}
// Hash k and the client modifier to get our verifier, save it in msg3->client_verifier
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx, client_ctx->K_c, 32);
crypto_blake2b_update(&hash_ctx,
BSSPEKE_VERIFY_CLIENT_MODIFIER,
BSSPEKE_VERIFY_CLIENT_MODIFIER_LEN);
crypto_blake2b_final(&hash_ctx, msg3->client_verifier);
}
return 0;
}
int
bsspeke_server_generate_message4(bsspeke_msg4_t *msg4,
const bsspeke_msg3_t *msg3,
bsspeke_server_ctx *server_ctx
) {
// Compute the two Diffie-Hellman shared secrets
// DH shared secret from b * A
uint8_t b_A[32];
crypto_x25519(b_A, server_ctx->b, msg3->A);
// DH shared secret from b * V
uint8_t b_V[32];
crypto_x25519(b_V, server_ctx->b, server_ctx->V);
// Hash everything we've learned so far to generate k, save it in ctx->k
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx,
server_ctx->client_id,
server_ctx->client_id_len);
crypto_blake2b_update(&hash_ctx,
server_ctx->server_id,
server_ctx->server_id_len);
crypto_blake2b_update(&hash_ctx, msg3->A, 32);
crypto_blake2b_update(&hash_ctx, server_ctx->B, 32);
crypto_blake2b_update(&hash_ctx, b_A, 32);
crypto_blake2b_update(&hash_ctx, b_V, 32);
crypto_blake2b_final(&hash_ctx, server_ctx->K_s);
}
// Check that the client's hash is correct
// Compute H( k || VERIFY_CLIENT_MODIFIER )
uint8_t my_client_verifier[32];
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx, server_ctx->K_s, 32);
crypto_blake2b_update(&hash_ctx,
BSSPEKE_VERIFY_CLIENT_MODIFIER,
BSSPEKE_VERIFY_CLIENT_MODIFIER_LEN);
crypto_blake2b_final(&hash_ctx, my_client_verifier);
}
// Compare vs msg3->client_verifier
if( crypto_verify32(msg3->client_verifier, my_client_verifier) != 0 ) {
return -1;
}
// Compute our own verifier H( k || VERIFY_SERVER_MODIFIER ), save it in msg4->server_verifier
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx, server_ctx->K_s, 32);
crypto_blake2b_update(&hash_ctx,
BSSPEKE_VERIFY_SERVER_MODIFIER,
BSSPEKE_VERIFY_SERVER_MODIFIER_LEN);
crypto_blake2b_final(&hash_ctx, msg4->server_verifier);
}
// If we made it this far, return success
return 0;
}
int
bsspeke_client_verify_message4(const bsspeke_msg4_t *msg4,
const bsspeke_client_ctx *client_ctx
) {
// Compute our own version of the server's verifier hash
uint8_t my_server_verifier[32];
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx, client_ctx->K_c, 32);
crypto_blake2b_update(&hash_ctx,
BSSPEKE_VERIFY_SERVER_MODIFIER,
BSSPEKE_VERIFY_SERVER_MODIFIER_LEN);
crypto_blake2b_final(&hash_ctx, my_server_verifier);
}
// If the hashes don't match, return failure
if( crypto_verify32(msg4->server_verifier, my_server_verifier) != 0 ) {
return -1;
}
// Otherwise, return success
return 0;
}
int main(int argc, char *argv[])
{
// Before execution of the protocol
// Both have:
// idS = server identity
char *server_id;
// Client has:
// idC = client identity
char *client_id;
char *password;
// Server has these for "idC":
// salt
// settings
// P = hashToPoint(p)
// V = v * P
uint8_t salt[32];
uint8_t P[32]; // curve point
uint8_t V[32]; // curve point
// Step 1: Client hashes password, maps to a point on the curve, blinds with a random value
// C: r = random()
// C: R = r * hashToPoint(H(password, idC, idS))
// C->S: idC, R
uint8_t r[32]; // scalar
uint8_t R[32]; // curve point
// Step 2: Server generates response with blind salt
// S: b = random()
// S: B = b * P
// S: R' = H(salt) * R
// C<-S: B, R', settings
uint8_t b[32]; // scalar
uint8_t B[32]; // curve point
uint8_t R_prime[32]; // curve point
// Step 3:
// C: BlindSalt = (1/r) * R'
// C: p || v = pwKdf(password, BlindSalt, idC, idS, settings)
// C: P = hashToPoint(p)
// C: a = random()
// C: A = a * P
// C: K_c = H(idC, idS, A, B, a * B, v * B)
// C: verifierC = H(K_c, verifyCModifier)
// C->S: A, verifierC[, encryptedDataC]
uint8_t blind_salt[32]; // curve point
uint8_t password_hash[64]; // hash
uint8_t client_P[32]; // curve point
uint8_t a[32]; // scalar
uint8_t A[32]; // curve point
uint8_t K_c[32]; // hash
uint8_t verifierC[32]; // hash
// Step 4:
// S: K_s = H(idC, idS, A, B, b * A, b * V)
// S: Checks verifierC == H(K_s, verifyCModifier)
// S: verifierS = H(K_s, verifySModifier)
// C<-S: verifierS[, encryptedDataS]
uint8_t K_s[32]; // hash
uint8_t server_hash[32]; // hash -- should match verifierC
// Step 5:
// C: Checks verifierS == H(K_c, verifySModifier)
uint8_t client_hash[32]; // hash -- should match verifierS
return 0;
}
#ifndef BSSPEKE_H
#define BSSPEKE_H
#define BSSPEKE_VERIFY_CLIENT_MODIFIER "client"
#define BSSPEKE_VERIFY_CLIENT_MODIFIER_LEN 6
#define BSSPEKE_VERIFY_SERVER_MODIFIER "server"
#define BSSPEKE_VERIFY_SERVER_MODIFIER_LEN 6
#define BSSPEKE_MIN_PHF_BLOCKS 100000
#define BSSPEKE_MIN_PHF_ITERATIONS 3
typedef struct {
// Login credentials
uint8_t *client_id;
size_t client_id_len;
uint8_t *password;
size_t password_len;
// Server identifier
uint8_t *server_id;
size_t server_id_len;
// Random number to blind the password in the OPRF
uint8_t r[32];
// Server's ephemeral public key
uint8_t B[32];
// Ephemeral keypair
uint8_t a[32];
//uint8_t A[32];
// Session key
uint8_t K_c[32];
} bsspeke_client_ctx;
typedef struct {
uint8_t *server_id;
size_t server_id_len;
uint8_t *client_id;
size_t client_id_len;
// Stored ECDH parameters for the given user
//uint8_t P[32]; // Base point
uint8_t V[32]; // User's long-term public key
// Ephemeral keypair
uint8_t b[32];
uint8_t B[32];
// User's ephemeral public key
uint8_t A[32];
// Session key
uint8_t K_s[32];
} bsspeke_server_ctx;
typedef struct {
char *client_id;
uint8_t *blind;
} bsspeke_msg1_t;
typedef struct {
uint8_t blind_salt[32];
uint8_t B[32];
uint32_t phf_blocks;
uint32_t phf_iterations;
} bsspeke_msg2_t;
typedef struct {
uint8_t A[32];
uint8_t client_verifier[32];
} bsspeke_msg3_t;
typedef struct {
uint8_t server_verifier[32];
} bsspeke_msg4_t;
#endif
This diff is collapsed.
// Monocypher version 3.1.2
//
// This file is dual-licensed. Choose whichever licence you want from
// the two licences listed below.
//
// The first licence is a regular 2-clause BSD licence. The second licence
// is the CC-0 from Creative Commons. It is intended to release Monocypher
// to the public domain. The BSD licence serves as a fallback option.
//
// SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0
//
// ------------------------------------------------------------------------
//
// Copyright (c) 2017-2019, Loup Vaillant
// All rights reserved.
//
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// ------------------------------------------------------------------------
//
// Written in 2017-2019 by Loup Vaillant
//
// To the extent possible under law, the author(s) have dedicated all copyright
// and related neighboring rights to this software to the public domain
// worldwide. This software is distributed without any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication along
// with this software. If not, see
// <https://creativecommons.org/publicdomain/zero/1.0/>
#ifndef MONOCYPHER_H
#define MONOCYPHER_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
////////////////////////
/// Type definitions ///
////////////////////////
// Vtable for EdDSA with a custom hash.
// Instantiate it to define a custom hash.
// Its size, contents, and layout, are part of the public API.
typedef struct {
void (*hash)(uint8_t hash[64], const uint8_t *message, size_t message_size);
void (*init )(void *ctx);
void (*update)(void *ctx, const uint8_t *message, size_t message_size);
void (*final )(void *ctx, uint8_t hash[64]);
size_t ctx_size;
} crypto_sign_vtable;
// Do not rely on the size or contents of any of the types below,
// they may change without notice.
// Poly1305
typedef struct {
uint32_t r[4]; // constant multiplier (from the secret key)
uint32_t h[5]; // accumulated hash
uint32_t c[5]; // chunk of the message
uint32_t pad[4]; // random number added at the end (from the secret key)
size_t c_idx; // How many bytes are there in the chunk.
} crypto_poly1305_ctx;
// Hash (Blake2b)
typedef struct {
uint64_t hash[8];
uint64_t input_offset[2];
uint64_t input[16];
size_t input_idx;
size_t hash_size;
} crypto_blake2b_ctx;
// Signatures (EdDSA)
typedef struct {
const crypto_sign_vtable *hash;
uint8_t buf[96];
uint8_t pk [32];
} crypto_sign_ctx_abstract;
typedef crypto_sign_ctx_abstract crypto_check_ctx_abstract;
typedef struct {
crypto_sign_ctx_abstract ctx;
crypto_blake2b_ctx hash;
} crypto_sign_ctx;
typedef crypto_sign_ctx crypto_check_ctx;
////////////////////////////
/// High level interface ///
////////////////////////////
// Constant time comparisons
// -------------------------
// Return 0 if a and b are equal, -1 otherwise
int crypto_verify16(const uint8_t a[16], const uint8_t b[16]);
int crypto_verify32(const uint8_t a[32], const uint8_t b[32]);
int crypto_verify64(const uint8_t a[64], const uint8_t b[64]);
// Erase sensitive data
// --------------------
// Please erase all copies
void crypto_wipe(void *secret, size_t size);
// Authenticated encryption
// ------------------------
void crypto_lock(uint8_t mac[16],
uint8_t *cipher_text,
const uint8_t key[32],
const uint8_t nonce[24],
const uint8_t *plain_text, size_t text_size);
int crypto_unlock(uint8_t *plain_text,
const uint8_t key[32],
const uint8_t nonce[24],
const uint8_t mac[16],
const uint8_t *cipher_text, size_t text_size);
// With additional data
void crypto_lock_aead(uint8_t mac[16],
uint8_t *cipher_text,
const uint8_t key[32],
const uint8_t nonce[24],
const uint8_t *ad , size_t ad_size,
const uint8_t *plain_text, size_t text_size);
int crypto_unlock_aead(uint8_t *plain_text,
const uint8_t key[32],
const uint8_t nonce[24],
const uint8_t mac[16],
const uint8_t *ad , size_t ad_size,
const uint8_t *cipher_text, size_t text_size);
// General purpose hash (Blake2b)
// ------------------------------
// Direct interface
void crypto_blake2b(uint8_t hash[64],
const uint8_t *message, size_t message_size);
void crypto_blake2b_general(uint8_t *hash , size_t hash_size,
const uint8_t *key , size_t key_size, // optional
const uint8_t *message, size_t message_size);
// Incremental interface
void crypto_blake2b_init (crypto_blake2b_ctx *ctx);
void crypto_blake2b_update(crypto_blake2b_ctx *ctx,
const uint8_t *message, size_t message_size);
void crypto_blake2b_final (crypto_blake2b_ctx *ctx, uint8_t *hash);
void crypto_blake2b_general_init(crypto_blake2b_ctx *ctx, size_t hash_size,
const uint8_t *key, size_t key_size);
// vtable for signatures
extern const crypto_sign_vtable crypto_blake2b_vtable;
// Password key derivation (Argon2 i)
// ----------------------------------
void crypto_argon2i(uint8_t *hash, uint32_t hash_size, // >= 4
void *work_area, uint32_t nb_blocks, // >= 8
uint32_t nb_iterations, // >= 3
const uint8_t *password, uint32_t password_size,
const uint8_t *salt, uint32_t salt_size); // >= 8
void crypto_argon2i_general(uint8_t *hash, uint32_t hash_size,// >= 4
void *work_area, uint32_t nb_blocks,// >= 8
uint32_t nb_iterations, // >= 3
const uint8_t *password, uint32_t password_size,
const uint8_t *salt, uint32_t salt_size,// >= 8
const uint8_t *key, uint32_t key_size,
const uint8_t *ad, uint32_t ad_size);
// Key exchange (x25519 + HChacha20)
// ---------------------------------
#define crypto_key_exchange_public_key crypto_x25519_public_key
void crypto_key_exchange(uint8_t shared_key [32],
const uint8_t your_secret_key [32],
const uint8_t their_public_key[32]);
// Signatures (EdDSA with curve25519 + Blake2b)
// --------------------------------------------
// Generate public key
void crypto_sign_public_key(uint8_t public_key[32],
const uint8_t secret_key[32]);
// Direct interface
void crypto_sign(uint8_t signature [64],
const uint8_t secret_key[32],
const uint8_t public_key[32], // optional, may be 0
const uint8_t *message, size_t message_size);
int crypto_check(const uint8_t signature [64],
const uint8_t public_key[32],
const uint8_t *message, size_t message_size);
////////////////////////////
/// Low level primitives ///
////////////////////////////
// For experts only. You have been warned.
// Chacha20
// --------
// Specialised hash.
// Used to hash X25519 shared secrets.
void crypto_hchacha20(uint8_t out[32],
const uint8_t key[32],
const uint8_t in [16]);
// Unauthenticated stream cipher.
// Don't forget to add authentication.
void crypto_chacha20(uint8_t *cipher_text,
const uint8_t *plain_text,
size_t text_size,
const uint8_t key[32],
const uint8_t nonce[8]);
void crypto_xchacha20(uint8_t *cipher_text,
const uint8_t *plain_text,
size_t text_size,
const uint8_t key[32],
const uint8_t nonce[24]);
void crypto_ietf_chacha20(uint8_t *cipher_text,
const uint8_t *plain_text,
size_t text_size,
const uint8_t key[32],
const uint8_t nonce[12]);
uint64_t crypto_chacha20_ctr(uint8_t *cipher_text,
const uint8_t *plain_text,
size_t text_size,
const uint8_t key[32],
const uint8_t nonce[8],
uint64_t ctr);
uint64_t crypto_xchacha20_ctr(uint8_t *cipher_text,
const uint8_t *plain_text,
size_t text_size,
const uint8_t key[32],
const uint8_t nonce[24],
uint64_t ctr);
uint32_t crypto_ietf_chacha20_ctr(uint8_t *cipher_text,
const uint8_t *plain_text,
size_t text_size,
const uint8_t key[32],
const uint8_t nonce[12],
uint32_t ctr);
// Poly 1305
// ---------
// This is a *one time* authenticator.
// Disclosing the mac reveals the key.
// See crypto_lock() on how to use it properly.
// Direct interface
void crypto_poly1305(uint8_t mac[16],
const uint8_t *message, size_t message_size,
const uint8_t key[32]);
// Incremental interface
void crypto_poly1305_init (crypto_poly1305_ctx *ctx, const uint8_t key[32]);
void crypto_poly1305_update(crypto_poly1305_ctx *ctx,
const uint8_t *message, size_t message_size);
void crypto_poly1305_final (crypto_poly1305_ctx *ctx, uint8_t mac[16]);
// X-25519
// -------
// Shared secrets are not quite random.
// Hash them to derive an actual shared key.
void crypto_x25519_public_key(uint8_t public_key[32],
const uint8_t secret_key[32]);
void crypto_x25519(uint8_t raw_shared_secret[32],
const uint8_t your_secret_key [32],
const uint8_t their_public_key [32]);
// "Dirty" versions of x25519_public_key()
// Only use to generate ephemeral keys you want to hide.
// Note that those functions leaks 3 bits of the private key.
void crypto_x25519_dirty_small(uint8_t pk[32], const uint8_t sk[32]);
void crypto_x25519_dirty_fast (uint8_t pk[32], const uint8_t sk[32]);
// scalar "division"
// Used for OPRF. Be aware that exponential blinding is less secure
// than Diffie-Hellman key exchange.
void crypto_x25519_inverse(uint8_t blind_salt [32],
const uint8_t private_key[32],
const uint8_t curve_point[32]);
// cvw: Exposing low-level primitives for BS-SPEKE
void crypto_x25519_clamp(uint8_t scalar[32]);
void crypto_x25519_scalarmult(uint8_t q[32], const uint8_t scalar[32],
const uint8_t p[32], int nb_bits);
// EdDSA to X25519
// ---------------
void crypto_from_eddsa_private(uint8_t x25519[32], const uint8_t eddsa[32]);
void crypto_from_eddsa_public (uint8_t x25519[32], const uint8_t eddsa[32]);
// EdDSA -- Incremental interface
// ------------------------------
// Signing (2 passes)
// Make sure the two passes hash the same message,
// else you might reveal the private key.
void crypto_sign_init_first_pass(crypto_sign_ctx_abstract *ctx,
const uint8_t secret_key[32],
const uint8_t public_key[32]);
void crypto_sign_update(crypto_sign_ctx_abstract *ctx,
const uint8_t *message, size_t message_size);
void crypto_sign_init_second_pass(crypto_sign_ctx_abstract *ctx);
// use crypto_sign_update() again.
void crypto_sign_final(crypto_sign_ctx_abstract *ctx, uint8_t signature[64]);
// Verification (1 pass)
// Make sure you don't use (parts of) the message
// before you're done checking it.
void crypto_check_init (crypto_check_ctx_abstract *ctx,
const uint8_t signature[64],
const uint8_t public_key[32]);
void crypto_check_update(crypto_check_ctx_abstract *ctx,
const uint8_t *message, size_t message_size);
int crypto_check_final (crypto_check_ctx_abstract *ctx);
// Custom hash interface
void crypto_sign_public_key_custom_hash(uint8_t public_key[32],
const uint8_t secret_key[32],
const crypto_sign_vtable *hash);
void crypto_sign_init_first_pass_custom_hash(crypto_sign_ctx_abstract *ctx,
const uint8_t secret_key[32],
const uint8_t public_key[32],
const crypto_sign_vtable *hash);
void crypto_check_init_custom_hash(crypto_check_ctx_abstract *ctx,
const uint8_t signature[64],
const uint8_t public_key[32],
const crypto_sign_vtable *hash);
// Elligator 2
// -----------
// Elligator mappings proper
void crypto_hidden_to_curve(uint8_t curve [32], const uint8_t hidden[32]);
int crypto_curve_to_hidden(uint8_t hidden[32], const uint8_t curve [32],
uint8_t tweak);
// Easy to use key pair generation
void crypto_hidden_key_pair(uint8_t hidden[32], uint8_t secret_key[32],
uint8_t seed[32]);
#ifdef __cplusplus
}
#endif
#endif // MONOCYPHER_H
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment