Added implementation of keying material export (RFC 5705) (API for PRF implementation...
authorThomas Pornin <pornin@bolet.org>
Tue, 4 Jul 2017 18:43:39 +0000 (20:43 +0200)
committerThomas Pornin <pornin@bolet.org>
Tue, 4 Jul 2017 18:43:39 +0000 (20:43 +0200)
15 files changed:
inc/bearssl_prf.h
inc/bearssl_ssl.h
mk/Rules.mk
mk/mkrules.sh
src/inner.h
src/ssl/prf.c
src/ssl/prf_md5sha1.c
src/ssl/prf_sha256.c
src/ssl/prf_sha384.c
src/ssl/ssl_engine.c
src/ssl/ssl_hs_client.c
src/ssl/ssl_hs_common.t0
src/ssl/ssl_hs_server.c
src/ssl/ssl_keyexport.c [new file with mode: 0644]
test/test_crypto.c

index 39d1c42..9d54ad8 100644 (file)
@@ -50,11 +50,32 @@ extern "C" {
  * rely on the SHA-256 based PRF, but some use SHA-384.
  *
  * The PRF always uses as input three parameters: a "secret" (some
- * bytes), a "label" (ASCII string), and a "seed" (again some bytes).
- * An arbitrary output length can be produced.
+ * bytes), a "label" (ASCII string), and a "seed" (again some bytes). An
+ * arbitrary output length can be produced. The "seed" is provided as an
+ * arbitrary number of binary chunks, that gets internally concatenated.
  */
 
-/** \brief PRF implementation for TLS 1.0 and 1.1.
+/**
+ * \brief Type for a seed chunk.
+ *
+ * Each chunk may have an arbitrary length, and may be empty (no byte at
+ * all). If the chunk length is zero, then the pointer to the chunk data
+ * may be `NULL`.
+ */
+typedef struct {
+       /**
+        * \brief Pointer to the chunk data.
+        */
+       const void *data;
+
+       /**
+        * \brief Chunk length (in bytes).
+        */
+       size_t len;
+} br_tls_prf_seed_chunk;
+
+/**
+ * \brief PRF implementation for TLS 1.0 and 1.1.
  *
  * This PRF is the one specified by TLS 1.0 and 1.1. It internally uses
  * MD5 and SHA-1.
@@ -64,14 +85,15 @@ extern "C" {
  * \param secret       secret value (key) for this computation.
  * \param secret_len   length of "secret" (in bytes).
  * \param label        PRF label (zero-terminated ASCII string).
- * \param seed         seed for this computation (usually non-secret).
- * \param seed_len     length of "seed" (in bytes).
+ * \param seed_num     number of seed chunks.
+ * \param seed         seed chnks for this computation (usually non-secret).
  */
 void br_tls10_prf(void *dst, size_t len,
-       const void *secret, size_t secret_len,
-       const char *label, const void *seed, size_t seed_len);
+       const void *secret, size_t secret_len, const char *label,
+       size_t seed_num, const br_tls_prf_seed_chunk *seed);
 
-/** \brief PRF implementation for TLS 1.2, with SHA-256.
+/**
+ * \brief PRF implementation for TLS 1.2, with SHA-256.
  *
  * This PRF is the one specified by TLS 1.2, when the underlying hash
  * function is SHA-256.
@@ -81,14 +103,15 @@ void br_tls10_prf(void *dst, size_t len,
  * \param secret       secret value (key) for this computation.
  * \param secret_len   length of "secret" (in bytes).
  * \param label        PRF label (zero-terminated ASCII string).
- * \param seed         seed for this computation (usually non-secret).
- * \param seed_len     length of "seed" (in bytes).
+ * \param seed_num     number of seed chunks.
+ * \param seed         seed chnks for this computation (usually non-secret).
  */
 void br_tls12_sha256_prf(void *dst, size_t len,
-       const void *secret, size_t secret_len,
-       const char *label, const void *seed, size_t seed_len);
+       const void *secret, size_t secret_len, const char *label,
+       size_t seed_num, const br_tls_prf_seed_chunk *seed);
 
-/** \brief PRF implementation for TLS 1.2, with SHA-384.
+/**
+ * \brief PRF implementation for TLS 1.2, with SHA-384.
  *
  * This PRF is the one specified by TLS 1.2, when the underlying hash
  * function is SHA-384.
@@ -98,26 +121,27 @@ void br_tls12_sha256_prf(void *dst, size_t len,
  * \param secret       secret value (key) for this computation.
  * \param secret_len   length of "secret" (in bytes).
  * \param label        PRF label (zero-terminated ASCII string).
- * \param seed         seed for this computation (usually non-secret).
- * \param seed_len     length of "seed" (in bytes).
+ * \param seed_num     number of seed chunks.
+ * \param seed         seed chnks for this computation (usually non-secret).
  */
 void br_tls12_sha384_prf(void *dst, size_t len,
-       const void *secret, size_t secret_len,
-       const char *label, const void *seed, size_t seed_len);
+       const void *secret, size_t secret_len, const char *label,
+       size_t seed_num, const br_tls_prf_seed_chunk *seed);
 
-/** \brief A convenient type name for a PRF implementation.
+/** 
+ * brief A convenient type name for a PRF implementation.
  *
  * \param dst          destination buffer.
  * \param len          output length (in bytes).
  * \param secret       secret value (key) for this computation.
  * \param secret_len   length of "secret" (in bytes).
  * \param label        PRF label (zero-terminated ASCII string).
- * \param seed         seed for this computation (usually non-secret).
- * \param seed_len     length of "seed" (in bytes).
+ * \param seed_num     number of seed chunks.
+ * \param seed         seed chnks for this computation (usually non-secret).
  */
 typedef void (*br_tls_prf_impl)(void *dst, size_t len,
-       const void *secret, size_t secret_len,
-       const char *label, const void *seed, size_t seed_len);
+       const void *secret, size_t secret_len, const char *label,
+       size_t seed_num, const br_tls_prf_seed_chunk *seed);
 
 #ifdef __cplusplus
 }
