/** * @file strobe.h * @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 lite protocol instances. */ #ifndef __STROBE_H__ #define __STROBE_H__ /* TODO: Implement state compaction, particularly for PRNG state */ /* TODO: Test this against the Python reference. */ #include #include #include #include #include #include "strobe_config.h" /** * A control word holds flags and other information that control a STROBE operation. * Defined below. */ typedef uint32_t control_word_t; /* Strobe object, below */ struct strobe_s; /** Initialize a STROBE object, using the description as a domain separator. */ void strobe_init ( struct strobe_s *__restrict__ strobe, const uint8_t *description, size_t desclen ); /** Underlying duplex primitive. */ ssize_t strobe_duplex ( struct strobe_s *__restrict__ strobe, control_word_t flags, uint8_t *inside, ssize_t len ); /** * More complex duplex primitive. * First reads/writes metadata based on control_flags, then data. Can pass * -len instead of len when reading. This means any length up to len. * Returns the number of data bytes read, or <0 on error. */ ssize_t strobe_operate ( struct strobe_s *__restrict__ strobe, control_word_t control_flags, uint8_t *inside, ssize_t len ); #if STROBE_SUPPORT_PRNG /** Seed the generator with len bytes of randomness. */ void strobe_seed_prng(const uint8_t *data, ssize_t len); /** * Fill *data with len bytes of randomness. * Return <0 if the generator is not seeded. */ int __attribute__((warn_unused_result)) strobe_randomize(uint8_t *data, ssize_t len); #endif /* STROBE_SUPPORT_PRNG */ /* Flags as defined in the paper */ #define FLAG_I (1<<0) /**< Inbound */ #define FLAG_A (1<<1) /**< Has application-side data (eg, not a MAC) */ #define FLAG_C (1<<2) /**< Uses encryption or rekeying. */ #define FLAG_T (1<<3) /**< Send or receive data via transport */ #define FLAG_M (1<<4) /**< Operation carries metadata. */ #define FLAG_META_I (1<<20) /**< Metadata has I */ #define FLAG_META_A (1<<21) /**< Metadata has A (always set) */ #define FLAG_META_C (1<<22) /**< Metadata has C */ #define FLAG_META_T (1<<23) /**< Metadata has T */ #define FLAG_META_M (1<<24) /**< Metadata has M (always set) */ #define GET_META_FLAGS(cw) (((cw) >> 20) & 0x3F) #define FLAG_MORE (1<<28) /**< Continue a streaming operation. */ #define FLAG_NO_DATA (1<<29) /**< Just send/recv the metadata, not the data. */ #if STROBE_SUPPORT_FLAG_POST /** * Post-op ratchet and/or MAC. * * TODO: I might want to remove these, because some details of how this is * done might be application-specific. In particular, some applications will * want to frame their MACs on the wire, and some will not. */ #define FLAG_POST_RATCHET (1<<30) /**< Ratchet state after */ #define FLAG_POST_MAC (1<<31) /**< Send/receive MAC after */ #endif /* Operation types as defined in the paper */ #define TYPE_AD FLAG_A /**< Context data, not sent to trensport */ #define TYPE_KEY (FLAG_A | FLAG_C) /**< Symmetric key, not sent to transport */ #define TYPE_CLR (FLAG_T | FLAG_A) /**< Data to be sent in the clear */ #define TYPE_ENC (FLAG_T | FLAG_A | FLAG_C) /**< Data sent encrypted */ #define TYPE_MAC (FLAG_T | FLAG_C) /**< Message authentication code */ #define TYPE_RATCHET FLAG_C /**< Erase data to prevent rollback */ #define TYPE_PRF (FLAG_I | FLAG_A | FLAG_C) /**< Return pseudorandom hash */ /** For operate, have (n)-byte little-endian length field. */ #define CW_LENGTH_BYTES(n) ((uint32_t)(n)<<16) #define STROBE_CW_GET_LENGTH_BYTES(cw) ((cw)>>16 & 0xF) #define MAKE_IMPLICIT(cw) ((cw) &~ (FLAG_T | FLAG_META_T)) #define MAKE_LENGTHLESS(cw) ((cw) &~ CW_LENGTH_BYTES(0x0F)) #define GET_CONTROL_TAG(cw) (((cw)>>8)&0xFF) #define CONTROL_WORD(w,value,flags) enum { w=value<<8|flags|FLAG_META_A|FLAG_META_M } /* Recommended control words */ /* 0x00-0x0F: symmetric cryptography */ CONTROL_WORD(SYM_SCHEME , 0x00, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(SYM_KEY , 0x01, TYPE_KEY ); CONTROL_WORD(APP_PLAINTEXT , 0x02, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(APP_CIPHERTEXT , 0x03, TYPE_ENC | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(NONCE , 0x04, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(AUTH_DATA , 0x05, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(MAC , 0x06, TYPE_MAC | CW_LENGTH_BYTES(2) ); CONTROL_WORD(HASH , 0x07, TYPE_PRF | CW_LENGTH_BYTES(2) ); CONTROL_WORD(DERIVE_KEY , 0x08, TYPE_PRF | CW_LENGTH_BYTES(2) ); CONTROL_WORD(BE_SLOW , 0x0C, TYPE_RATCHET | CW_LENGTH_BYTES(4) ); CONTROL_WORD(SIV_PT_INNER , 0x0D, TYPE_CLR ); /* FUTURE: implement SIV */ CONTROL_WORD(SIV_MAC_OUTER , 0x0E, TYPE_CLR ); CONTROL_WORD(RATCHET , 0x0F, TYPE_RATCHET ); /* 0x10-0x1F: Asymmetric key exchange and encryption */ CONTROL_WORD(KEM_SCHEME , 0x10, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(PUBLIC_KEY , 0x11, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(KEM_EPH , 0x12, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(KEM_RESULT , 0x13, TYPE_KEY ); /* 0x18-0x1F: Signatures */ CONTROL_WORD(SIG_SCHEME , 0x18, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(SIG_EPH , 0x19, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(SIG_CHALLENGE , 0x1A, TYPE_PRF | CW_LENGTH_BYTES(2) ); CONTROL_WORD(SIG_RESPONSE , 0x1B, TYPE_ENC | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(SIG_DETERM , 0x1C, TYPE_PRF | CW_LENGTH_BYTES(2) ); /* 0x20-0x2F: header and other metadata */ CONTROL_WORD(HANDSHAKE , 0x20, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(VERSION , 0x21, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CIPHERSUITE , 0x22, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(META_PLAINTEXT , 0x24, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(META_CIPHERTEXT, 0x25, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CERTIFICATE , 0x26, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(ENCRYPTED_CERT , 0x27, TYPE_ENC | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(OVER , 0x2E, TYPE_MAC | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CLOSE , 0x2F, TYPE_MAC | CW_LENGTH_BYTES(2) | FLAG_META_T ); /* 0x30-0x3F: Certificates. * * These are still experimental and unimplemented, and will probably change. * The intention is that certs should look something like this: * * CERT_VERSION 1 * CERT_SERIAL .{0,32} ? # for revocation, else omitted * CERT_VALIDITY? # omitted if your devices aren't tracking time or can't update * CERT_PURPOSE .*? # if your application makes such a distinction * ((CERT_REC_PRE .* CERT_REC_POST .*) | (CERT_NAME .*)) * ^ if the cert is an intermediate ^ if it isn't an intermediate * (CERT_PK_SCHEME PUBLIC_KEY)+ # Limited to 1? Could have multiple? Dunno. * * CERT_COMMENT * * (SIG_SCHEME SIG_CHAL SIG_EPH SIG_RESPONSE)+ # or similar depending on sig scheme * ^ Possibly this should be limited to 1. * * If your application uses cert chains, they would be given in separate CERTIFICATE * (or ENCRYPTED_CERT) messages, in order from CA to leaf. At any time, the client * could track "what's the most recent trusted intermediate which has authority over * the name I'm trying to contact." Then if there are multiple certifying chains, the * untrusted ones would be ignored. */ CONTROL_WORD(CERT_VERSION , 0x30, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CERT_SERIAL , 0x31, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CERT_VALIDITY , 0x32, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CERT_PURPOSE , 0x33, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CERT_REC_PRE , 0x34, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CERT_REC_POST , 0x35, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CERT_NAME , 0x36, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CERT_PK_SCHEME , 0x37, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); CONTROL_WORD(CERT_COMMENT , 0x3F, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T ); #if STROBE_INTEROP_F_BITS == 1600 #define kword_t uint64_t #elif STROBE_INTEROP_F_BITS == 800 #define kword_t uint32_t #elif STROBE_INTEROP_F_BITS == 400 #define kword_t uint16_t #else #error "Strobe supports only Keccak-F{400,800,1600}" #endif /* IO callback context. Opaque to STROBE. */ typedef struct { void *a, *b; /**< Two pointers for use by the callback context. */ #if STROBE_IO_CTX_HAS_FD int fd; /**< A file descriptor, or whatever. */ #endif } strobe_io_ctx_s; /** Callback context */ typedef struct { // FUTURE: make these return two values? /** * Read up to [size] bytes of data. Set the buffer to where it * points, and return how many bytes were actually read. */ ssize_t (*read) (strobe_io_ctx_s *ctx, const uint8_t **buffer, ssize_t size); /** * The write callback is trickier. * * The first call returns a buffer to write the data to. * * The next call writes that data out, and returns a new (or more likely, * the same) buffer. */ ssize_t (*write) (strobe_io_ctx_s *ctx, uint8_t **buffer, ssize_t size); } strobe_io_callbacks_s; extern const strobe_io_callbacks_s strobe_io_cb_buffer, strobe_io_cb_const_buffer; /** Keccak's domain: 25 words of size b/25, or b/8 bytes. */ typedef union { kword_t w[25]; uint8_t b[25*sizeof(kword_t)/sizeof(uint8_t)]; } kdomain_s; /** The main strobe state object. */ typedef struct strobe_s { kdomain_s state; uint8_t position, pos_begin, flags; const strobe_io_callbacks_s *io; strobe_io_ctx_s io_ctx; } strobe_s, strobe_t[1]; #if STROBE_CW_MAX_LENGTH_BYTES <= 1 typedef uint8_t strobe_length_t; #elif STROBE_CW_MAX_LENGTH_BYTES <= 2 typedef uint16_t strobe_length_t; #elif STROBE_CW_MAX_LENGTH_BYTES <= 4 typedef uint32_t strobe_length_t; #elif STROBE_CW_MAX_LENGTH_BYTES <= 8 typedef uint64_t strobe_length_t; #elif STROBE_CW_MAX_LENGTH_BYTES <= 16 typedef uint128_t strobe_length_t; #else #error "Can't deal with >128-bit length fields'" #endif typedef struct { uint8_t control; strobe_length_t len; } __attribute__((packed)) strobe_serialized_control_t; /* Protocol building blocks */ #define TRY(foo) do { ssize_t _ret = (foo); if (_ret < 0) return _ret; } while(0) /** * Destroy a STROBE object by writing zeros over it. * NB: if you don't have C11's memset_s, the compiler might optimize this call * away! */ static inline void strobe_destroy(strobe_t strobe) { #ifdef __STDC_LIB_EXT1__ memset_s(strobe,sizeof(*strobe),0,sizeof(*strobe)); #else memset(strobe,0,sizeof(*strobe)); #endif } /** * Detach the I/O from a STROBE object. */ static inline void strobe_detach(strobe_t strobe) { strobe->io = NULL; /* Not really relevant: */ // strobe->io_ctx.a = strobe->io_ctx.b = NULL; } /** Reverse the role of a STROBE object (i.e. to emulate the other guy). */ static inline void strobe_reverse(strobe_t strobe) { strobe->flags ^= FLAG_I; } /** Attach a buffer to a strobe object. */ static inline void strobe_attach_buffer(strobe_t strobe, uint8_t *start, size_t length) { strobe->io = &strobe_io_cb_buffer; strobe->io_ctx.a = start; strobe->io_ctx.b = start+length; } /** Attach a buffer to a strobe object. */ static inline void strobe_attach_const_buffer(strobe_t strobe, uint8_t *start, size_t length) { strobe->io = &strobe_io_cb_const_buffer; strobe->io_ctx.a = start; strobe->io_ctx.b = start+length; } /** Same as operate, but only for sending data or putting it into the sponge. */ static inline ssize_t strobe_put ( strobe_s *__restrict__ strobe, control_word_t control_flags, const uint8_t *inside, ssize_t len ) { assert(!(control_flags & (FLAG_M|FLAG_I|FLAG_META_I))); return strobe_operate(strobe,control_flags,(uint8_t *)inside,len); } /** Receive data or extract it from the sponge. */ static inline ssize_t strobe_get ( strobe_s *__restrict__ strobe, control_word_t control_flags, uint8_t *inside, ssize_t len ) { assert(!(control_flags & FLAG_M)); assert((control_flags & (FLAG_T|FLAG_C)) != 0); control_flags |= FLAG_I; if (control_flags & FLAG_META_T) control_flags |= FLAG_META_I; return strobe_operate(strobe,control_flags,(uint8_t *)inside,len); } /* Endian swaps */ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ static inline kword_t eswap_letoh(kword_t w) { return w; } static inline kword_t eswap_htole(kword_t w) { return w; } static inline uint16_t eswap_letoh_16(uint16_t w) { return w; } static inline uint16_t eswap_htole_16(uint16_t w) { return w; } static inline uint32_t eswap_letoh_32(uint32_t w) { return w; } static inline uint32_t eswap_htole_32(uint32_t w) { return w; } static inline uint64_t eswap_letoh_64(uint64_t w) { return w; } static inline uint64_t eswap_htole_64(uint64_t w) { return w; } static inline strobe_length_t eswap_letoh_sl(strobe_length_t w) { return w; } static inline strobe_length_t eswap_htole_sl(strobe_length_t w) { return w; } #else #error "Fix eswap() on non-little-endian machine" #endif /** * Receive just control/metadata from the other party. * This is for complex protocols where you may not know what will come next. */ static inline ssize_t strobe_get_control ( strobe_s *__restrict__ strobe, strobe_serialized_control_t *cw, uint32_t flags ) { unsigned int length_bytes = STROBE_CW_GET_LENGTH_BYTES(flags); control_word_t cwf = GET_META_FLAGS(flags) | FLAG_I | FLAG_T; cw->len = 0; ssize_t ret = strobe_duplex(strobe, cwf, (uint8_t *)cw, sizeof(cw->control) + length_bytes); /* Probably a nop, allowing tailcall, except we're inline anyway */ cw->len = eswap_letoh_sl(cw->len); return ret; } static inline ssize_t strobe_put_mac( strobe_t strobe ) { return strobe_operate(strobe, MAC, NULL, STROBE_INTEROP_MAC_BYTES); } static inline ssize_t strobe_get_mac(strobe_t strobe ) { return strobe_operate(strobe, MAC|FLAG_I, NULL, STROBE_INTEROP_MAC_BYTES); } static inline ssize_t strobe_key ( strobe_t strobe, control_word_t cw, const uint8_t *data, uint16_t len ) { assert(!(cw & FLAG_T)); return strobe_put(strobe, cw, data, len); } #if STROBE_CONVENIENCE_ECDH int strobe_eph_ecdh ( strobe_t strobe, int i_go_first ); #endif #if X25519_SUPPORT_SIGN int strobe_session_sign ( strobe_t strobe, const uint8_t my_seckey[32], const uint8_t my_pubkey[32] ); #endif #if X25519_SUPPORT_VERIFY int strobe_session_verify ( strobe_t strobe, const uint8_t their_pubkey[32] ); #if STROBE_SUPPORT_CERT_VERIFY /* Parse a signature but don't verify it. * Useful for intermediate certs we don't trust. */ int strobe_session_dont_verify ( strobe_t strobe, const uint8_t their_pubkey[32] ); #endif #endif #endif /* __STROBE_H__ */