Browse Source

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)
Stephen Farrell 1 year ago
parent
commit
cae72eefc3
4 changed files with 199 additions and 50 deletions
  1. 44 1
      crypto/hpke/hpke.c
  2. 54 21
      doc/man3/OSSL_HPKE_CTX_new.pod
  3. 8 1
      include/openssl/hpke.h
  4. 93 27
      test/hpke_test.c

+ 44 - 1
crypto/hpke/hpke.c

@@ -56,6 +56,7 @@ struct ossl_hpke_ctx_st
     const OSSL_HPKE_KDF_INFO *kdf_info;
     const OSSL_HPKE_KDF_INFO *kdf_info;
     const OSSL_HPKE_AEAD_INFO *aead_info;
     const OSSL_HPKE_AEAD_INFO *aead_info;
     EVP_CIPHER *aead_ciph;
     EVP_CIPHER *aead_ciph;
+    int role; /* sender(0) or receiver(1) */
     uint64_t seq; /* aead sequence number */
     uint64_t seq; /* aead sequence number */
     unsigned char *shared_secret; /* KEM output, zz */
     unsigned char *shared_secret; /* KEM output, zz */
     size_t shared_secretlen;
     size_t shared_secretlen;
@@ -801,7 +802,7 @@ err:
  * in doc/man3/OSSL_HPKE_CTX_new.pod to avoid duplication
  * 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_LIB_CTX *libctx, const char *propq)
 {
 {
     OSSL_HPKE_CTX *ctx = NULL;
     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);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
         return NULL;
         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));
     ctx = OPENSSL_zalloc(sizeof(*ctx));
     if (ctx == NULL)
     if (ctx == NULL)
         return NULL;
         return NULL;
@@ -833,6 +838,7 @@ OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite,
             goto err;
             goto err;
         }
         }
     }
     }
+    ctx->role = role;
     ctx->mode = mode;
     ctx->mode = mode;
     ctx->suite = suite;
     ctx->suite = suite;
     ctx->kem_info = kem_info;
     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);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
         return 0;
         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);
     OPENSSL_clear_free(ctx->ikme, ctx->ikmelen);
     ctx->ikme = OPENSSL_memdup(ikme, ikmelen);
     ctx->ikme = OPENSSL_memdup(ikme, ikmelen);
     if (ctx->ikme == NULL)
     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);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
         return 0;
         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);
     EVP_PKEY_free(ctx->authpriv);
     ctx->authpriv = EVP_PKEY_dup(priv);
     ctx->authpriv = EVP_PKEY_dup(priv);
     if (ctx->authpriv == NULL)
     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);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
         return 0;
         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 */
     /* check the value seems like a good public key for this kem */
     kem_info = ossl_HPKE_KEM_INFO_find_id(ctx->suite.kem_id);
     kem_info = ossl_HPKE_KEM_INFO_find_id(ctx->suite.kem_id);
     if (kem_info == NULL)
     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);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
         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;
     ctx->seq = seq;
     return 1;
     return 1;
 }
 }