index 2ac9e25..ad807ab 100644 (file)
@@ -2075,6 +2075,41 @@ void br_ssl_engine_close(br_ssl_engine_context *cc);
  */
 int br_ssl_engine_renegotiate(br_ssl_engine_context *cc);
 
+/**
+ * \brief Export key material from a connected SSL engine (RFC 5705).
+ *
+ * This calls compute a secret key of arbitrary length from the master
+ * secret of a connected SSL engine. If the provided context is not
+ * currently in "application data" state (initial handshake is not
+ * finished, another handshake is ongoing, or the connection failed or
+ * was closed), then this function returns 0. Otherwise, a secret key of
+ * length `len` bytes is computed and written in the buffer pointed to
+ * by `dst`, and 1 is returned.
+ *
+ * The computed key follows the specification described in RFC 5705.
+ * That RFC includes two key computations, with and without a "context
+ * value". If `context` is `NULL`, then the variant without context is
+ * used; otherwise, the `context_len` bytes located at the address
+ * pointed to by `context` are used in the computation. Note that it
+ * is possible to have a "with context" key with a context length of
+ * zero bytes, by setting `context` to a non-`NULL` value but
+ * `context_len` to 0.
+ *
+ * When context bytes are used, the context length MUST NOT exceed
+ * 65535 bytes.
+ *
+ * \param cc            SSL engine context.
+ * \param dst           destination buffer for exported key.
+ * \param len           exported key length (in bytes).
+ * \param label         disambiguation label.
+ * \param context       context value (or `NULL`).
+ * \param context_len   context length (in bytes).
+ * \return  1 on success, 0 on error.
+ */
+int br_ssl_key_export(br_ssl_engine_context *cc,
+       void *dst, size_t len, const char *label,
+       const void *context, size_t context_len);
+
 /*
  * Pre-declaration for the SSL client context.
  */
index 4d609cc..5a50049 100644 (file)
@@ -1,6 +1,6 @@
 # Automatically generated rules. Use 'mkrules.sh' to modify/regenerate.
 
