@ -31,12 +31,18 @@
// This file implements draft-irtf-cfrg-hpke-08.
# define KEM_CONTEXT_LEN (2 * X25519_PUBLIC_VALUE_LEN)
struct evp_hpke_kdf_st {
uint16_t id ;
// We only support HKDF-based KDFs.
const EVP_MD * ( * hkdf_md_func ) ( void ) ;
} ;
// This is strlen("HPKE") + 3 * sizeof(uint16_t).
# define HPKE_SUITE_ID_LEN 10
struct evp_hpke_aead_st {
uint16_t id ;
const EVP_AEAD * ( * aead_func ) ( void ) ;
} ;
# define HPKE_MODE_BASE 0
# define KEM_CONTEXT_LEN (2 * X25519_PUBLIC_VALUE_LEN)
static const char kHpkeVersionId [ ] = " HPKE-v1 " ;
@ -51,20 +57,6 @@ static const uint8_t kX25519SuiteID[] = {
' K ' , ' E ' , ' M ' , EVP_HPKE_DHKEM_X25519_HKDF_SHA256 > > 8 ,
EVP_HPKE_DHKEM_X25519_HKDF_SHA256 & 0x00ff } ;
// The suite_id for non-KEM pieces of HPKE is defined as concat("HPKE",
// I2OSP(kem_id, 2), I2OSP(kdf_id, 2), I2OSP(aead_id, 2)).
static int hpke_build_suite_id ( uint8_t out [ HPKE_SUITE_ID_LEN ] , uint16_t kdf_id ,
uint16_t aead_id ) {
CBB cbb ;
int ret = CBB_init_fixed ( & cbb , out , HPKE_SUITE_ID_LEN ) & &
add_label_string ( & cbb , " HPKE " ) & &
CBB_add_u16 ( & cbb , EVP_HPKE_DHKEM_X25519_HKDF_SHA256 ) & &
CBB_add_u16 ( & cbb , kdf_id ) & &
CBB_add_u16 ( & cbb , aead_id ) ;
CBB_cleanup ( & cbb ) ;
return ret ;
}
static int hpke_labeled_extract ( const EVP_MD * hkdf_md , uint8_t * out_key ,
size_t * out_len , const uint8_t * salt ,
size_t salt_len , const uint8_t * suite_id ,
@ -108,81 +100,62 @@ static int hpke_extract_and_expand(const EVP_MD *hkdf_md, uint8_t *out_key,
const uint8_t kem_context [ KEM_CONTEXT_LEN ] ) {
uint8_t prk [ EVP_MAX_MD_SIZE ] ;
size_t prk_len ;
static const char kEaePrkLabel [ ] = " eae_prk " ;
if ( ! hpke_labeled_extract ( hkdf_md , prk , & prk_len , NULL , 0 , kX25519SuiteID ,
sizeof ( kX25519SuiteID ) , kEaePrkLabel , dh ,
sizeof ( kX25519SuiteID ) , " eae_prk " , dh ,
X25519_PUBLIC_VALUE_LEN ) ) {
return 0 ;
}
static const char kPRKExpandLabel [ ] = " shared_secret " ;
if ( ! hpke_labeled_expand ( hkdf_md , out_key , out_len , prk , prk_len ,
kX25519SuiteID , sizeof ( kX25519SuiteID ) ,
kPRKExpandLabel , kem_context , KEM_CONTEXT_LEN ) ) {
" shared_secret " , kem_context , KEM_CONTEXT_LEN ) ) {
return 0 ;
}
return 1 ;
}
uint16_t EVP_HPKE_CTX_get_aead_id ( const EVP_HPKE_CTX * hpke ) {
return hpke - > aead_id ;
}
uint16_t EVP_HPKE_CTX_get_kdf_id ( const EVP_HPKE_CTX * hpke ) {
return hpke - > kdf_id ;
}
// This is strlen("HPKE") + 3 * sizeof(uint16_t).
# define HPKE_SUITE_ID_LEN 10
const EVP_AEAD * EVP_HPKE_get_aead ( uint16_t aead_id ) {
switch ( aead_id ) {
case EVP_HPKE_AEAD_AES_128_GCM :
return EVP_aead_aes_128_gcm ( ) ;
case EVP_HPKE_AEAD_AES_256_GCM :
return EVP_aead_aes_256_gcm ( ) ;
case EVP_HPKE_AEAD_CHACHA20POLY1305 :
return EVP_aead_chacha20_poly1305 ( ) ;
}
OPENSSL_PUT_ERROR ( EVP , ERR_R_INTERNAL_ERROR ) ;
return NULL ;
// The suite_id for non-KEM pieces of HPKE is defined as concat("HPKE",
// I2OSP(kem_id, 2), I2OSP(kdf_id, 2), I2OSP(aead_id, 2)).
static int hpke_build_suite_id ( const EVP_HPKE_CTX * hpke ,
uint8_t out [ HPKE_SUITE_ID_LEN ] ) {
CBB cbb ;
int ret = CBB_init_fixed ( & cbb , out , HPKE_SUITE_ID_LEN ) & &
add_label_string ( & cbb , " HPKE " ) & &
CBB_add_u16 ( & cbb , EVP_HPKE_DHKEM_X25519_HKDF_SHA256 ) & &
CBB_add_u16 ( & cbb , hpke - > kdf - > id ) & &
CBB_add_u16 ( & cbb , hpke - > aead - > id ) ;
CBB_cleanup ( & cbb ) ;
return ret ;
}
const EVP_MD * EVP_HPKE_get_hkdf_md ( uint16_t kdf_id ) {
switch ( kdf_id ) {
case EVP_HPKE_HKDF_SHA256 :
return EVP_sha256 ( ) ;
}
OPENSSL_PUT_ERROR ( EVP , ERR_R_INTERNAL_ERROR ) ;
return NULL ;
}
# define HPKE_MODE_BASE 0
static int hpke_key_schedule ( EVP_HPKE_CTX * hpke , const uint8_t * shared_secret ,
size_t shared_secret_len , const uint8_t * info ,
size_t info_len ) {
// Attempt to get an EVP_AEAD*.
const EVP_AEAD * aead = EVP_HPKE_get_aead ( hpke - > aead_id ) ;
if ( aead = = NULL ) {
return 0 ;
}
uint8_t suite_id [ HPKE_SUITE_ID_LEN ] ;
if ( ! hpke_build_suite_id ( suite_id , hpke - > kdf_id , hpke - > aead _id) ) {
if ( ! hpke_build_suite_id ( hpke , suite_id ) ) {
return 0 ;
}
// psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id)
static const char kPskIdHashLabel [ ] = " psk_id_hash " ;
// TODO(davidben): Precompute this value and store it with the EVP_HPKE_KDF.
const EVP_MD * hkdf_md = hpke - > kdf - > hkdf_md_func ( ) ;
uint8_t psk_id_hash [ EVP_MAX_MD_SIZE ] ;
size_t psk_id_hash_len ;
if ( ! hpke_labeled_extract ( hpke - > h kdf_md , psk_id_hash , & psk_id_hash_len , NULL ,
0 , suite_id , sizeof ( suite_id ) , kPskIdHashLabel ,
NULL , 0 ) ) {
if ( ! hpke_labeled_extract ( hkdf_md , psk_id_hash , & psk_id_hash_len , NULL , 0 ,
suite_id , sizeof ( suite_id ) , " psk_id_hash " , NULL ,
0 ) ) {
return 0 ;
}
// info_hash = LabeledExtract("", "info_hash", info)
static const char kInfoHashLabel [ ] = " info_hash " ;
uint8_t info_hash [ EVP_MAX_MD_SIZE ] ;
size_t info_hash_len ;
if ( ! hpke_labeled_extract ( hpke - > h kdf_md , info_hash , & info_hash_len , NULL , 0 ,
suite_id , sizeof ( suite_id ) , kInfoHashLabel , info ,
if ( ! hpke_labeled_extract ( hkdf_md , info_hash , & info_hash_len , NULL , 0 ,
suite_id , sizeof ( suite_id ) , " info_hash " , info ,
info_len ) ) {
return 0 ;
}
@ -200,45 +173,37 @@ static int hpke_key_schedule(EVP_HPKE_CTX *hpke, const uint8_t *shared_secret,
}
// secret = LabeledExtract(shared_secret, "secret", psk)
static const char kSecretExtractLabel [ ] = " secret " ;
uint8_t secret [ EVP_MAX_MD_SIZE ] ;
size_t secret_len ;
if ( ! hpke_labeled_extract ( hpke - > h kdf_md , secret , & secret_len , shared_secret ,
if ( ! hpke_labeled_extract ( hkdf_md , secret , & secret_len , shared_secret ,
shared_secret_len , suite_id , sizeof ( suite_id ) ,
kSecretExtractLabel , NULL , 0 ) ) {
" secret " , NULL , 0 ) ) {
return 0 ;
}
// key = LabeledExpand(secret, "key", key_schedule_context, Nk)
static const char kKeyExpandLabel [ ] = " key " ;
const EVP_AEAD * aead = hpke - > aead - > aead_func ( ) ;
uint8_t key [ EVP_AEAD_MAX_KEY_LENGTH ] ;
const size_t kKeyLen = EVP_AEAD_key_length ( aead ) ;
if ( ! hpke_labeled_expand ( hpke - > hkdf_md , key , kKeyLen , secret , secret_len ,
suite_id , sizeof ( suite_id ) , kKeyExpandLabel , context ,
context_len ) ) {
return 0 ;
}
// Initialize the HPKE context's AEAD context, storing a copy of |key|.
if ( ! EVP_AEAD_CTX_init ( & hpke - > aead_ctx , aead , key , kKeyLen , 0 , NULL ) ) {
if ( ! hpke_labeled_expand ( hkdf_md , key , kKeyLen , secret , secret_len , suite_id ,
sizeof ( suite_id ) , " key " , context , context_len ) | |
! EVP_AEAD_CTX_init ( & hpke - > aead_ctx , aead , key , kKeyLen ,
EVP_AEAD_DEFAULT_TAG_LENGTH , NULL ) ) {
return 0 ;
}
// base_nonce = LabeledExpand(secret, "base_nonce", key_schedule_context, Nn)
static const char kNonceExpandLabel [ ] = " base_nonce " ;
if ( ! hpke_labeled_expand ( hpke - > hkdf_md , hpke - > base_nonce ,
if ( ! hpke_labeled_expand ( hkdf_md , hpke - > base_nonce ,
EVP_AEAD_nonce_length ( aead ) , secret , secret_len ,
suite_id , sizeof ( suite_id ) , kNonceExpandLabel ,
context , context _len) ) {
suite_id , sizeof ( suite_id ) , " base_nonce " , context ,
context_len ) ) {
return 0 ;
}
// exporter_secret = LabeledExpand(secret, "exp", key_schedule_context, Nh)
static const char kExporterSecretExpandLabel [ ] = " exp " ;
if ( ! hpke_labeled_expand ( hpke - > hkdf_md , hpke - > exporter_secret ,
EVP_MD_size ( hpke - > hkdf_md ) , secret , secret_len ,
suite_id , sizeof ( suite_id ) ,
kExporterSecretExpandLabel , context , context_len ) ) {
if ( ! hpke_labeled_expand ( hkdf_md , hpke - > exporter_secret , EVP_MD_size ( hkdf_md ) ,
secret , secret_len , suite_id , sizeof ( suite_id ) ,
" exp " , context , context_len ) ) {
return 0 ;
}
@ -290,6 +255,33 @@ static int hpke_decap(const EVP_HPKE_CTX *hpke,
return 1 ;
}
const EVP_HPKE_KDF * EVP_hpke_hkdf_sha256 ( void ) {
static const EVP_HPKE_KDF kKDF = { EVP_HPKE_HKDF_SHA256 , & EVP_sha256 } ;
return & kKDF ;
}
uint16_t EVP_HPKE_KDF_id ( const EVP_HPKE_KDF * kdf ) { return kdf - > id ; }
const EVP_HPKE_AEAD * EVP_hpke_aes_128_gcm ( void ) {
static const EVP_HPKE_AEAD kAEAD = { EVP_HPKE_AES_128_GCM ,
& EVP_aead_aes_128_gcm } ;
return & kAEAD ;
}
const EVP_HPKE_AEAD * EVP_hpke_aes_256_gcm ( void ) {
static const EVP_HPKE_AEAD kAEAD = { EVP_HPKE_AES_256_GCM ,
& EVP_aead_aes_256_gcm } ;
return & kAEAD ;
}
const EVP_HPKE_AEAD * EVP_hpke_chacha20_poly1305 ( void ) {
static const EVP_HPKE_AEAD kAEAD = { EVP_HPKE_CHACHA20_POLY1305 ,
& EVP_aead_chacha20_poly1305 } ;
return & kAEAD ;
}
uint16_t EVP_HPKE_AEAD_id ( const EVP_HPKE_AEAD * aead ) { return aead - > id ; }
void EVP_HPKE_CTX_init ( EVP_HPKE_CTX * ctx ) {
OPENSSL_memset ( ctx , 0 , sizeof ( EVP_HPKE_CTX ) ) ;
EVP_AEAD_CTX_zero ( & ctx - > aead_ctx ) ;
@ -300,23 +292,25 @@ void EVP_HPKE_CTX_cleanup(EVP_HPKE_CTX *ctx) {
}
int EVP_HPKE_CTX_setup_base_s_x25519 ( EVP_HPKE_CTX * hpke , uint8_t * out_enc ,
size_t out_enc_len , uint16_t kdf_id ,
uint16_t aead_id ,
size_t out_enc_len ,
const EVP_HPKE_KDF * kdf ,
const EVP_HPKE_AEAD * aead ,
const uint8_t * peer_public_value ,
size_t peer_public_value_len ,
const uint8_t * info , size_t info_len ) {
uint8_t seed [ X25519_PRIVATE_KEY_LEN ] ;
RAND_bytes ( seed , sizeof ( seed ) ) ;
return EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing (
hpke , out_enc , out_enc_len , kdf_id , aead_i d , peer_public_value ,
hpke , out_enc , out_enc_len , kdf , aead , peer_public_value ,
peer_public_value_len , info , info_len , seed , sizeof ( seed ) ) ;
}
int EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing (
EVP_HPKE_CTX * hpke , uint8_t * out_enc , size_t out_enc_len , uint16_t kdf_id ,
uint16_t aead_id , const uint8_t * peer_public_value ,
size_t peer_public_value_len , const uint8_t * info , size_t info_len ,
const uint8_t * seed , size_t seed_len ) {
EVP_HPKE_CTX * hpke , uint8_t * out_enc , size_t out_enc_len ,
const EVP_HPKE_KDF * kdf , const EVP_HPKE_AEAD * aead ,
const uint8_t * peer_public_value , size_t peer_public_value_len ,
const uint8_t * info , size_t info_len , const uint8_t * seed ,
size_t seed_len ) {
if ( out_enc_len ! = X25519_PUBLIC_VALUE_LEN ) {
OPENSSL_PUT_ERROR ( EVP , EVP_R_INVALID_BUFFER_SIZE ) ;
return 0 ;
@ -331,12 +325,8 @@ int EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing(
}
hpke - > is_sender = 1 ;
hpke - > kdf_id = kdf_id ;
hpke - > aead_id = aead_id ;
hpke - > hkdf_md = EVP_HPKE_get_hkdf_md ( kdf_id ) ;
if ( hpke - > hkdf_md = = NULL ) {
return 0 ;
}
hpke - > kdf = kdf ;
hpke - > aead = aead ;
X25519_public_from_private ( out_enc , seed ) ;
uint8_t shared_secret [ SHA256_DIGEST_LENGTH ] ;
if ( ! hpke_encap ( hpke , shared_secret , peer_public_value , seed , out_enc ) | |
@ -347,13 +337,11 @@ int EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing(
return 1 ;
}
int EVP_HPKE_CTX_setup_base_r_x25519 ( EVP_HPKE_CTX * hpke , uint16_t kdf_id ,
uint16_t aead_id , const uint8_t * enc ,
size_t enc_len , const uint8_t * public_key ,
size_t public_key_len ,
const uint8_t * private_key ,
size_t private_key_len ,
const uint8_t * info , size_t info_len ) {
int EVP_HPKE_CTX_setup_base_r_x25519 (
EVP_HPKE_CTX * hpke , const EVP_HPKE_KDF * kdf , const EVP_HPKE_AEAD * aead ,
const uint8_t * enc , size_t enc_len , const uint8_t * public_key ,
size_t public_key_len , const uint8_t * private_key , size_t private_key_len ,
const uint8_t * info , size_t info_len ) {
if ( enc_len ! = X25519_PUBLIC_VALUE_LEN ) {
OPENSSL_PUT_ERROR ( EVP , EVP_R_INVALID_PEER_KEY ) ;
return 0 ;
@ -365,12 +353,8 @@ int EVP_HPKE_CTX_setup_base_r_x25519(EVP_HPKE_CTX *hpke, uint16_t kdf_id,
}
hpke - > is_sender = 0 ;
hpke - > kdf_id = kdf_id ;
hpke - > aead_id = aead_id ;
hpke - > hkdf_md = EVP_HPKE_get_hkdf_md ( kdf_id ) ;
if ( hpke - > hkdf_md = = NULL ) {
return 0 ;
}
hpke - > kdf = kdf ;
hpke - > aead = aead ;
uint8_t shared_secret [ SHA256_DIGEST_LENGTH ] ;
if ( ! hpke_decap ( hpke , shared_secret , enc , public_key , private_key ) | |
! hpke_key_schedule ( hpke , shared_secret , sizeof ( shared_secret ) , info ,
@ -398,11 +382,6 @@ static void hpke_nonce(const EVP_HPKE_CTX *hpke, uint8_t *out_nonce,
}
}
size_t EVP_HPKE_CTX_max_overhead ( const EVP_HPKE_CTX * hpke ) {
assert ( hpke - > is_sender ) ;
return EVP_AEAD_max_overhead ( hpke - > aead_ctx . aead ) ;
}
int EVP_HPKE_CTX_open ( EVP_HPKE_CTX * hpke , uint8_t * out , size_t * out_len ,
size_t max_out_len , const uint8_t * in , size_t in_len ,
const uint8_t * ad , size_t ad_len ) {
@ -455,15 +434,27 @@ int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *hpke, uint8_t *out,
size_t secret_len , const uint8_t * context ,
size_t context_len ) {
uint8_t suite_id [ HPKE_SUITE_ID_LEN ] ;
if ( ! hpke_build_suite_id ( suite_id , hpke - > kdf_id , hpke - > aead _id) ) {
if ( ! hpke_build_suite_id ( hpke , suite _id) ) {
return 0 ;
}
static const char kExportExpandLabel [ ] = " sec " ;
if ( ! hpke_labeled_expand ( hpke - > hkdf_md , out , secret_len ,
hpke - > exporter_secret , EVP_MD_size ( hpke - > hkdf_md ) ,
suite_id , sizeof ( suite_id ) , kExportExpandLabel ,
context , context_len ) ) {
const EVP_MD * hkdf_md = hpke - > kdf - > hkdf_md_func ( ) ;
if ( ! hpke_labeled_expand ( hkdf_md , out , secret_len , hpke - > exporter_secret ,
EVP_MD_size ( hkdf_md ) , suite_id , sizeof ( suite_id ) ,
" sec " , context , context_len ) ) {
return 0 ;
}
return 1 ;
}
size_t EVP_HPKE_CTX_max_overhead ( const EVP_HPKE_CTX * hpke ) {
assert ( hpke - > is_sender ) ;
return EVP_AEAD_max_overhead ( EVP_AEAD_CTX_aead ( & hpke - > aead_ctx ) ) ;
}
const EVP_HPKE_AEAD * EVP_HPKE_CTX_aead ( const EVP_HPKE_CTX * hpke ) {
return hpke - > aead ;
}
const EVP_HPKE_KDF * EVP_HPKE_CTX_kdf ( const EVP_HPKE_CTX * hpke ) {
return hpke - > kdf ;
}