@@ -1036,6 +1063,10 @@ int OSSL_HPKE_encap(OSSL_HPKE_CTX *ctx,
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
         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) {
     if (infolen > OSSL_HPKE_MAX_INFOLEN) {
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
         return 0;
         return 0;
@@ -1069,6 +1100,10 @@ int OSSL_HPKE_decap(OSSL_HPKE_CTX *ctx,
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
         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) {
     if (infolen > OSSL_HPKE_MAX_INFOLEN) {
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
         return 0;
         return 0;
@@ -1105,6 +1140,10 @@ int OSSL_HPKE_seal(OSSL_HPKE_CTX *ctx,
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
         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 !!! */
     if ((ctx->seq + 1) == 0) { /* wrap around imminent !!! */
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
         return 0;
@@ -1143,6 +1182,10 @@ int OSSL_HPKE_open(OSSL_HPKE_CTX *ctx,
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
         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 !!! */
     if ((ctx->seq + 1) == 0) { /* wrap around imminent !!! */
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
         return 0;

+ 54 - 21
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;
      uint16_t    aead_id;
  } OSSL_HPKE_SUITE;
  } 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);
                                   OSSL_LIB_CTX *libctx, const char *propq);
  void OSSL_HPKE_CTX_free(OSSL_HPKE_CTX *ctx);
  void OSSL_HPKE_CTX_free(OSSL_HPKE_CTX *ctx);
 
 
@@ -182,8 +182,35 @@ supplied before the encapsulation/decapsulation operation will work.
 
 
 =back
 =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
 =head2 Parameter Size Limits
 
 
@@ -202,13 +229,14 @@ for the I<infolen> parameter.
 
 
 =head2 Context Construct/Free
 =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
 =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
 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
 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
 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
 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
 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
 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
 return 0 before the first call a sender made to OSSL_HPKE_seal() and 1 after
 that first call.
 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
 For compatibility with other implementations these I<seq> increments are
 represented as I<uint64_t>.
 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
 =head2 Protocol Convenience Functions
 
 
 Additional convenience APIs allow the caller to access internal details of
 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>
 OSSL_HPKE_suite_check() checks if a specific B<OSSL_HPKE_SUITE> I<suite>
 is supported locally.
 is supported locally.
@@ -483,7 +512,9 @@ This example demonstrates a minimal round-trip using HPKE.
             goto err;
             goto err;
 
 
         /* sender's actions - encrypt data using the receivers public key */
         /* 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;
             goto err;
         if (OSSL_HPKE_encap(sctx, enc, &enclen, pub, publen, info, infolen) != 1)
         if (OSSL_HPKE_encap(sctx, enc, &enclen, pub, publen, info, infolen) != 1)
             goto err;
             goto err;
@@ -491,7 +522,9 @@ This example demonstrates a minimal round-trip using HPKE.
             goto err;
             goto err;
 
 
         /* receiver's actions - decrypt data using the recievers private key */
         /* 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;
             goto err;
         if (OSSL_HPKE_decap(rctx, enc, enclen, priv, info, infolen) != 1)
         if (OSSL_HPKE_decap(rctx, enc, enclen, priv, info, infolen) != 1)
             goto err;
             goto err;

+ 8 - 1
include/openssl/hpke.h

@@ -65,6 +65,13 @@
 # define OSSL_HPKE_AEADSTR_CP         "chacha20-poly1305"  /* AEAD id 3 */
 # define OSSL_HPKE_AEADSTR_CP         "chacha20-poly1305"  /* AEAD id 3 */
 # define OSSL_HPKE_AEADSTR_EXP        "exporter"           /* AEAD id 0xff */
 # 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 {
 typedef struct {
     uint16_t    kem_id; /* Key Encapsulation Method id */
     uint16_t    kem_id; /* Key Encapsulation Method id */
     uint16_t    kdf_id; /* Key Derivation Function id */
     uint16_t    kdf_id; /* Key Derivation Function id */
@@ -84,7 +91,7 @@ typedef struct {
 
 
 typedef struct ossl_hpke_ctx_st OSSL_HPKE_CTX;
 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);
                                  OSSL_LIB_CTX *libctx, const char *propq);
 void OSSL_HPKE_CTX_free(OSSL_HPKE_CTX *ctx);
 void OSSL_HPKE_CTX_free(OSSL_HPKE_CTX *ctx);
 
 

+ 93 - 27
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)))
     if (!TEST_true(cmpkey(privE, base->expected_pkEm, base->expected_pkEmlen)))
         goto end;
         goto end;
     if (!TEST_ptr(sealctx = OSSL_HPKE_CTX_new(base->mode, base->suite,
     if (!TEST_ptr(sealctx = OSSL_HPKE_CTX_new(base->mode, base->suite,
+                                              OSSL_HPKE_ROLE_SENDER,
                                               libctx, propq)))
                                               libctx, propq)))
         goto end;
         goto end;
     if (!TEST_true(OSSL_HPKE_CTX_set1_ikme(sealctx, base->ikmE, base->ikmElen)))
     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;
             goto end;
     }
     }
     if (!TEST_ptr(openctx = OSSL_HPKE_CTX_new(base->mode, base->suite,
     if (!TEST_ptr(openctx = OSSL_HPKE_CTX_new(base->mode, base->suite,
+                                              OSSL_HPKE_ROLE_RECEIVER,
                                               libctx, propq)))
                                               libctx, propq)))
         goto end;
         goto end;
     if (base->mode == OSSL_HPKE_MODE_PSK
     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;
         OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
         size_t plainlen = OSSL_HPKE_TSTSIZE;
         size_t plainlen = OSSL_HPKE_TSTSIZE;
         unsigned char plain[OSSL_HPKE_TSTSIZE];
         unsigned char plain[OSSL_HPKE_TSTSIZE];
-        uint64_t startseq = 0;
         OSSL_HPKE_CTX *rctx = NULL;
         OSSL_HPKE_CTX *rctx = NULL;
         OSSL_HPKE_CTX *ctx = NULL;
         OSSL_HPKE_CTX *ctx = NULL;
 
 
@@ -995,6 +996,7 @@ static int test_hpke_modes_suites(void)
                                                     NULL, 0, testctx, NULL)))
                                                     NULL, 0, testctx, NULL)))
                         overallresult = 0;
                         overallresult = 0;
                     if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
                     if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+                                                          OSSL_HPKE_ROLE_SENDER,
                                                           testctx, NULL)))
                                                           testctx, NULL)))
                         overallresult = 0;
                         overallresult = 0;
                     if (hpke_mode == OSSL_HPKE_MODE_PSK
                     if (hpke_mode == OSSL_HPKE_MODE_PSK
@@ -1009,15 +1011,6 @@ static int test_hpke_modes_suites(void)
                                                                    authpriv)))
                                                                    authpriv)))
                             overallresult = 0;
                             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,
                     if (!TEST_true(OSSL_HPKE_encap(ctx, senderpub,
                                                    &senderpublen,
                                                    &senderpublen,
                                                    pub, publen,
                                                    pub, publen,
@@ -1037,9 +1030,10 @@ static int test_hpke_modes_suites(void)
                         overallresult = 0;
                         overallresult = 0;
                     OSSL_HPKE_CTX_free(ctx);
                     OSSL_HPKE_CTX_free(ctx);
                     memset(clear, 0, clearlen);
                     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;
                         overallresult = 0;
                     if (hpke_mode == OSSL_HPKE_MODE_PSK
                     if (hpke_mode == OSSL_HPKE_MODE_PSK
                         || hpke_mode == OSSL_HPKE_MODE_PSKAUTH) {
                         || hpke_mode == OSSL_HPKE_MODE_PSKAUTH) {
@@ -1063,10 +1057,6 @@ static int test_hpke_modes_suites(void)
                                                                   authpublen)))
                                                                   authpublen)))
                             overallresult = 0;
                             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,
                     if (!TEST_true(OSSL_HPKE_decap(rctx, senderpub,
                                                    senderpublen, privp,
                                                    senderpublen, privp,
                                                    infop, infolen)))
                                                    infop, infolen)))