-OBJ = $(OBJDIR)$Pccopy$O $(OBJDIR)$Pdec16be$O $(OBJDIR)$Pdec16le$O $(OBJDIR)$Pdec32be$O $(OBJDIR)$Pdec32le$O $(OBJDIR)$Pdec64be$O $(OBJDIR)$Pdec64le$O $(OBJDIR)$Penc16be$O $(OBJDIR)$Penc16le$O $(OBJDIR)$Penc32be$O $(OBJDIR)$Penc32le$O $(OBJDIR)$Penc64be$O $(OBJDIR)$Penc64le$O $(OBJDIR)$Ppemdec$O $(OBJDIR)$Pec_all_m15$O $(OBJDIR)$Pec_all_m31$O $(OBJDIR)$Pec_c25519_i15$O $(OBJDIR)$Pec_c25519_i31$O $(OBJDIR)$Pec_c25519_m15$O $(OBJDIR)$Pec_c25519_m31$O $(OBJDIR)$Pec_curve25519$O $(OBJDIR)$Pec_default$O $(OBJDIR)$Pec_p256_m15$O $(OBJDIR)$Pec_p256_m31$O $(OBJDIR)$Pec_prime_i15$O $(OBJDIR)$Pec_prime_i31$O $(OBJDIR)$Pec_secp256r1$O $(OBJDIR)$Pec_secp384r1$O $(OBJDIR)$Pec_secp521r1$O $(OBJDIR)$Pecdsa_atr$O $(OBJDIR)$Pecdsa_default_sign_asn1$O $(OBJDIR)$Pecdsa_default_sign_raw$O $(OBJDIR)$Pecdsa_default_vrfy_asn1$O $(OBJDIR)$Pecdsa_default_vrfy_raw$O $(OBJDIR)$Pecdsa_i15_bits$O $(OBJDIR)$Pecdsa_i15_sign_asn1$O $(OBJDIR)$Pecdsa_i15_sign_raw$O $(OBJDIR)$Pecdsa_i15_vrfy_asn1$O $(OBJDIR)$Pecdsa_i15_vrfy_raw$O $(OBJDIR)$Pecdsa_i31_bits$O $(OBJDIR)$Pecdsa_i31_sign_asn1$O $(OBJDIR)$Pecdsa_i31_sign_raw$O $(OBJDIR)$Pecdsa_i31_vrfy_asn1$O $(OBJDIR)$Pecdsa_i31_vrfy_raw$O $(OBJDIR)$Pecdsa_rta$O $(OBJDIR)$Pdig_oid$O $(OBJDIR)$Pdig_size$O $(OBJDIR)$Pghash_ctmul$O $(OBJDIR)$Pghash_ctmul32$O $(OBJDIR)$Pghash_ctmul64$O $(OBJDIR)$Pghash_pclmul$O $(OBJDIR)$Pghash_pwr8$O $(OBJDIR)$Pmd5$O $(OBJDIR)$Pmd5sha1$O $(OBJDIR)$Pmultihash$O $(OBJDIR)$Psha1$O $(OBJDIR)$Psha2big$O $(OBJDIR)$Psha2small$O $(OBJDIR)$Pi15_add$O $(OBJDIR)$Pi15_bitlen$O $(OBJDIR)$Pi15_decmod$O $(OBJDIR)$Pi15_decode$O $(OBJDIR)$Pi15_decred$O $(OBJDIR)$Pi15_encode$O $(OBJDIR)$Pi15_fmont$O $(OBJDIR)$Pi15_iszero$O $(OBJDIR)$Pi15_modpow$O $(OBJDIR)$Pi15_modpow2$O $(OBJDIR)$Pi15_montmul$O $(OBJDIR)$Pi15_mulacc$O $(OBJDIR)$Pi15_muladd$O $(OBJDIR)$Pi15_ninv15$O $(OBJDIR)$Pi15_reduce$O $(OBJDIR)$Pi15_rshift$O $(OBJDIR)$Pi15_sub$O $(OBJDIR)$Pi15_tmont$O $(OBJDIR)$Pi31_add$O $(OBJDIR)$Pi31_bitlen$O $(OBJDIR)$Pi31_decmod$O $(OBJDIR)$Pi31_decode$O $(OBJDIR)$Pi31_decred$O $(OBJDIR)$Pi31_encode$O $(OBJDIR)$Pi31_fmont$O $(OBJDIR)$Pi31_iszero$O $(OBJDIR)$Pi31_modpow$O $(OBJDIR)$Pi31_modpow2$O $(OBJDIR)$Pi31_montmul$O $(OBJDIR)$Pi31_mulacc$O $(OBJDIR)$Pi31_muladd$O $(OBJDIR)$Pi31_ninv31$O $(OBJDIR)$Pi31_reduce$O $(OBJDIR)$Pi31_rshift$O $(OBJDIR)$Pi31_sub$O $(OBJDIR)$Pi31_tmont$O $(OBJDIR)$Pi32_add$O $(OBJDIR)$Pi32_bitlen$O $(OBJDIR)$Pi32_decmod$O $(OBJDIR)$Pi32_decode$O $(OBJDIR)$Pi32_decred$O $(OBJDIR)$Pi32_div32$O $(OBJDIR)$Pi32_encode$O $(OBJDIR)$Pi32_fmont$O $(OBJDIR)$Pi32_iszero$O $(OBJDIR)$Pi32_modpow$O $(OBJDIR)$Pi32_montmul$O $(OBJDIR)$Pi32_mulacc$O $(OBJDIR)$Pi32_muladd$O $(OBJDIR)$Pi32_ninv32$O $(OBJDIR)$Pi32_reduce$O $(OBJDIR)$Pi32_sub$O $(OBJDIR)$Pi32_tmont$O $(OBJDIR)$Pi62_modpow2$O $(OBJDIR)$Phmac$O $(OBJDIR)$Phmac_ct$O $(OBJDIR)$Phmac_drbg$O $(OBJDIR)$Prsa_default_pkcs1_sign$O $(OBJDIR)$Prsa_default_pkcs1_vrfy$O $(OBJDIR)$Prsa_default_priv$O $(OBJDIR)$Prsa_default_pub$O $(OBJDIR)$Prsa_i15_pkcs1_sign$O $(OBJDIR)$Prsa_i15_pkcs1_vrfy$O $(OBJDIR)$Prsa_i15_priv$O $(OBJDIR)$Prsa_i15_pub$O $(OBJDIR)$Prsa_i31_pkcs1_sign$O $(OBJDIR)$Prsa_i31_pkcs1_vrfy$O $(OBJDIR)$Prsa_i31_priv$O $(OBJDIR)$Prsa_i31_pub$O $(OBJDIR)$Prsa_i32_pkcs1_sign$O $(OBJDIR)$Prsa_i32_pkcs1_vrfy$O $(OBJDIR)$Prsa_i32_priv$O $(OBJDIR)$Prsa_i32_pub$O $(OBJDIR)$Prsa_i62_pkcs1_sign$O $(OBJDIR)$Prsa_i62_pkcs1_vrfy$O $(OBJDIR)$Prsa_i62_priv$O $(OBJDIR)$Prsa_i62_pub$O $(OBJDIR)$Prsa_pkcs1_sig_pad$O $(OBJDIR)$Prsa_pkcs1_sig_unpad$O $(OBJDIR)$Prsa_ssl_decrypt$O $(OBJDIR)$Pprf$O $(OBJDIR)$Pprf_md5sha1$O $(OBJDIR)$Pprf_sha256$O $(OBJDIR)$Pprf_sha384$O $(OBJDIR)$Pssl_ccert_single_ec$O $(OBJDIR)$Pssl_ccert_single_rsa$O $(OBJDIR)$Pssl_client$O $(OBJDIR)$Pssl_client_default_rsapub$O $(OBJDIR)$Pssl_client_full$O $(OBJDIR)$Pssl_engine$O $(OBJDIR)$Pssl_engine_default_aescbc$O $(OBJDIR)$Pssl_engine_default_aesgcm$O $(OBJDIR)$Pssl_engine_default_chapol$O $(OBJDIR)$Pssl_engine_default_descbc$O $(OBJDIR)$Pssl_engine_default_ec$O $(OBJDIR)$Pssl_engine_default_ecdsa$O $(OBJDIR)$Pssl_engine_default_rsavrfy$O $(OBJDIR)$Pssl_hashes$O $(OBJDIR)$Pssl_hs_client$O $(OBJDIR)$Pssl_hs_server$O $(OBJDIR)$Pssl_io$O $(OBJDIR)$Pssl_lru$O $(OBJDIR)$Pssl_rec_cbc$O $(OBJDIR)$Pssl_rec_chapol$O $(OBJDIR)$Pssl_rec_gcm$O $(OBJDIR)$Pssl_scert_single_ec$O $(OBJDIR)$Pssl_scert_single_rsa$O $(OBJDIR)$Pssl_server$O $(OBJDIR)$Pssl_server_full_ec$O $(OBJDIR)$Pssl_server_full_rsa$O $(OBJDIR)$Pssl_server_mine2c$O $(OBJDIR)$Pssl_server_mine2g$O $(OBJDIR)$Pssl_server_minf2c$O $(OBJDIR)$Pssl_server_minf2g$O $(OBJDIR)$Pssl_server_minr2g$O $(OBJDIR)$Pssl_server_minu2g$O $(OBJDIR)$Pssl_server_minv2g$O $(OBJDIR)$Paes_big_cbcdec$O $(OBJDIR)$Paes_big_cbcenc$O $(OBJDIR)$Paes_big_ctr$O $(OBJDIR)$Paes_big_dec$O $(OBJDIR)$Paes_big_enc$O $(OBJDIR)$Paes_common$O $(OBJDIR)$Paes_ct$O $(OBJDIR)$Paes_ct64$O $(OBJDIR)$Paes_ct64_cbcdec$O $(OBJDIR)$Paes_ct64_cbcenc$O $(OBJDIR)$Paes_ct64_ctr$O $(OBJDIR)$Paes_ct64_dec$O $(OBJDIR)$Paes_ct64_enc$O $(OBJDIR)$Paes_ct_cbcdec$O $(OBJDIR)$Paes_ct_cbcenc$O $(OBJDIR)$Paes_ct_ctr$O $(OBJDIR)$Paes_ct_dec$O $(OBJDIR)$Paes_ct_enc$O $(OBJDIR)$Paes_pwr8$O $(OBJDIR)$Paes_pwr8_cbcdec$O $(OBJDIR)$Paes_pwr8_cbcenc$O $(OBJDIR)$Paes_pwr8_ctr$O $(OBJDIR)$Paes_small_cbcdec$O $(OBJDIR)$Paes_small_cbcenc$O $(OBJDIR)$Paes_small_ctr$O $(OBJDIR)$Paes_small_dec$O $(OBJDIR)$Paes_small_enc$O $(OBJDIR)$Paes_x86ni$O $(OBJDIR)$Paes_x86ni_cbcdec$O $(OBJDIR)$Paes_x86ni_cbcenc$O $(OBJDIR)$Paes_x86ni_ctr$O $(OBJDIR)$Pchacha20_ct$O $(OBJDIR)$Pdes_ct$O $(OBJDIR)$Pdes_ct_cbcdec$O $(OBJDIR)$Pdes_ct_cbcenc$O $(OBJDIR)$Pdes_support$O $(OBJDIR)$Pdes_tab$O $(OBJDIR)$Pdes_tab_cbcdec$O $(OBJDIR)$Pdes_tab_cbcenc$O $(OBJDIR)$Ppoly1305_ctmul$O $(OBJDIR)$Ppoly1305_ctmul32$O $(OBJDIR)$Ppoly1305_ctmulq$O $(OBJDIR)$Ppoly1305_i15$O $(OBJDIR)$Pskey_decoder$O $(OBJDIR)$Px509_decoder$O $(OBJDIR)$Px509_knownkey$O $(OBJDIR)$Px509_minimal$O $(OBJDIR)$Px509_minimal_full$O
+OBJ = $(OBJDIR)$Pccopy$O $(OBJDIR)$Pdec16be$O $(OBJDIR)$Pdec16le$O $(OBJDIR)$Pdec32be$O $(OBJDIR)$Pdec32le$O $(OBJDIR)$Pdec64be$O $(OBJDIR)$Pdec64le$O $(OBJDIR)$Penc16be$O $(OBJDIR)$Penc16le$O $(OBJDIR)$Penc32be$O $(OBJDIR)$Penc32le$O $(OBJDIR)$Penc64be$O $(OBJDIR)$Penc64le$O $(OBJDIR)$Ppemdec$O $(OBJDIR)$Pec_all_m15$O $(OBJDIR)$Pec_all_m31$O $(OBJDIR)$Pec_c25519_i15$O $(OBJDIR)$Pec_c25519_i31$O $(OBJDIR)$Pec_c25519_m15$O $(OBJDIR)$Pec_c25519_m31$O $(OBJDIR)$Pec_curve25519$O $(OBJDIR)$Pec_default$O $(OBJDIR)$Pec_p256_m15$O $(OBJDIR)$Pec_p256_m31$O $(OBJDIR)$Pec_prime_i15$O $(OBJDIR)$Pec_prime_i31$O $(OBJDIR)$Pec_secp256r1$O $(OBJDIR)$Pec_secp384r1$O $(OBJDIR)$Pec_secp521r1$O $(OBJDIR)$Pecdsa_atr$O $(OBJDIR)$Pecdsa_default_sign_asn1$O $(OBJDIR)$Pecdsa_default_sign_raw$O $(OBJDIR)$Pecdsa_default_vrfy_asn1$O $(OBJDIR)$Pecdsa_default_vrfy_raw$O $(OBJDIR)$Pecdsa_i15_bits$O $(OBJDIR)$Pecdsa_i15_sign_asn1$O $(OBJDIR)$Pecdsa_i15_sign_raw$O $(OBJDIR)$Pecdsa_i15_vrfy_asn1$O $(OBJDIR)$Pecdsa_i15_vrfy_raw$O $(OBJDIR)$Pecdsa_i31_bits$O $(OBJDIR)$Pecdsa_i31_sign_asn1$O $(OBJDIR)$Pecdsa_i31_sign_raw$O $(OBJDIR)$Pecdsa_i31_vrfy_asn1$O $(OBJDIR)$Pecdsa_i31_vrfy_raw$O $(OBJDIR)$Pecdsa_rta$O $(OBJDIR)$Pdig_oid$O $(OBJDIR)$Pdig_size$O $(OBJDIR)$Pghash_ctmul$O $(OBJDIR)$Pghash_ctmul32$O $(OBJDIR)$Pghash_ctmul64$O $(OBJDIR)$Pghash_pclmul$O $(OBJDIR)$Pghash_pwr8$O $(OBJDIR)$Pmd5$O $(OBJDIR)$Pmd5sha1$O $(OBJDIR)$Pmultihash$O $(OBJDIR)$Psha1$O $(OBJDIR)$Psha2big$O $(OBJDIR)$Psha2small$O $(OBJDIR)$Pi15_add$O $(OBJDIR)$Pi15_bitlen$O $(OBJDIR)$Pi15_decmod$O $(OBJDIR)$Pi15_decode$O $(OBJDIR)$Pi15_decred$O $(OBJDIR)$Pi15_encode$O $(OBJDIR)$Pi15_fmont$O $(OBJDIR)$Pi15_iszero$O $(OBJDIR)$Pi15_modpow$O $(OBJDIR)$Pi15_modpow2$O $(OBJDIR)$Pi15_montmul$O $(OBJDIR)$Pi15_mulacc$O $(OBJDIR)$Pi15_muladd$O $(OBJDIR)$Pi15_ninv15$O $(OBJDIR)$Pi15_reduce$O $(OBJDIR)$Pi15_rshift$O $(OBJDIR)$Pi15_sub$O $(OBJDIR)$Pi15_tmont$O $(OBJDIR)$Pi31_add$O $(OBJDIR)$Pi31_bitlen$O $(OBJDIR)$Pi31_decmod$O $(OBJDIR)$Pi31_decode$O $(OBJDIR)$Pi31_decred$O $(OBJDIR)$Pi31_encode$O $(OBJDIR)$Pi31_fmont$O $(OBJDIR)$Pi31_iszero$O $(OBJDIR)$Pi31_modpow$O $(OBJDIR)$Pi31_modpow2$O $(OBJDIR)$Pi31_montmul$O $(OBJDIR)$Pi31_mulacc$O $(OBJDIR)$Pi31_muladd$O $(OBJDIR)$Pi31_ninv31$O $(OBJDIR)$Pi31_reduce$O $(OBJDIR)$Pi31_rshift$O $(OBJDIR)$Pi31_sub$O $(OBJDIR)$Pi31_tmont$O $(OBJDIR)$Pi32_add$O $(OBJDIR)$Pi32_bitlen$O $(OBJDIR)$Pi32_decmod$O $(OBJDIR)$Pi32_decode$O $(OBJDIR)$Pi32_decred$O $(OBJDIR)$Pi32_div32$O $(OBJDIR)$Pi32_encode$O $(OBJDIR)$Pi32_fmont$O $(OBJDIR)$Pi32_iszero$O $(OBJDIR)$Pi32_modpow$O $(OBJDIR)$Pi32_montmul$O $(OBJDIR)$Pi32_mulacc$O $(OBJDIR)$Pi32_muladd$O $(OBJDIR)$Pi32_ninv32$O $(OBJDIR)$Pi32_reduce$O $(OBJDIR)$Pi32_sub$O $(OBJDIR)$Pi32_tmont$O $(OBJDIR)$Pi62_modpow2$O $(OBJDIR)$Phmac$O $(OBJDIR)$Phmac_ct$O $(OBJDIR)$Phmac_drbg$O $(OBJDIR)$Prsa_default_pkcs1_sign$O $(OBJDIR)$Prsa_default_pkcs1_vrfy$O $(OBJDIR)$Prsa_default_priv$O $(OBJDIR)$Prsa_default_pub$O $(OBJDIR)$Prsa_i15_pkcs1_sign$O $(OBJDIR)$Prsa_i15_pkcs1_vrfy$O $(OBJDIR)$Prsa_i15_priv$O $(OBJDIR)$Prsa_i15_pub$O $(OBJDIR)$Prsa_i31_pkcs1_sign$O $(OBJDIR)$Prsa_i31_pkcs1_vrfy$O $(OBJDIR)$Prsa_i31_priv$O $(OBJDIR)$Prsa_i31_pub$O $(OBJDIR)$Prsa_i32_pkcs1_sign$O $(OBJDIR)$Prsa_i32_pkcs1_vrfy$O $(OBJDIR)$Prsa_i32_priv$O $(OBJDIR)$Prsa_i32_pub$O $(OBJDIR)$Prsa_i62_pkcs1_sign$O $(OBJDIR)$Prsa_i62_pkcs1_vrfy$O $(OBJDIR)$Prsa_i62_priv$O $(OBJDIR)$Prsa_i62_pub$O $(OBJDIR)$Prsa_pkcs1_sig_pad$O $(OBJDIR)$Prsa_pkcs1_sig_unpad$O $(OBJDIR)$Prsa_ssl_decrypt$O $(OBJDIR)$Pprf$O $(OBJDIR)$Pprf_md5sha1$O $(OBJDIR)$Pprf_sha256$O $(OBJDIR)$Pprf_sha384$O $(OBJDIR)$Pssl_ccert_single_ec$O $(OBJDIR)$Pssl_ccert_single_rsa$O $(OBJDIR)$Pssl_client$O $(OBJDIR)$Pssl_client_default_rsapub$O $(OBJDIR)$Pssl_client_full$O $(OBJDIR)$Pssl_engine$O $(OBJDIR)$Pssl_engine_default_aescbc$O $(OBJDIR)$Pssl_engine_default_aesgcm$O $(OBJDIR)$Pssl_engine_default_chapol$O $(OBJDIR)$Pssl_engine_default_descbc$O $(OBJDIR)$Pssl_engine_default_ec$O $(OBJDIR)$Pssl_engine_default_ecdsa$O $(OBJDIR)$Pssl_engine_default_rsavrfy$O $(OBJDIR)$Pssl_hashes$O $(OBJDIR)$Pssl_hs_client$O $(OBJDIR)$Pssl_hs_server$O $(OBJDIR)$Pssl_io$O $(OBJDIR)$Pssl_keyexport$O $(OBJDIR)$Pssl_lru$O $(OBJDIR)$Pssl_rec_cbc$O $(OBJDIR)$Pssl_rec_chapol$O $(OBJDIR)$Pssl_rec_gcm$O $(OBJDIR)$Pssl_scert_single_ec$O $(OBJDIR)$Pssl_scert_single_rsa$O $(OBJDIR)$Pssl_server$O $(OBJDIR)$Pssl_server_full_ec$O $(OBJDIR)$Pssl_server_full_rsa$O $(OBJDIR)$Pssl_server_mine2c$O $(OBJDIR)$Pssl_server_mine2g$O $(OBJDIR)$Pssl_server_minf2c$O $(OBJDIR)$Pssl_server_minf2g$O $(OBJDIR)$Pssl_server_minr2g$O $(OBJDIR)$Pssl_server_minu2g$O $(OBJDIR)$Pssl_server_minv2g$O $(OBJDIR)$Paes_big_cbcdec$O $(OBJDIR)$Paes_big_cbcenc$O $(OBJDIR)$Paes_big_ctr$O $(OBJDIR)$Paes_big_dec$O $(OBJDIR)$Paes_big_enc$O $(OBJDIR)$Paes_common$O $(OBJDIR)$Paes_ct$O $(OBJDIR)$Paes_ct64$O $(OBJDIR)$Paes_ct64_cbcdec$O $(OBJDIR)$Paes_ct64_cbcenc$O $(OBJDIR)$Paes_ct64_ctr$O $(OBJDIR)$Paes_ct64_dec$O $(OBJDIR)$Paes_ct64_enc$O $(OBJDIR)$Paes_ct_cbcdec$O $(OBJDIR)$Paes_ct_cbcenc$O $(OBJDIR)$Paes_ct_ctr$O $(OBJDIR)$Paes_ct_dec$O $(OBJDIR)$Paes_ct_enc$O $(OBJDIR)$Paes_pwr8$O $(OBJDIR)$Paes_pwr8_cbcdec$O $(OBJDIR)$Paes_pwr8_cbcenc$O $(OBJDIR)$Paes_pwr8_ctr$O $(OBJDIR)$Paes_small_cbcdec$O $(OBJDIR)$Paes_small_cbcenc$O $(OBJDIR)$Paes_small_ctr$O $(OBJDIR)$Paes_small_dec$O $(OBJDIR)$Paes_small_enc$O $(OBJDIR)$Paes_x86ni$O $(OBJDIR)$Paes_x86ni_cbcdec$O $(OBJDIR)$Paes_x86ni_cbcenc$O $(OBJDIR)$Paes_x86ni_ctr$O $(OBJDIR)$Pchacha20_ct$O $(OBJDIR)$Pdes_ct$O $(OBJDIR)$Pdes_ct_cbcdec$O $(OBJDIR)$Pdes_ct_cbcenc$O $(OBJDIR)$Pdes_support$O $(OBJDIR)$Pdes_tab$O $(OBJDIR)$Pdes_tab_cbcdec$O $(OBJDIR)$Pdes_tab_cbcenc$O $(OBJDIR)$Ppoly1305_ctmul$O $(OBJDIR)$Ppoly1305_ctmul32$O $(OBJDIR)$Ppoly1305_ctmulq$O $(OBJDIR)$Ppoly1305_i15$O $(OBJDIR)$Pskey_decoder$O $(OBJDIR)$Px509_decoder$O $(OBJDIR)$Px509_knownkey$O $(OBJDIR)$Px509_minimal$O $(OBJDIR)$Px509_minimal_full$O
 OBJBRSSL = $(OBJDIR)$Pbrssl$O $(OBJDIR)$Pcerts$O $(OBJDIR)$Pchain$O $(OBJDIR)$Pclient$O $(OBJDIR)$Perrors$O $(OBJDIR)$Pfiles$O $(OBJDIR)$Pkeys$O $(OBJDIR)$Pnames$O $(OBJDIR)$Pserver$O $(OBJDIR)$Pskey$O $(OBJDIR)$Psslio$O $(OBJDIR)$Pta$O $(OBJDIR)$Pvector$O $(OBJDIR)$Pverify$O $(OBJDIR)$Pxmem$O
 OBJTESTCRYPTO = $(OBJDIR)$Ptest_crypto$O
 OBJTESTSPEED = $(OBJDIR)$Ptest_speed$O
