corecrypto/ccspake/src/ccspake_mac.c

273 lines
8.0 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Copyright (c) (2018,2019) Apple Inc. All rights reserved.
*
* corecrypto is licensed under Apple Inc.s Internal Use License Agreement (which
* is contained in the License.txt file distributed with corecrypto) and only to
* people who accept that license. IMPORTANT: Any license rights granted to you by
* Apple Inc. (if any) are limited to internal use within your organization only on
* devices and computers you own or control, for the sole purpose of verifying the
* security characteristics and correct functioning of the Apple Software. You may
* not, directly or indirectly, redistribute the Apple Software or any portions thereof.
*/
#include <corecrypto/ccspake.h>
#include "ccspake_priv.h"
#include "cc_priv.h"
static const uint8_t KDF_LABEL[] = "ConfirmationKeys";
/*! @function ccspake_kdf_label_size
@abstract Returns the size of the label used for key derivation
@param ctx SPAKE2+ context
@return Size of the KDF label
*/
CC_NONNULL((1))
size_t ccspake_kdf_label_size(ccspake_const_ctx_t ctx)
{
return sizeof(KDF_LABEL) - 1 + ccspake_ctx_aad_len(ctx);
}
/*! @function ccspake_build_kdf_label
@abstract Builds the KDF label and writes it to `label`
@param ctx SPAKE2+ context
@param label Target buffer
*/
CC_NONNULL((1, 2))
void ccspake_build_kdf_label(ccspake_const_ctx_t ctx, uint8_t *label)
{
size_t kdf_label_len = sizeof(KDF_LABEL) - 1;
size_t aad_len = ccspake_ctx_aad_len(ctx);
cc_memcpy(label, KDF_LABEL, kdf_label_len);
if (aad_len) {
cc_memcpy(label + kdf_label_len, ccspake_ctx_aad(ctx), aad_len);
}
}
/*! @function ccspake_ikm_write_len
@abstract Write `len` to `ikm` as a 64-byte little-endian integer
@param ikm Target buffer
@param len Number to write
@return ikm plus the number of bytes written
*/
CC_NONNULL((1))
static uint8_t *ccspake_ikm_write_len(uint8_t *ikm, uint64_t len)
{
CC_STORE64_LE(len, ikm);
return ikm + sizeof(len);
}
/*! @function ccspake_ikm_write_point_data
@abstract Write an EC point's coordinates to `ikm`.
@param ikm Target buffer
@param cp EC curve parameters
@param x x-coordinate of the point
@param y y-coordinate of the point
@return ikm plus the number of bytes written
*/
CC_NONNULL((1, 2, 3, 4))
static uint8_t *ccspake_ikm_write_point_data(uint8_t *ikm, ccec_const_cp_t cp, const cc_unit *x, const cc_unit *y)
{
size_t len = ccec_cp_prime_size(cp);
cc_size n = ccec_cp_n(cp);
*ikm++ = 0x04;
// Write coordinates.
ccn_write_uint_padded(n, x, len, ikm);
ccn_write_uint_padded(n, y, len, ikm + len);
return ikm + len * 2;
}
/*! @function ccspake_ikm_write_point
@abstract Write an EC point's coordinates to `ikm`, prefixed by the length.
@param ikm Target buffer
@param cp EC curve parameters
@param x x-coordinate of the point
@param y y-coordinate of the point
@return ikm plus the number of bytes written
*/
CC_NONNULL((1, 2, 3, 4))
static uint8_t *ccspake_ikm_write_point(uint8_t *ikm, ccec_const_cp_t cp, const cc_unit *x, const cc_unit *y)
{
size_t len = ccec_cp_prime_size(cp);
// Write length.
ikm = ccspake_ikm_write_len(ikm, (uint64_t)len * 2 + 1);
// Write coordinates.
return ccspake_ikm_write_point_data(ikm, cp, x, y);
}
/*! @function ccspake_ikm_size
@abstract Returns the size of the "input key material" for key derivation
@param ctx SPAKE2+ context
@return The size of the "input key material"
*/
CC_NONNULL((1))
static size_t ccspake_ikm_size(ccspake_const_ctx_t ctx)
{
ccec_const_cp_t cp = ccspake_ctx_cp(ctx);
// Coordinates for points X, Y, Z, V (with leading 0x04).
size_t sz = ccspake_sizeof_point(ccspake_ctx_scp(ctx)) * 4;
// w0.
sz += ccec_cp_order_size(cp);
// Five 64-bit lengths.
sz += 5 * 8;
return sz;
}
/*! @function ccspake_derive_shared_key
@abstract Derives the shared key when the protocol completes
@param ctx SPAKE2+ context
@param sk Target buffer
*/
CC_NONNULL((1, 2))
static void ccspake_derive_shared_key(ccspake_const_ctx_t ctx, uint8_t *sk)
{
ccec_const_cp_t cp = ccspake_ctx_cp(ctx);
uint8_t ikm[ccspake_ikm_size(ctx)];
uint8_t *out = ikm;
// Write len(X) || X || len(Y) || Y.
if (ccspake_ctx_is_prover(ctx)) {
out = ccspake_ikm_write_point(out, cp, ccspake_ctx_XY_x(ctx), ccspake_ctx_XY_y(ctx));
out = ccspake_ikm_write_point(out, cp, ccspake_ctx_Q_x(ctx), ccspake_ctx_Q_y(ctx));
} else {
out = ccspake_ikm_write_point(out, cp, ccspake_ctx_Q_x(ctx), ccspake_ctx_Q_y(ctx));
out = ccspake_ikm_write_point(out, cp, ccspake_ctx_XY_x(ctx), ccspake_ctx_XY_y(ctx));
}
// Write len(Z) || Z.
out = ccspake_ikm_write_point(out, cp, ccspake_ctx_Z_x(ctx), ccspake_ctx_Z_y(ctx));
// Write len(V) || V.
out = ccspake_ikm_write_point(out, cp, ccspake_ctx_V_x(ctx), ccspake_ctx_V_y(ctx));
// Write len(w0) || w0.
size_t len = ccec_cp_order_size(cp);
out = ccspake_ikm_write_len(out, (uint64_t)len);
ccn_write_uint_padded(ccec_cp_n(cp), ccspake_ctx_w0(ctx), len, out);
// Sanity check.
cc_assert((size_t)(out + len - ikm) == ccspake_ikm_size(ctx));
// Derive.
ccdigest(ccspake_ctx_mac(ctx)->di, sizeof(ikm), ikm, sk);
}
/*! @function ccspake_mac_compute_internal
@abstract Generic function to derive MAC keys and compute MACs
@param ctx SPAKE2+ context
@param key Key to derive MAC keys from (of length `h_len / 2`)
@param use_k1 Flag to tell whether to compute a MAC with K1 or K2
@param x x-coordinate of the point to confirm
@param y y-coordinate of the point to confirm
@param t_len Length of t
@param t Target buffer
*/
CC_NONNULL((1, 2, 4, 5, 7))
static int ccspake_mac_compute_internal(ccspake_const_ctx_t ctx,
const uint8_t *key,
bool use_k1,
const cc_unit *x,
const cc_unit *y,
size_t t_len,
uint8_t *t)
{
size_t h_len = ccspake_ctx_mac(ctx)->di->output_size;
uint8_t mac_keys[h_len];
int rv = ccspake_ctx_mac(ctx)->derive(ctx, h_len / 2, key, sizeof(mac_keys), mac_keys);
if (rv != 0) {
return rv;
}
ccspake_const_cp_t scp = ccspake_ctx_scp(ctx);
uint8_t info[ccspake_sizeof_point(scp)];
ccec_const_cp_t cp = ccspake_ctx_cp(ctx);
ccspake_ikm_write_point_data(info, cp, x, y);
uint8_t *mkey = mac_keys + (!use_k1 * (h_len / 2));
rv = ccspake_ctx_mac(ctx)->compute(ctx, h_len / 2, mkey, sizeof(info), info, t_len, t);
cc_clear(sizeof(mac_keys), mac_keys);
cc_clear(sizeof(info), info);
return rv;
}
int ccspake_mac_compute(ccspake_ctx_t ctx, size_t t_len, uint8_t *t)
{
CCSPAKE_EXPECT_STATES(KEX_BOTH, MAC_VERIFY);
uint8_t key[ccspake_ctx_mac(ctx)->di->output_size];
ccspake_derive_shared_key(ctx, key);
int rv = ccspake_mac_compute_internal(
ctx, key, !ccspake_ctx_is_prover(ctx), ccspake_ctx_Q_x(ctx), ccspake_ctx_Q_y(ctx), t_len, t);
cc_clear(sizeof(key), key);
if (rv != 0) {
return rv;
}
CCSPAKE_ADD_STATE(MAC_GENERATE);
return CCERR_OK;
}
int ccspake_mac_verify_and_get_session_key(ccspake_ctx_t ctx, size_t t_len, const uint8_t *t, size_t sk_len, uint8_t *sk)
{
CCSPAKE_EXPECT_STATES(KEX_BOTH, MAC_GENERATE);
size_t h_len = ccspake_ctx_mac(ctx)->di->output_size;
if (sk_len != h_len / 2) {
return CCERR_PARAMETER;
}
uint8_t key[h_len];
ccspake_derive_shared_key(ctx, key);
uint8_t tag[t_len];
int rv = ccspake_mac_compute_internal(
ctx, key, ccspake_ctx_is_prover(ctx), ccspake_ctx_XY_x(ctx), ccspake_ctx_XY_y(ctx), t_len, tag);
if (rv != 0) {
goto cleanup;
}
if (cc_cmp_safe(t_len, t, tag)) {
rv = CCERR_INTEGRITY;
goto cleanup;
}
cc_memcpy(sk, key + h_len / 2, h_len / 2);
CCSPAKE_ADD_STATE(MAC_VERIFY);
cleanup:
cc_clear(sizeof(tag), tag);
cc_clear(sizeof(key), key);
return rv;
}