@@ -1142,6 +1132,7 @@ static int test_hpke_export(void)
                                     NULL, 0, testctx, NULL)))
                                     NULL, 0, testctx, NULL)))
         goto end;
         goto end;
     if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
     if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+                                          OSSL_HPKE_ROLE_SENDER,
                                           testctx, NULL)))
                                           testctx, NULL)))
         goto end;
         goto end;
     /* a few error cases 1st */
     /* 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)))
     if (!TEST_mem_eq(exp, sizeof(exp), exp2, sizeof(exp2)))
         goto end;
         goto end;
     if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
     if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+                                           OSSL_HPKE_ROLE_RECEIVER,
                                            testctx, NULL)))
                                            testctx, NULL)))
         goto end;
         goto end;
     if (!TEST_true(OSSL_HPKE_decap(rctx, enc, enclen, privp, NULL, 0)))
     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 */
     /* a psk context with no psk => encap fail */
     if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_PSK, hpke_suite,
     if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_PSK, hpke_suite,
+                                          OSSL_HPKE_ROLE_SENDER,
                                           testctx, NULL)))
                                           testctx, NULL)))
         goto end;
         goto end;
     /* set bad length psk */
     /* set bad length psk */
@@ -1422,14 +1415,17 @@ static int test_hpke_oddcalls(void)
 
 
     /* bad suite */
     /* bad suite */
     if (!TEST_ptr_null(ctx = OSSL_HPKE_CTX_new(hpke_mode, bad_suite,
     if (!TEST_ptr_null(ctx = OSSL_HPKE_CTX_new(hpke_mode, bad_suite,
+                                               OSSL_HPKE_ROLE_SENDER,
                                                testctx, NULL)))
                                                testctx, NULL)))
         goto end;
         goto end;
     /* bad mode */
     /* bad mode */
     if (!TEST_ptr_null(ctx = OSSL_HPKE_CTX_new(bad_mode, hpke_suite,
     if (!TEST_ptr_null(ctx = OSSL_HPKE_CTX_new(bad_mode, hpke_suite,
+                                               OSSL_HPKE_ROLE_SENDER,
                                                testctx, NULL)))
                                                testctx, NULL)))
         goto end;
         goto end;
     /* make good ctx */
     /* make good ctx */
     if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
     if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+                                          OSSL_HPKE_ROLE_SENDER,
                                           testctx, NULL)))
                                           testctx, NULL)))
         goto end;
         goto end;
     /* too long ikm */
     /* too long ikm */