@@ -536,6 +536,9 @@ $(OBJDIR)$Pssl_hs_server$O: src$Pssl$Pssl_hs_server.c $(HEADERSPRIV)
 $(OBJDIR)$Pssl_io$O: src$Pssl$Pssl_io.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pssl_io$O src$Pssl$Pssl_io.c
 
+$(OBJDIR)$Pssl_keyexport$O: src$Pssl$Pssl_keyexport.c $(HEADERSPRIV)
+       $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pssl_keyexport$O src$Pssl$Pssl_keyexport.c
+
 $(OBJDIR)$Pssl_lru$O: src$Pssl$Pssl_lru.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pssl_lru$O src$Pssl$Pssl_lru.c
 
index cd5a617..1cc9c88 100755 (executable)
@@ -208,6 +208,7 @@ coresrc=" \
        src/ssl/ssl_hs_client.c \
        src/ssl/ssl_hs_server.c \
        src/ssl/ssl_io.c \
+       src/ssl/ssl_keyexport.c \
        src/ssl/ssl_lru.c \
        src/ssl/ssl_rec_cbc.c \
        src/ssl/ssl_rec_chapol.c \
index 3830c7c..fb7f4a4 100644 (file)
@@ -509,8 +509,8 @@ void br_sha2small_round(const unsigned char *buf, uint32_t *val);
  */
 void br_tls_phash(void *dst, size_t len,
        const br_hash_class *dig,
-       const void *secret, size_t secret_len,
-       const char *label, const void *seed, size_t seed_len);
+       const void *secret, size_t secret_len, const char *label,
+       size_t seed_num, const br_tls_prf_seed_chunk *seed);
 
 /*
  * Copy all configured hash implementations from a multihash context
index 43a74c3..f04a5fb 100644 (file)
 void
 br_tls_phash(void *dst, size_t len,
        const br_hash_class *dig,
-       const void *secret, size_t secret_len,
-       const char *label, const void *seed, size_t seed_len)
+       const void *secret, size_t secret_len, const char *label,
+       size_t seed_num, const br_tls_prf_seed_chunk *seed)
 {
        unsigned char *buf;
        unsigned char tmp[64], a[64];
        br_hmac_key_context kc;
        br_hmac_context hc;
-       size_t label_len, hlen;
+       size_t label_len, hlen, u;
 
        if (len == 0) {
                return;
@@ -46,15 +46,17 @@ br_tls_phash(void *dst, size_t len,
        br_hmac_key_init(&kc, dig, secret, secret_len);
        br_hmac_init(&hc, &kc, 0);
        br_hmac_update(&hc, label, label_len);
-       br_hmac_update(&hc, seed, seed_len);
+       for (u = 0; u < seed_num; u ++) {
+               br_hmac_update(&hc, seed[u].data, seed[u].len);
+       }
        br_hmac_out(&hc, a);
        for (;;) {
-               size_t u;
-
                br_hmac_init(&hc, &kc, 0);
                br_hmac_update(&hc, a, hlen);
                br_hmac_update(&hc, label, label_len);
-               br_hmac_update(&hc, seed, seed_len);
+               for (u = 0; u < seed_num; u ++) {
+                       br_hmac_update(&hc, seed[u].data, seed[u].len);
+               }
                br_hmac_out(&hc, tmp);
                for (u = 0; u < hlen && u < len; u ++) {
                        buf[u] ^= tmp[u];
index 414dfed..3212833 100644 (file)
@@ -27,8 +27,8 @@
 /* see bearssl.h */
 void
 br_tls10_prf(void *dst, size_t len,
-       const void *secret, size_t secret_len,
-       const char *label, const void *seed, size_t seed_len)
+       const void *secret, size_t secret_len, const char *label,
+       size_t seed_num, const br_tls_prf_seed_chunk *seed)
 {
        const unsigned char *s1;
        size_t slen;
@@ -37,7 +37,7 @@ br_tls10_prf(void *dst, size_t len,
        slen = (secret_len + 1) >> 1;
        memset(dst, 0, len);
        br_tls_phash(dst, len, &br_md5_vtable,
-               s1, slen, label, seed, seed_len);
+               s1, slen, label, seed_num, seed);
        br_tls_phash(dst, len, &br_sha1_vtable,
-               s1 + secret_len - slen, slen, label, seed, seed_len);
+               s1 + secret_len - slen, slen, label, seed_num, seed);
 }
