|
- /**
- * @cond internal
- * @file strobe.c
- * @copyright
- * Copyright (c) 2015-2016 Cryptography Research, Inc. \n
- * Released under the MIT License. See LICENSE.txt for license information.
- * @author Mike Hamburg
- * @brief Strobe protocol code.
- */
-
- #define __STDC_WANT_LIB_EXT1__ 1 /* for memset_s */
- #include <assert.h>
- #include <stdint.h>
- #include <string.h>
- #include <limits.h> /* for INT_MAX */
-
- #include "strobe.h"
- #if X25519_SUPPORT_SIGN || X25519_SUPPORT_VERIFY || STROBE_CONVENIENCE_ECDH
- #include "x25519.h"
- #endif
-
- /* Sets the security level at 128 bits (but this holds even
- * when the attacker has lots of data).
- */
- #define CAPACITY_BITS (2*STROBE_INTEROP_SECURITY_BITS)
-
- /* Internal rate is 2 bytes less than sponge's "rate" */
- #define PAD_BYTES 2
- #define RATE_INNER ((25*sizeof(kword_t)-CAPACITY_BITS/8))
- #define RATE (RATE_INNER-PAD_BYTES)
-
- /* Pull in a Keccak-F implementation. Use the target-specific
- * asm one if available.
- */
- #include "keccak_f.c.inc"
-
- /* These padding bytes are applied before F. They are
- * required for parseability. Their values are chosen
- * for compatibilty with cSHAKE.
- */
- #define SHAKE_XOR_RATE 0x80
- #define SHAKE_XOR_MARK 0x04
-
- #ifndef MIN
- #define MIN(x,y) (((x)<(y)) ? (x) : (y))
- #endif
-
- /* Mark current position and state, and run F.
- * Should be compatible with CSHAKE.
- */
- static void
- _run_f (strobe_s *strobe, unsigned int p) {
- strobe->state.b[p] ^= strobe->pos_begin;
- strobe->pos_begin = 0;
- strobe->state.b[p+1] ^= SHAKE_XOR_MARK;
- strobe->state.b[RATE+1] ^= SHAKE_XOR_RATE;
- keccak_f(&strobe->state);
- }
-
- /* Place a "mark" in the hash, which is distinct from the effect of writing any byte
- * into the hash. Then write the new mode into the hash.
- */
- static inline void
- _strobe_mark(strobe_s *strobe, unsigned int * pptr, uint8_t flags) {
- unsigned int p = *pptr;
-
- /* This flag (in the param flags byte) indicates that the
- * object's role (as initiator or responder) has already
- * been determined.
- */
- const uint8_t FLAG_HAVE_ROLE = 1<<2;
-
- /* Mark the state */
- strobe->state.b[p++] ^= strobe->pos_begin;
- strobe->pos_begin = p;
- if (p >= RATE) { _run_f(strobe,p); p = 0; }
-
- /* Adjust the direction based on transport */
- if (flags & FLAG_T) {
- if (!(strobe->flags & FLAG_HAVE_ROLE)) {
- /* Set who is initiator and who is responder */
- strobe->flags |= FLAG_HAVE_ROLE | (flags & FLAG_I);
- }
- strobe->state.b[p] ^= strobe->flags & FLAG_I;
- }
-
- /* Absorb the rest of the mode marker */
- strobe->state.b[p++] ^= flags;
- uint8_t flags_that_cause_runf = FLAG_C;
- if (p >= RATE || (flags & flags_that_cause_runf)) { _run_f(strobe,p); p = 0; }
- *pptr = p;
- }
-
- /* The core duplex mode */
- ssize_t strobe_duplex (
- strobe_s *strobe,
- control_word_t flags,
- uint8_t *inside,
- ssize_t len
- ) {
- /* Sanity check */
- assert(strobe->position < RATE);
-
- if (len < 0) {
- assert(0);
- /* In production mode, no assert, but at least signal an error */
- return -1;
- }
-
- #if STROBE_SANITY_CHECK_FLAGS
- /* Sanity check flags against what flags we know and are implementing. */
- control_word_t known_flags = FLAG_I|FLAG_A|FLAG_C|FLAG_T|FLAG_M;
- known_flags|= FLAG_META_I|FLAG_META_A|FLAG_META_C|FLAG_META_T|FLAG_META_M;
- known_flags|= CW_LENGTH_BYTES(0xF);
- known_flags|= 0xFF00;
- known_flags|= FLAG_MORE|FLAG_NO_DATA;
- #if STROBE_SUPPORT_FLAG_POST
- known_flags|= FLAG_POST_RATCHET|FLAG_POST_MAC;
- #endif
- if (flags &~ known_flags) {
- assert(0);
- /* In production mode, no assert, but at least signal an error */
- return -1;
- }
- #endif
-
- ssize_t len2 = len, ret = 0;
- uint8_t cumul = 0;
- uint8_t s2s = -1;
- uint8_t s2o = (flags & FLAG_C) ? -1 : 0;
- if ((flags & FLAG_I) || !(flags & FLAG_T)) s2s ^= s2o; // duplex <-> unduplex
-
- unsigned int p = strobe->position;
- assert (p < RATE);
- if (!(flags & FLAG_MORE))
- {
- /* Mark the beginning of the operation in the strobe state */
- _strobe_mark(strobe, &p, flags);
- }
-
- /* Figure out where to write input and output */
- const uint8_t *in = NULL;
- uint8_t *out = NULL;
- ssize_t avail = 0;
-
- if (!(flags & FLAG_A)) {
- inside = NULL;
- }
-
- if (flags & FLAG_I) {
- out = inside;
- } else {
- in = inside;
- }
-
- while (len > 0) {
- /* First iteration will just skip to read section ... */
- len -= avail;
-
- for (; avail; avail--) {
- assert (p < RATE);
- uint8_t s = strobe->state.b[p], i = in ? *in++ : 0, o;
-
- o = i ^ (s&s2o);
- strobe->state.b[p++] = i ^ (s & s2s);
- cumul |= o;
- if (out) *out++ = o;
-
- if (p >= RATE) {
- _run_f(strobe,p);
- p = 0;
- }
- }
-
- /* Get more data */
- if (strobe->io == NULL || !(flags & FLAG_T)) {
- /* Nothing to write; leave output as NULL */
- avail = len;
- } else if ((flags & FLAG_I) && len > 0) {
- /* Read from wire */
- avail = strobe->io->read(&strobe->io_ctx, &in, len);
- } else {
- /* Write to wire. On the last iteration, len=0. */
- avail = strobe->io->write(&strobe->io_ctx, &out, len);
- }
-
- if (avail < 0) {
- /* IO fail! */
- strobe->position = p;
- return -1;
- } else if (avail > len) {
- avail = len;
- }
- }
-
- if ((flags & (0xF | FLAG_I)) == (TYPE_MAC | FLAG_I)) {
- /* Check MAC */
- ret = cumul ? -1 : len2;
- } else {
- ret = len2;
- }
-
- strobe->position = p;
- return ret;
- }
-
- /* Outer duplex mode: this one handles control words and reading/writing lengths. */
- static ssize_t strobe_operate_0 (
- strobe_s *__restrict__ strobe,
- uint32_t flags,
- uint8_t *inside,
- ssize_t len
- ) {
- unsigned int length_bytes = STROBE_CW_GET_LENGTH_BYTES(flags);
- control_word_t cwf = GET_META_FLAGS(flags);
-
- int more = flags & FLAG_MORE;
-
- int receiving_the_length = (cwf & FLAG_I) && length_bytes > 0 && !more;
-
- if (len < 0 && !receiving_the_length) {
- assert(((void)"strobe_operate length < 0, but not receiving the length",0));
- /* In case assertions are off... */
- return -1;
- }
-
- /* Read/write the control word */
- strobe_serialized_control_t str = {
- GET_CONTROL_TAG(flags),
- receiving_the_length ? 0 : eswap_htole_sl(len)
- };
- if (!more) {
- TRY(strobe_duplex(strobe, cwf, (uint8_t *)&str, sizeof(str.control) + length_bytes));
- }
- str.len = eswap_letoh_sl(str.len);
-
- // Check received control word and length
- if ( str.control != GET_CONTROL_TAG(flags)
- || str.len > INT_MAX
- || ((ssize_t)(len + str.len) > 0 && (ssize_t)str.len != len)
- ) {
- return -1;
- }
- len = str.len;
-
- if (flags & FLAG_NO_DATA) return 0;
-
- return strobe_duplex(strobe, flags, inside, len);
- }
-
- ssize_t __attribute__((noinline)) strobe_operate (
- strobe_s *__restrict__ strobe,
- uint32_t flags,
- uint8_t *inside,
- ssize_t len
- ) {
- #if STROBE_SUPPORT_FLAG_POST
- int ret;
- TRY(( ret = strobe_operate_0(strobe, flags, inside, len) ));
- if (flags & FLAG_POST_RATCHET) {
- assert(!(flags & FLAG_MORE));
- strobe_operate_0(strobe, RATCHET, NULL, STROBE_INTEROP_RATCHET_BYTES);
- }
- if (flags & FLAG_POST_MAC) {
- assert(!(flags & FLAG_MORE));
- control_word_t cwmac = MAC | (flags & (FLAG_I | FLAG_META_I | FLAG_META_T));
- TRY( strobe_operate_0(strobe, cwmac, NULL, STROBE_INTEROP_MAC_BYTES) );
- }
- return ret;
- #else
- /* Not supporting FLAG_POST_RATCHET or FLAG_POST_MAC */
- return strobe_operate_0(strobe, flags, inside, len);
- #endif
- }
-
- static ssize_t cb_buffer_write(strobe_io_ctx_s *ctx, uint8_t **buffer, ssize_t size) {
- uint8_t *a = ctx->a, *b = ctx->b;
- ssize_t avail = b-a;
- if (size < 0 || size > avail) return -1;
- ctx->a = a+size;
- *buffer = a;
- return avail;
- }
-
- static ssize_t cb_buffer_dont_write(strobe_io_ctx_s *ctx, uint8_t **buffer, ssize_t size) {
- (void)ctx;
- *buffer = NULL;
- if (size) return -1;
- return 0;
- }
-
- const strobe_io_callbacks_s strobe_io_cb_buffer = {
- (ssize_t (*)(strobe_io_ctx_s *, const uint8_t **, ssize_t))cb_buffer_write, cb_buffer_write
- }, strobe_io_cb_const_buffer = {
- (ssize_t (*)(strobe_io_ctx_s *, const uint8_t **, ssize_t))cb_buffer_write, cb_buffer_dont_write
- };
-
- void strobe_init (
- struct strobe_s *__restrict__ strobe,
- const uint8_t *description,
- size_t desclen
- ) {
- const uint8_t proto[18] = {
- 1,RATE+PAD_BYTES,
- 1,0, /* Empty NIST perso string */
- 1,12*8, /* 12 = strlen("STROBEvX.Y.Z") */
- 'S','T','R','O','B','E',
- 'v',
- '0'+STROBE_INTEROP_V_MAJOR,'.',
- '0'+STROBE_INTEROP_V_MINOR,'.',
- '0'+STROBE_INTEROP_V_PATCH,
- /* Rest is 0s, which is already there because we memset it */
- };
- memset(strobe,0,sizeof(*strobe));
- memcpy(strobe,proto,sizeof(proto));
- keccak_f(&strobe->state);
-
- strobe_duplex(strobe,FLAG_A|FLAG_M,(uint8_t*)description,desclen);
- }
-
- #if STROBE_SUPPORT_PRNG
- #if STROBE_SINGLE_THREAD
- static strobe_t tl_prng = {{{{0}},0,0,0,NULL,{NULL,NULL
- #if STROBE_IO_CTX_HAS_FD
- ,0
- #endif
- }}};
- #else
- static _Thread_local strobe_t tl_prng = {{{{0}},0,0,0,NULL,{NULL,NULL
- #if STROBE_IO_CTX_HAS_FD
- ,0
- #endif
- }}};
- #endif
-
- #define FLAG_PRNG_INITED (1<<4)
- #define FLAG_PRNG_SEEDED (1<<5)
- int strobe_randomize(uint8_t *data, ssize_t len) {
- if (!(tl_prng->flags & FLAG_PRNG_SEEDED)) {
- return -1;
- }
- #if STROBE_SUPPORT_POST_FLAGS
- strobe_get(tl_prng,HASH|FLAG_POST_RATCHET,data,len);
- #else
- strobe_get(tl_prng,HASH,data,len);
- strobe_operate(tl_prng, RATCHET, NULL, STROBE_INTEROP_RATCHET_BYTES);
- #endif
- return 0;
- }
-
- void strobe_seed_prng(const uint8_t *data, ssize_t len) {
- if (!(tl_prng->flags & FLAG_PRNG_INITED)) {
- strobe_init(tl_prng,(const uint8_t *)"prng",4);
- }
- #if STROBE_SUPPORT_POST_FLAGS
- strobe_put(tl_prng,SYM_KEY|FLAG_POST_RATCHET,data,len);
- #else
- strobe_put(tl_prng,SYM_KEY,data,len);
- strobe_operate(tl_prng, RATCHET, NULL, STROBE_INTEROP_RATCHET_BYTES);
- #endif
- tl_prng->flags |= (FLAG_PRNG_INITED | FLAG_PRNG_SEEDED);
- }
- #endif
-
- #if X25519_SUPPORT_VERIFY
- int strobe_session_verify (
- strobe_t strobe,
- const uint8_t their_pubkey[EC_PUBLIC_BYTES]
- ) {
- uint8_t nonce[EC_PUBLIC_BYTES], chal[EC_CHALLENGE_BYTES], resp[EC_PRIVATE_BYTES];
-
- /* TODO: use SIG_SCHEME to identify the signature scheme */
- strobe_put(strobe, MAKE_IMPLICIT(PUBLIC_KEY), their_pubkey, EC_PUBLIC_BYTES);
- TRY( strobe_get(strobe, SIG_EPH, nonce, EC_PUBLIC_BYTES) );
- strobe_get(strobe, SIG_CHALLENGE, chal, EC_CHALLENGE_BYTES);
- TRY( strobe_get(strobe, SIG_RESPONSE, resp, EC_PRIVATE_BYTES) );
- return x25519_verify_p2(resp, chal, nonce, their_pubkey);
- }
-
- #if STROBE_SUPPORT_CERT_VERIFY
- int strobe_session_dont_verify (
- strobe_t strobe,
- const uint8_t their_pubkey[EC_PUBLIC_BYTES]
- ) {
- strobe_put(strobe, MAKE_IMPLICIT(PUBLIC_KEY), their_pubkey, EC_PUBLIC_BYTES);
- TRY( strobe_get(strobe, SIG_EPH, NULL, EC_PUBLIC_BYTES) );
- strobe_get(strobe, SIG_CHALLENGE, NULL, EC_CHALLENGE_BYTES);
- return strobe_get(strobe, SIG_RESPONSE, NULL, EC_PRIVATE_BYTES);
- }
- #endif
- #endif
-
- #if X25519_SUPPORT_SIGN
- int strobe_session_sign (
- strobe_t strobe,
- const uint8_t my_seckey[EC_PRIVATE_BYTES],
- const uint8_t my_pubkey[EC_PUBLIC_BYTES]
- ) {
- uint8_t nonce[EC_PUBLIC_BYTES], chal[EC_CHALLENGE_BYTES], resp[EC_UNIFORM_BYTES];
-
- uint8_t *const eph_secret = resp;
- /* The eph secret is put into resp; responding to it conveniently overwrites that. */
-
- /* FUTURE: an option not to put in public key, eg if it's already known to be
- * in the session log
- */
- strobe_put(strobe, MAKE_IMPLICIT(PUBLIC_KEY), my_pubkey, EC_PUBLIC_BYTES);
-
- /* OK, sample the randomness */
- #if X25519_DETERMINISTIC_SIGS
- {
- strobe_t too;
- memcpy(too,strobe,sizeof(too));
- strobe_put(too,SYM_KEY,my_seckey,EC_PRIVATE_BYTES);
- strobe_get(too,HASH,eph_secret,EC_UNIFORM_BYTES);
- strobe_destroy(too);
- }
- #else
- TRY( strobe_randomize(eph_secret,EC_PRIVATE_BYTES) );
- #endif
-
- /* Nonce = g^eph */
- x25519_base_uniform(nonce,resp);
- TRY( strobe_put(strobe, SIG_EPH, nonce, EC_PUBLIC_BYTES) );
-
- /* Get the challenge */
- strobe_get(strobe, SIG_CHALLENGE, chal, EC_CHALLENGE_BYTES);
-
- /* Respond */
- x25519_sign_p2 (resp, chal, eph_secret, my_seckey);
- TRY( strobe_put(strobe, SIG_RESPONSE, resp, EC_PRIVATE_BYTES) );
-
- #if EC_UNIFORM_BYTES > EC_PRIVATE_BYTES
- /* Doesn't happen for Curve25519, but clear the high bytes of the nonce
- * if they're not overwritten. */
- memset(resp,0,sizeof(resp));
- #endif
-
- return 0;
- }
- #endif
-
- #if STROBE_CONVENIENCE_ECDH
- int strobe_eph_ecdh (
- strobe_t strobe,
- int i_go_first
- ) {
- uint8_t e_pub[EC_PUBLIC_BYTES], e_sec[EC_PRIVATE_BYTES], e_oth[EC_PUBLIC_BYTES];
-
- /* SEND EPH */
- TRY( strobe_randomize(e_sec,sizeof(e_sec)) );
- x25519_base(e_pub,e_sec,1);
- if (i_go_first)
- TRY( strobe_put(strobe, KEM_EPH, e_pub, sizeof(e_pub)) );
-
- /* RECV EPH */
- TRY( strobe_get(strobe, KEM_EPH, e_oth, sizeof(e_oth)) );
-
- /* SEND EPH */
- if (!i_go_first)
- TRY( strobe_put(strobe, KEM_EPH, e_pub, sizeof(e_pub)) );
-
- /* ECDH */
- TRY( x25519(e_pub, e_sec, e_oth, 1) );
- strobe_operate(strobe, KEM_RESULT, e_pub, sizeof(e_pub));
-
- return 0;
- }
-
- #endif // STROBE_CONVENIENCE_ECDH
|