summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Farrell <stephen.farrell@cs.tcd.ie>2022-12-07 21:36:46 +0000
committerTomas Mraz <tomas@openssl.org>2022-12-08 10:59:03 +0100
commitcae72eefc3fbdd2f7a1a065f237bf3943619bca2 (patch)
treecba00a2de71c008076d5ed7f3afaecd370422444
parentfc93335760686ad7cf3633d457caf18b0ac83ea2 (diff)
downloadopenssl-new-cae72eefc3fbdd2f7a1a065f237bf3943619bca2.tar.gz
prevent HPKE sender setting seq unwisely
Reviewed-by: Shane Lontis <shane.lontis@oracle.com> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/19840)
-rw-r--r--crypto/hpke/hpke.c45
-rw-r--r--doc/man3/OSSL_HPKE_CTX_new.pod75
-rw-r--r--include/openssl/hpke.h9
-rw-r--r--test/hpke_test.c120
4 files changed, 199 insertions, 50 deletions
diff --git a/crypto/hpke/hpke.c b/crypto/hpke/hpke.c
index a106366a75..12a12ad8fc 100644
--- a/crypto/hpke/hpke.c
+++ b/crypto/hpke/hpke.c
@@ -56,6 +56,7 @@ struct ossl_hpke_ctx_st
const OSSL_HPKE_KDF_INFO *kdf_info;
const OSSL_HPKE_AEAD_INFO *aead_info;
EVP_CIPHER *aead_ciph;
+ int role; /* sender(0) or receiver(1) */
uint64_t seq; /* aead sequence number */
unsigned char *shared_secret; /* KEM output, zz */
size_t shared_secretlen;
@@ -801,7 +802,7 @@ err:
* in doc/man3/OSSL_HPKE_CTX_new.pod to avoid duplication
*/
-OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite,
+OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite, int role,
OSSL_LIB_CTX *libctx, const char *propq)
{
OSSL_HPKE_CTX *ctx = NULL;
@@ -817,6 +818,10 @@ OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite,
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
return NULL;
}
+ if (role != OSSL_HPKE_ROLE_SENDER && role != OSSL_HPKE_ROLE_RECEIVER) {
+ ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
ctx = OPENSSL_zalloc(sizeof(*ctx));
if (ctx == NULL)
return NULL;
@@ -833,6 +838,7 @@ OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite,
goto err;
}
}
+ ctx->role = role;
ctx->mode = mode;
ctx->suite = suite;
ctx->kem_info = kem_info;
@@ -915,6 +921,10 @@ int OSSL_HPKE_CTX_set1_ikme(OSSL_HPKE_CTX *ctx,
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
}
+ if (ctx->role != OSSL_HPKE_ROLE_SENDER) {
+ ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
OPENSSL_clear_free(ctx->ikme, ctx->ikmelen);
ctx->ikme = OPENSSL_memdup(ikme, ikmelen);
if (ctx->ikme == NULL)
@@ -934,6 +944,10 @@ int OSSL_HPKE_CTX_set1_authpriv(OSSL_HPKE_CTX *ctx, EVP_PKEY *priv)
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
}
+ if (ctx->role != OSSL_HPKE_ROLE_SENDER) {
+ ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
EVP_PKEY_free(ctx->authpriv);
ctx->authpriv = EVP_PKEY_dup(priv);
if (ctx->authpriv == NULL)
@@ -959,6 +973,10 @@ int OSSL_HPKE_CTX_set1_authpub(OSSL_HPKE_CTX *ctx,
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
}
+ if (ctx->role != OSSL_HPKE_ROLE_RECEIVER) {
+ ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
/* check the value seems like a good public key for this kem */
kem_info = ossl_HPKE_KEM_INFO_find_id(ctx->suite.kem_id);
if (kem_info == NULL)
@@ -1020,6 +1038,15 @@ int OSSL_HPKE_CTX_set_seq(OSSL_HPKE_CTX *ctx, uint64_t seq)
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ /*
+ * We disallow senders from doing this as it's dangerous
+ * Receivers are ok to use this, as no harm should ensue
+ * if they go wrong.
+ */
+ if (ctx->role == OSSL_HPKE_ROLE_SENDER) {
+ ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
ctx->seq = seq;
return 1;
}
@@ -1036,6 +1063,10 @@ int OSSL_HPKE_encap(OSSL_HPKE_CTX *ctx,
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ if (ctx->role != OSSL_HPKE_ROLE_SENDER) {
+ ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
if (infolen > OSSL_HPKE_MAX_INFOLEN) {
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
@@ -1069,6 +1100,10 @@ int OSSL_HPKE_decap(OSSL_HPKE_CTX *ctx,
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ if (ctx->role != OSSL_HPKE_ROLE_RECEIVER) {
+ ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
if (infolen > OSSL_HPKE_MAX_INFOLEN) {
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
@@ -1105,6 +1140,10 @@ int OSSL_HPKE_seal(OSSL_HPKE_CTX *ctx,
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ if (ctx->role != OSSL_HPKE_ROLE_SENDER) {
+ ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
if ((ctx->seq + 1) == 0) { /* wrap around imminent !!! */
ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
@@ -1143,6 +1182,10 @@ int OSSL_HPKE_open(OSSL_HPKE_CTX *ctx,
ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ if (ctx->role != OSSL_HPKE_ROLE_RECEIVER) {
+ ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
if ((ctx->seq + 1) == 0) { /* wrap around imminent !!! */
ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
diff --git a/doc/man3/OSSL_HPKE_CTX_new.pod b/doc/man3/OSSL_HPKE_CTX_new.pod
index 1e0b118e84..50ad2895d8 100644
--- a/doc/man3/OSSL_HPKE_CTX_new.pod
+++ b/doc/man3/OSSL_HPKE_CTX_new.pod
@@ -24,7 +24,7 @@ OSSL_HPKE_CTX_get_seq, OSSL_HPKE_CTX_set_seq
uint16_t aead_id;
} OSSL_HPKE_SUITE;
- OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite,
+ OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite, int role,
OSSL_LIB_CTX *libctx, const char *propq);
void OSSL_HPKE_CTX_free(OSSL_HPKE_CTX *ctx);
@@ -182,8 +182,35 @@ supplied before the encapsulation/decapsulation operation will work.
=back
-For further information related to authentication see L</Pre-Shared Key HPKE modes>
-and L</Sender-authenticated HPKE Modes>.
+For further information related to authentication see L</Pre-Shared Key HPKE
+modes> and L</Sender-authenticated HPKE Modes>.
+
+=head2 HPKE Roles
+
+HPKE contexts have a role - either sender or receiver. This is used
+to control which functions can be called and so that senders do not
+re-use a key and nonce with different plaintexts.
+
+OSSL_HPKE_CTX_free(), OSSL_HPKE_export(), OSSL_HPKE_CTX_set1_psk(),
+and OSSL_HPKE_CTX_get_seq() can be called regardless of role.
+
+=over 4
+
+=item B<OSSL_HPKE_ROLE_SENDER>, 0
+
+An I<OSSL_HPKE_CTX> with this role can be used with
+OSSL_HPKE_encap(), OSSL_HPKE_seal(), OSSL_HPKE_CTX_set1_ikme() and
+OSSL_HPKE_CTX_set1_authpriv().
+
+=item B<OSSL_HPKE_ROLE_RECEIVER>, 1
+
+An I<OSSL_HPKE_CTX> with this role can be used with OSSL_HPKE_decap(),
+OSSL_HPKE_open(), OSSL_HPKE_CTX_set1_authpub() and OSSL_HPKE_CTX_set_seq().
+
+=back
+
+Calling a function with an incorrect role set on I<OSSL_HPKE_CTX> will result
+in an error.
=head2 Parameter Size Limits
@@ -202,13 +229,14 @@ for the I<infolen> parameter.
=head2 Context Construct/Free
-OSSL_HPKE_CTX_new() creates a B<OSSL_HPKE_CTX> context object used for subsequent
-HPKE operations, given a I<mode> (See L</HPKE Modes>) and
-I<suite> (see L</OSSL_HPKE_SUITE Identifiers>). The I<libctx> and I<propq>
-are used when fetching algorithms from providers and may be set to NULL.
+OSSL_HPKE_CTX_new() creates a B<OSSL_HPKE_CTX> context object used for
+subsequent HPKE operations, given a I<mode> (See L</HPKE Modes>), I<suite> (see
+L</OSSL_HPKE_SUITE Identifiers>) and a I<role> (see L</HPKE Roles>). The
+I<libctx> and I<propq> are used when fetching algorithms from providers and may
+be set to NULL.
-OSSL_HPKE_CTX_free() frees the I<ctx> B<OSSL_HPKE_CTX> that was created previously
-by a call to OSSL_HPKE_CTX_new().
+OSSL_HPKE_CTX_free() frees the I<ctx> B<OSSL_HPKE_CTX> that was created
+previously by a call to OSSL_HPKE_CTX_new().
=head2 Sender APIs
@@ -363,13 +391,14 @@ or values that leak.
Some protocols may have to deal with packet loss while still being able to
decrypt arriving packets later. We provide a way to set the increment used for
-the nonce to the next subsequent call to OSSL_HPKE_seal() or OSSL_HPKE_open().
-The OSSL_HPKE_CTX_set_seq() API can be used for such purposes with the I<seq>
-parameter value resetting the internal nonce to be used for the next call.
+the nonce to the next subsequent call to OSSL_HPKE_open() (but not to
+OSSL_HPKE_seal() as explained below). The OSSL_HPKE_CTX_set_seq() API can be
+used for such purposes with the I<seq> parameter value resetting the internal
+nonce increment to be used for the next call.
A baseline nonce value is established based on the encapsulation or
decapsulation operation and is then incremented by 1 for each call to seal or
-open. (In other words, the I<seq> is a zero-based counter.)
+open. (In other words, the first I<seq> increment defaults to zero.)
If a caller needs to determine how many calls to seal or open have been made
the OSSL_HPKE_CTX_get_seq() API can be used to retrieve the increment (in the
@@ -377,18 +406,18 @@ I<seq> output) that will be used in the next call to seal or open. That would
return 0 before the first call a sender made to OSSL_HPKE_seal() and 1 after
that first call.
+Note that re-use of the same nonce and key with different plaintexts would
+be very dangerous and could lead to loss of confidentiality and integrity.
+We therefore only support application control over I<seq> for decryption
+(i.e. OSSL_HPKE_open()) operations.
+
For compatibility with other implementations these I<seq> increments are
represented as I<uint64_t>.
-Note that re-use of the same nonce and key with different plaintexts is very
-dangerous and can lead to loss of confidentiality. Applications therefore need
-to exercise extreme caution in using these APIs and would be better off avoiding
-them entirely.
-
=head2 Protocol Convenience Functions
Additional convenience APIs allow the caller to access internal details of
-local HPKE support and/or algorithms, such as parmameter lengths.
+local HPKE support and/or algorithms, such as parameter lengths.
OSSL_HPKE_suite_check() checks if a specific B<OSSL_HPKE_SUITE> I<suite>
is supported locally.
@@ -483,7 +512,9 @@ This example demonstrates a minimal round-trip using HPKE.
goto err;
/* sender's actions - encrypt data using the receivers public key */
- if ((sctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite, NULL, NULL)) == NULL)
+ if ((sctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_SENDER,
+ NULL, NULL)) == NULL)
goto err;
if (OSSL_HPKE_encap(sctx, enc, &enclen, pub, publen, info, infolen) != 1)
goto err;
@@ -491,7 +522,9 @@ This example demonstrates a minimal round-trip using HPKE.
goto err;
/* receiver's actions - decrypt data using the recievers private key */
- if ((rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite, NULL, NULL)) == NULL)
+ if ((rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_RECEIVER,
+ NULL, NULL)) == NULL)
goto err;
if (OSSL_HPKE_decap(rctx, enc, enclen, priv, info, infolen) != 1)
goto err;
diff --git a/include/openssl/hpke.h b/include/openssl/hpke.h
index d38c3e3703..7b1419e66c 100644
--- a/include/openssl/hpke.h
+++ b/include/openssl/hpke.h
@@ -65,6 +65,13 @@
# define OSSL_HPKE_AEADSTR_CP "chacha20-poly1305" /* AEAD id 3 */
# define OSSL_HPKE_AEADSTR_EXP "exporter" /* AEAD id 0xff */
+/*
+ * Roles for use in creating an OSSL_HPKE_CTX, most
+ * important use of this is to control nonce re-use.
+ */
+# define OSSL_HPKE_ROLE_SENDER 0
+# define OSSL_HPKE_ROLE_RECEIVER 1
+
typedef struct {
uint16_t kem_id; /* Key Encapsulation Method id */
uint16_t kdf_id; /* Key Derivation Function id */
@@ -84,7 +91,7 @@ typedef struct {
typedef struct ossl_hpke_ctx_st OSSL_HPKE_CTX;
-OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite,
+OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite, int role,
OSSL_LIB_CTX *libctx, const char *propq);
void OSSL_HPKE_CTX_free(OSSL_HPKE_CTX *ctx);
diff --git a/test/hpke_test.c b/test/hpke_test.c
index 0a718869d2..fc2f80857f 100644
--- a/test/hpke_test.c
+++ b/test/hpke_test.c
@@ -123,6 +123,7 @@ static int do_testhpke(const TEST_BASEDATA *base,
if (!TEST_true(cmpkey(privE, base->expected_pkEm, base->expected_pkEmlen)))
goto end;
if (!TEST_ptr(sealctx = OSSL_HPKE_CTX_new(base->mode, base->suite,
+ OSSL_HPKE_ROLE_SENDER,
libctx, propq)))
goto end;
if (!TEST_true(OSSL_HPKE_CTX_set1_ikme(sealctx, base->ikmE, base->ikmElen)))
@@ -172,6 +173,7 @@ static int do_testhpke(const TEST_BASEDATA *base,
goto end;
}
if (!TEST_ptr(openctx = OSSL_HPKE_CTX_new(base->mode, base->suite,
+ OSSL_HPKE_ROLE_RECEIVER,
libctx, propq)))
goto end;
if (base->mode == OSSL_HPKE_MODE_PSK
@@ -913,7 +915,6 @@ static int test_hpke_modes_suites(void)
OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
size_t plainlen = OSSL_HPKE_TSTSIZE;
unsigned char plain[OSSL_HPKE_TSTSIZE];
- uint64_t startseq = 0;
OSSL_HPKE_CTX *rctx = NULL;
OSSL_HPKE_CTX *ctx = NULL;
@@ -995,6 +996,7 @@ static int test_hpke_modes_suites(void)
NULL, 0, testctx, NULL)))
overallresult = 0;
if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_SENDER,
testctx, NULL)))
overallresult = 0;
if (hpke_mode == OSSL_HPKE_MODE_PSK
@@ -1009,15 +1011,6 @@ static int test_hpke_modes_suites(void)
authpriv)))
overallresult = 0;
}
- if (COIN_IS_HEADS) {
- if (!TEST_int_eq(1, RAND_bytes_ex(testctx,
- (unsigned char *) &startseq,
- sizeof(startseq), 0))
- || !TEST_true(OSSL_HPKE_CTX_set_seq(ctx, startseq)))
- overallresult = 0;
- } else {
- startseq = 0;
- }
if (!TEST_true(OSSL_HPKE_encap(ctx, senderpub,
&senderpublen,
pub, publen,
@@ -1037,9 +1030,10 @@ static int test_hpke_modes_suites(void)
overallresult = 0;
OSSL_HPKE_CTX_free(ctx);
memset(clear, 0, clearlen);
- if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode,
- hpke_suite,
- testctx, NULL)))
+ rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_RECEIVER,
+ testctx, NULL);
+ if (!TEST_ptr(rctx))
overallresult = 0;
if (hpke_mode == OSSL_HPKE_MODE_PSK
|| hpke_mode == OSSL_HPKE_MODE_PSKAUTH) {
@@ -1063,10 +1057,6 @@ static int test_hpke_modes_suites(void)
authpublen)))
overallresult = 0;
}
- if (startseq != 0) {
- if (!TEST_true(OSSL_HPKE_CTX_set_seq(rctx, startseq)))
- overallresult = 0;
- }
if (!TEST_true(OSSL_HPKE_decap(rctx, senderpub,
senderpublen, privp,
infop, infolen)))
@@ -1142,6 +1132,7 @@ static int test_hpke_export(void)
NULL, 0, testctx, NULL)))
goto end;
if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_SENDER,
testctx, NULL)))
goto end;
/* a few error cases 1st */
@@ -1168,6 +1159,7 @@ static int test_hpke_export(void)
if (!TEST_mem_eq(exp, sizeof(exp), exp2, sizeof(exp2)))
goto end;
if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_RECEIVER,
testctx, NULL)))
goto end;
if (!TEST_true(OSSL_HPKE_decap(rctx, enc, enclen, privp, NULL, 0)))
@@ -1403,6 +1395,7 @@ static int test_hpke_oddcalls(void)
/* a psk context with no psk => encap fail */
if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_PSK, hpke_suite,
+ OSSL_HPKE_ROLE_SENDER,
testctx, NULL)))
goto end;
/* set bad length psk */
@@ -1422,14 +1415,17 @@ static int test_hpke_oddcalls(void)
/* bad suite */
if (!TEST_ptr_null(ctx = OSSL_HPKE_CTX_new(hpke_mode, bad_suite,
+ OSSL_HPKE_ROLE_SENDER,
testctx, NULL)))
goto end;
/* bad mode */
if (!TEST_ptr_null(ctx = OSSL_HPKE_CTX_new(bad_mode, hpke_suite,
+ OSSL_HPKE_ROLE_SENDER,
testctx, NULL)))
goto end;
/* make good ctx */
if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_SENDER,
testctx, NULL)))
goto end;
/* too long ikm */
@@ -1472,17 +1468,7 @@ static int test_hpke_oddcalls(void)
if (!TEST_false(OSSL_HPKE_seal(ctx, cipher, &cipherlen, NULL, 0,
plain, plainlen)))
goto end;
- /* the sequence ought not have been incremented, so good to start over */
plainlen = sizeof(plain);
- /* seq wrap around test */
- if (!TEST_true(OSSL_HPKE_CTX_set_seq(ctx, -1)))
- goto end;
- if (!TEST_false(OSSL_HPKE_seal(ctx, cipher, &cipherlen, NULL, 0,
- plain, plainlen)))
- goto end;
- /* reset seq */
- if (!TEST_true(OSSL_HPKE_CTX_set_seq(ctx, 0)))
- goto end;
/* working seal */
if (!TEST_true(OSSL_HPKE_seal(ctx, cipher, &cipherlen, NULL, 0,
plain, plainlen)))
@@ -1491,6 +1477,7 @@ static int test_hpke_oddcalls(void)
/* receiver side */
/* decap fail with psk mode but no psk set */
if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_PSK, hpke_suite,
+ OSSL_HPKE_ROLE_RECEIVER,
testctx, NULL)))
goto end;
if (!TEST_false(OSSL_HPKE_decap(rctx, enc, enclen, privp, NULL, 0)))
@@ -1500,6 +1487,7 @@ static int test_hpke_oddcalls(void)
/* back good calls for base mode */
if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_RECEIVER,
testctx, NULL)))
goto end;
/* open before decap */
@@ -1815,6 +1803,7 @@ static int test_hpke_compressed(void)
NULL, 0, testctx, NULL)))
goto end;
if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_SENDER,
testctx, NULL)))
goto end;
if (!TEST_true(OSSL_HPKE_CTX_set1_authpriv(ctx, authpriv)))
@@ -1827,6 +1816,7 @@ static int test_hpke_compressed(void)
/* receiver side providing compressed form of auth public */
if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_RECEIVER,
testctx, NULL)))
goto end;
if (!TEST_true(OSSL_HPKE_CTX_set1_authpub(rctx, authpub, authpublen)))
@@ -1846,6 +1836,81 @@ end:
return erv;
}
+/*
+ * Test that nonce reuse calls are prevented as we expect
+ */
+static int test_hpke_noncereuse(void)
+{
+ int erv = 0;
+ EVP_PKEY *privp = NULL;
+ unsigned char pub[OSSL_HPKE_TSTSIZE];
+ size_t publen = sizeof(pub);
+ int hpke_mode = OSSL_HPKE_MODE_BASE;
+ OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
+ OSSL_HPKE_CTX *ctx = NULL;
+ OSSL_HPKE_CTX *rctx = NULL;
+ unsigned char plain[] = "quick brown fox";
+ size_t plainlen = sizeof(plain);
+ unsigned char enc[OSSL_HPKE_TSTSIZE];
+ size_t enclen = sizeof(enc);
+ unsigned char cipher[OSSL_HPKE_TSTSIZE];
+ size_t cipherlen = sizeof(cipher);
+ unsigned char clear[OSSL_HPKE_TSTSIZE];
+ size_t clearlen = sizeof(clear);
+ uint64_t seq = 0xbad1dea;
+
+ /* sender side is not allowed set seq once some crypto done */
+ if (!TEST_true(OSSL_HPKE_keygen(hpke_suite, pub, &publen, &privp,
+ NULL, 0, testctx, NULL)))
+ goto end;
+ if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_SENDER,
+ testctx, NULL)))
+ goto end;
+ /* set seq will fail before any crypto done */
+ if (!TEST_false(OSSL_HPKE_CTX_set_seq(ctx, seq)))
+ goto end;
+ if (!TEST_true(OSSL_HPKE_encap(ctx, enc, &enclen, pub, publen, NULL, 0)))
+ goto end;
+ /* set seq will also fail after some crypto done */
+ if (!TEST_false(OSSL_HPKE_CTX_set_seq(ctx, seq + 1)))
+ goto end;
+ if (!TEST_true(OSSL_HPKE_seal(ctx, cipher, &cipherlen, NULL, 0,
+ plain, plainlen)))
+ goto end;
+
+ /* receiver side is allowed control seq */
+ if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_RECEIVER,
+ testctx, NULL)))
+ goto end;
+ /* set seq will work before any crypto done */
+ if (!TEST_true(OSSL_HPKE_CTX_set_seq(rctx, seq)))
+ goto end;
+ if (!TEST_true(OSSL_HPKE_decap(rctx, enc, enclen, privp, NULL, 0)))
+ goto end;
+ /* set seq will work for receivers even after crypto done */
+ if (!TEST_true(OSSL_HPKE_CTX_set_seq(rctx, seq)))
+ goto end;
+ /* but that value isn't good so decap will fail */
+ if (!TEST_false(OSSL_HPKE_open(rctx, clear, &clearlen, NULL, 0,
+ cipher, cipherlen)))
+ goto end;
+ /* reset seq to correct value and _open() should work */
+ if (!TEST_true(OSSL_HPKE_CTX_set_seq(rctx, 0)))
+ goto end;
+ if (!TEST_true(OSSL_HPKE_open(rctx, clear, &clearlen, NULL, 0,
+ cipher, cipherlen)))
+ goto end;
+ erv = 1;
+
+end:
+ EVP_PKEY_free(privp);
+ OSSL_HPKE_CTX_free(ctx);
+ OSSL_HPKE_CTX_free(rctx);
+ return erv;
+}
+
typedef enum OPTION_choice {
OPT_ERR = -1,
OPT_EOF = 0,
@@ -1894,6 +1959,7 @@ int setup_tests(void)
ADD_TEST(test_hpke_random_suites);
ADD_TEST(test_hpke_oddcalls);
ADD_TEST(test_hpke_compressed);
+ ADD_TEST(test_hpke_noncereuse);
return 1;
}