index cec916a..76041de 100644 (file)
 /* see bearssl.h */
 void
 br_tls12_sha256_prf(void *dst, size_t len,
-       const void *secret, size_t secret_len,
-       const char *label, const void *seed, size_t seed_len)
+       const void *secret, size_t secret_len, const char *label,
+       size_t seed_num, const br_tls_prf_seed_chunk *seed)
 {
        memset(dst, 0, len);
        br_tls_phash(dst, len, &br_sha256_vtable,
-               secret, secret_len, label, seed, seed_len);
+               secret, secret_len, label, seed_num, seed);
 }
index 5069560..c20c4e6 100644 (file)
 /* see bearssl.h */
 void
 br_tls12_sha384_prf(void *dst, size_t len,
-       const void *secret, size_t secret_len,
-       const char *label, const void *seed, size_t seed_len)
+       const void *secret, size_t secret_len, const char *label,
+       size_t seed_num, const br_tls_prf_seed_chunk *seed)
 {
        memset(dst, 0, len);
        br_tls_phash(dst, len, &br_sha384_vtable,
-               secret, secret_len, label, seed, seed_len);
+               secret, secret_len, label, seed_num, seed);
 }
index cdd9bcb..7106a5e 100644 (file)
@@ -1335,13 +1335,14 @@ br_ssl_engine_compute_master(br_ssl_engine_context *cc,
        int prf_id, const void *pms, size_t pms_len)
 {
        br_tls_prf_impl iprf;
-       unsigned char seed[64];
+       br_tls_prf_seed_chunk seed[2] = {
+               { cc->client_random, sizeof cc->client_random },
+               { cc->server_random, sizeof cc->server_random }
+       };
 
        iprf = br_ssl_engine_get_PRF(cc, prf_id);
-       memcpy(seed, cc->client_random, 32);
-       memcpy(seed + 32, cc->server_random, 32);
        iprf(cc->session.master_secret, sizeof cc->session.master_secret,
-               pms, pms_len, "master secret", seed, sizeof seed);
+               pms, pms_len, "master secret", 2, seed);
 }
 
 /*
@@ -1352,14 +1353,15 @@ compute_key_block(br_ssl_engine_context *cc, int prf_id,
        size_t half_len, unsigned char *kb)
 {
        br_tls_prf_impl iprf;
-       unsigned char seed[64];
+       br_tls_prf_seed_chunk seed[2] = {
+               { cc->server_random, sizeof cc->server_random },
+               { cc->client_random, sizeof cc->client_random }
+       };
 
        iprf = br_ssl_engine_get_PRF(cc, prf_id);
-       memcpy(seed, cc->server_random, 32);
-       memcpy(seed + 32, cc->client_random, 32);
        iprf(kb, half_len << 1,
                cc->session.master_secret, sizeof cc->session.master_secret,
-               "key expansion", seed, sizeof seed);
+               "key expansion", 2, seed);
 }
 
 /* see inner.h */