@@ -1472,17 +1468,7 @@ static int test_hpke_oddcalls(void)
     if (!TEST_false(OSSL_HPKE_seal(ctx, cipher, &cipherlen, NULL, 0,
     if (!TEST_false(OSSL_HPKE_seal(ctx, cipher, &cipherlen, NULL, 0,
                                    plain, plainlen)))
                                    plain, plainlen)))
         goto end;
         goto end;
-    /* the sequence ought not have been incremented, so good to start over */
     plainlen = sizeof(plain);
     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 */
     /* working seal */
     if (!TEST_true(OSSL_HPKE_seal(ctx, cipher, &cipherlen, NULL, 0,
     if (!TEST_true(OSSL_HPKE_seal(ctx, cipher, &cipherlen, NULL, 0,
                                   plain, plainlen)))
                                   plain, plainlen)))
@@ -1491,6 +1477,7 @@ static int test_hpke_oddcalls(void)
     /* receiver side */
     /* receiver side */
     /* decap fail with psk mode but no psk set */
     /* decap fail with psk mode but no psk set */
     if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_PSK, hpke_suite,
     if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_PSK, hpke_suite,
+                                           OSSL_HPKE_ROLE_RECEIVER,
                                            testctx, NULL)))
                                            testctx, NULL)))
         goto end;
         goto end;
     if (!TEST_false(OSSL_HPKE_decap(rctx, enc, enclen, privp, NULL, 0)))
     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  */
     /* back good calls for base mode  */
     if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
     if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+                                           OSSL_HPKE_ROLE_RECEIVER,
                                            testctx, NULL)))
                                            testctx, NULL)))
         goto end;
         goto end;
     /* open before decap */
     /* open before decap */
@@ -1815,6 +1803,7 @@ static int test_hpke_compressed(void)
                                     NULL, 0, testctx, NULL)))
                                     NULL, 0, testctx, NULL)))
         goto end;
         goto end;
     if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
     if (!TEST_ptr(ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+                                          OSSL_HPKE_ROLE_SENDER,
                                           testctx, NULL)))
                                           testctx, NULL)))
         goto end;
         goto end;
     if (!TEST_true(OSSL_HPKE_CTX_set1_authpriv(ctx, authpriv)))
     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 */
     /* receiver side providing compressed form of auth public */
     if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
     if (!TEST_ptr(rctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+                                           OSSL_HPKE_ROLE_RECEIVER,
                                            testctx, NULL)))
                                            testctx, NULL)))
         goto end;
         goto end;
     if (!TEST_true(OSSL_HPKE_CTX_set1_authpub(rctx, authpub, authpublen)))
     if (!TEST_true(OSSL_HPKE_CTX_set1_authpub(rctx, authpub, authpublen)))
@@ -1846,6 +1836,81 @@ end:
     return erv;
     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 {
 typedef enum OPTION_choice {
     OPT_ERR = -1,
     OPT_ERR = -1,
     OPT_EOF = 0,
     OPT_EOF = 0,
@@ -1894,6 +1959,7 @@ int setup_tests(void)
     ADD_TEST(test_hpke_random_suites);
     ADD_TEST(test_hpke_random_suites);
     ADD_TEST(test_hpke_oddcalls);
     ADD_TEST(test_hpke_oddcalls);
     ADD_TEST(test_hpke_compressed);
     ADD_TEST(test_hpke_compressed);
+    ADD_TEST(test_hpke_noncereuse);
     return 1;
     return 1;
 }
 }