index e3864c2..34cb407 100644 (file)
@@ -1192,21 +1192,22 @@ br_ssl_hs_client_run(void *t0ctx)
 
        int prf_id = T0_POP();
        int from_client = T0_POPi();
-       unsigned char seed[48];
-       size_t seed_len;
+       unsigned char tmp[48];
+       br_tls_prf_seed_chunk seed;
 
        br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
+       seed.data = tmp;
        if (ENG->session.version >= BR_TLS12) {
-               seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
+               seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp);
        } else {
-               br_multihash_out(&ENG->mhash, br_md5_ID, seed);
-               br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
-               seed_len = 36;
+               br_multihash_out(&ENG->mhash, br_md5_ID, tmp);
+               br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16);
+               seed.len = 36;
        }
        prf(ENG->pad, 12, ENG->session.master_secret,
                sizeof ENG->session.master_secret,
                from_client ? "client finished" : "server finished",
-               seed, seed_len);
+               1, &seed);
 
                                }
                                break;
index 05b1797..962daa7 100644 (file)
@@ -754,6 +754,10 @@ cc: mkrand ( addr len -- ) {
 \ -- PRF for TLS-1.2:
 \       4  with SHA-256
 \       5  with SHA-384
+\
+\ WARNING: if adding a new cipher suite that does not use SHA-256 for the
+\ PRF (with TLS 1.2), be sure to check the suites_sha384[] array defined
+\ in ssl/ssl_keyexport.c
 
 data: cipher-suite-def
 
@@ -1019,21 +1023,22 @@ cc: switch-chapol-in ( is_client prf_id -- ) {
 cc: compute-Finished-inner ( from_client prf_id -- ) {
        int prf_id = T0_POP();
        int from_client = T0_POPi();
-       unsigned char seed[48];
-       size_t seed_len;
+       unsigned char tmp[48];
+       br_tls_prf_seed_chunk seed;
 
        br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
+       seed.data = tmp;
        if (ENG->session.version >= BR_TLS12) {
-               seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
+               seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp);
        } else {
-               br_multihash_out(&ENG->mhash, br_md5_ID, seed);
-               br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
-               seed_len = 36;
+               br_multihash_out(&ENG->mhash, br_md5_ID, tmp);
+               br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16);
+               seed.len = 36;
        }
        prf(ENG->pad, 12, ENG->session.master_secret,
                sizeof ENG->session.master_secret,
                from_client ? "client finished" : "server finished",
-               seed, seed_len);
+               1, &seed);
 }
 
 \ Receive ChangeCipherSpec and Finished from the peer.
index 2476278..8a4c01b 100644 (file)
@@ -1250,21 +1250,22 @@ br_ssl_hs_server_run(void *t0ctx)
 
        int prf_id = T0_POP();
        int from_client = T0_POPi();
-       unsigned char seed[48];
-       size_t seed_len;
+       unsigned char tmp[48];
+       br_tls_prf_seed_chunk seed;
 
        br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
+       seed.data = tmp;
        if (ENG->session.version >= BR_TLS12) {
-               seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
+               seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp);
        } else {
-               br_multihash_out(&ENG->mhash, br_md5_ID, seed);
-               br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
-               seed_len = 36;
+               br_multihash_out(&ENG->mhash, br_md5_ID, tmp);
+               br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16);
+               seed.len = 36;
        }
        prf(ENG->pad, 12, ENG->session.master_secret,
                sizeof ENG->session.master_secret,
                from_client ? "client finished" : "server finished",
-               seed, seed_len);
+               1, &seed);
 
                                }
                                break;
diff --git a/src/ssl/ssl_keyexport.c b/src/ssl/ssl_keyexport.c
new file mode 100644 (file)
index 0000000..58e6dc3
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * Supported cipher suites that use SHA-384 for the PRF when selected
+ * for TLS 1.2. All other cipher suites are deemed to use SHA-256.
+ */
+static const uint16_t suites_sha384[] = {
+       BR_TLS_RSA_WITH_AES_256_GCM_SHA384,
+       BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+       BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
+       BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+       BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
+       BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+       BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
+       BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+       BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
+};
+
+/* see bearssl_ssl.h */
+int
+br_ssl_key_export(br_ssl_engine_context *cc,
+       void *dst, size_t len, const char *label,
+       const void *context, size_t context_len)
+{
+       br_tls_prf_seed_chunk chunks[4];
+       br_tls_prf_impl iprf;
+       size_t num_chunks, u;
+       unsigned char tmp[2];
+       int prf_id;
+
+       if (cc->application_data != 1) {
+               return 0;
+       }
+       chunks[0].data = cc->client_random;
+       chunks[0].len = sizeof cc->client_random;
+       chunks[1].data = cc->server_random;
+       chunks[1].len = sizeof cc->server_random;
+       if (context != NULL) {
+               br_enc16be(tmp, (unsigned)context_len);
+               chunks[2].data = tmp;
+               chunks[2].len = 2;
+               chunks[3].data = context;
+               chunks[3].len = context_len;
+               num_chunks = 4;
+       } else {
+               num_chunks = 2;
+       }
+       prf_id = BR_SSLPRF_SHA256;
+       for (u = 0; u < (sizeof suites_sha384) / sizeof(uint16_t); u ++) {
+               if (suites_sha384[u] == cc->session.cipher_suite) {
+                       prf_id = BR_SSLPRF_SHA384;
+               }
+       }
+       iprf = br_ssl_engine_get_PRF(cc, prf_id);
+       iprf(dst, len,
+               cc->session.master_secret, sizeof cc->session.master_secret,
+               label, num_chunks, chunks);
+       return 1;
+}
index ae0c712..7e282ab 100644 (file)
@@ -1075,21 +1075,43 @@ test_HMAC_DRBG(void)
 }
 
 static void
-do_KAT_PRF(
-       void (*prf)(void *dst, size_t len,
-               const void *secret, size_t secret_len,
-               const char *label, const void *seed, size_t seed_len),
+do_KAT_PRF(br_tls_prf_impl prf,
        const char *ssecret, const char *label, const char *sseed,
        const char *sref)
 {
        unsigned char secret[100], seed[100], ref[500], out[500];
        size_t secret_len, seed_len, ref_len;
+       br_tls_prf_seed_chunk chunks[2];
 
        secret_len = hextobin(secret, ssecret);
        seed_len = hextobin(seed, sseed);
        ref_len = hextobin(ref, sref);
-       prf(out, ref_len, secret, secret_len, label, seed, seed_len);
-       check_equals("TLS PRF KAT", out, ref, ref_len);
+
+       chunks[0].data = seed;
+       chunks[0].len = seed_len;
+       prf(out, ref_len, secret, secret_len, label, 1, chunks);
+       check_equals("TLS PRF KAT 1", out, ref, ref_len);
+
+       chunks[0].data = seed;
+       chunks[0].len = seed_len;
+       chunks[1].data = NULL;
+       chunks[1].len = 0;
+       prf(out, ref_len, secret, secret_len, label, 2, chunks);
+       check_equals("TLS PRF KAT 2", out, ref, ref_len);
+
+       chunks[0].data = NULL;
+       chunks[0].len = 0;
+       chunks[1].data = seed;
+       chunks[1].len = seed_len;
+       prf(out, ref_len, secret, secret_len, label, 2, chunks);
+       check_equals("TLS PRF KAT 3", out, ref, ref_len);
+
+       chunks[0].data = seed;
+       chunks[0].len = seed_len >> 1;
+       chunks[1].data = seed + chunks[0].len;
+       chunks[1].len = seed_len - chunks[0].len;
+       prf(out, ref_len, secret, secret_len, label, 2, chunks);
+       check_equals("TLS PRF KAT 4", out, ref, ref_len);
 }
 
 static void