Add HPKE seal/open context example

One KEM encapsulation protecting an ordered message sequence via
wc_HpkeInitSealContext()/wc_HpkeContextSealBase() and the open
equivalents, with out-of-order rejection.
pull/610/head
Anthony 2026-07-24 15:18:48 -04:00 committed by Anthony Hu
parent f09fb7378f
commit 96fb285077
3 changed files with 194 additions and 5 deletions

View File

@ -3,14 +3,20 @@ WOLFSSL_INSTALL_DIR=/usr/local
CFLAGS= -I$(WOLFSSL_INSTALL_DIR)/include -Wall
LIBS= -L$(WOLFSSL_INSTALL_DIR)/lib -lwolfssl
all: hpke_test hpke_context
hpke_test: hpke_test.o
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
.PHONY: clean check
hpke_context: hpke_context.o
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
.PHONY: clean check all
clean:
rm -f *.o hpke_test
rm -f *.o hpke_test hpke_context
check: hpke_test
check: all
out=$$(./hpke_test) && printf '%s' "$$out" | grep -q 'HPKE test success'
out=$$(./hpke_context) && printf '%s' "$$out" | grep -q 'HPKE context test success'
@echo "PASS: pk-hpke checks"

View File

@ -1,9 +1,22 @@
# HPKE Example with all supported options
# HPKE Examples
To build wolfSSL for this example run `./configure --enable-hpke --enable-aesgcm --enable-curve25519 --enable-ecc && make && sudo make install`
Demonstrates HPKE (Hybrid Public Key Encryption, RFC 9180): public-key
encryption built from a key encapsulation mechanism (KEM), a key derivation
function (KDF), and an authenticated cipher (AEAD).
To build wolfSSL for these examples run `./configure --enable-hpke --enable-aesgcm --enable-curve25519 --enable-ecc && make && sudo make install`
* `hpke_test.c` - one-shot seal/open (`wc_HpkeSealBase()` / `wc_HpkeOpenBase()`)
with all supported KEM/KDF/AEAD combinations.
* `hpke_context.c` - seal/open contexts (`wc_HpkeInitSealContext()` /
`wc_HpkeContextSealBase()` and the open equivalents): one key encapsulation
protecting an ordered sequence of messages.
```sh
make
./hpke_test
HPKE test success
./hpke_context
...
HPKE context test success
```

View File

@ -0,0 +1,170 @@
/* hpke_context.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL. (formerly known as CyaSSL)
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/* Example of HPKE (RFC 9180) context reuse: one KEM encapsulation protecting
* a sequence of messages.
*
* wc_HpkeSealBase() does a fresh key encapsulation per message. With a
* seal/open context the encapsulation happens once and each message gets the
* next AEAD nonce, so both sides must process messages in the same order. */
#include <stdio.h>
#include <string.h>
#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/hpke.h>
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#if defined(HAVE_HPKE) && (defined(HAVE_ECC) || defined(HAVE_CURVE25519)) && \
defined(HAVE_AESGCM)
#define NUM_MSGS 3
#define TAG_SZ 16
#define MAX_MSG 64
static const char* msgs[NUM_MSGS] = {
"first message",
"second message",
"third message"
};
int main(void)
{
int ret;
int i;
Hpke hpke[1];
HpkeBaseContext sealCtx[1];
HpkeBaseContext openCtx[1];
WC_RNG rng[1];
int rngInit = 0;
void* receiverKey = NULL;
void* ephemeralKey = NULL;
byte pubKey[HPKE_Npk_MAX];
word16 pubKeySz = (word16)sizeof(pubKey);
byte cipher[NUM_MSGS][MAX_MSG + TAG_SZ];
byte plain[MAX_MSG];
const char* info = "hpke context example";
const char* aad = "message aad";
#if defined(HAVE_CURVE25519)
ret = wc_HpkeInit(hpke, DHKEM_X25519_HKDF_SHA256, HKDF_SHA256,
HPKE_AES_128_GCM, NULL);
#else
ret = wc_HpkeInit(hpke, DHKEM_P256_HKDF_SHA256, HKDF_SHA256,
HPKE_AES_128_GCM, NULL);
#endif
if (ret != 0)
goto exit;
ret = wc_InitRng(rng);
if (ret != 0)
goto exit;
rngInit = 1;
ret = wc_HpkeGenerateKeyPair(hpke, &receiverKey, rng);
if (ret == 0)
ret = wc_HpkeGenerateKeyPair(hpke, &ephemeralKey, rng);
if (ret != 0)
goto exit;
/* Sender: one encapsulation, then seal each message in order. */
ret = wc_HpkeInitSealContext(hpke, sealCtx, ephemeralKey, receiverKey,
(byte*)info, (word32)strlen(info));
if (ret != 0)
goto exit;
for (i = 0; i < NUM_MSGS; i++) {
ret = wc_HpkeContextSealBase(hpke, sealCtx, (byte*)aad,
(word32)strlen(aad), (byte*)msgs[i],
(word32)strlen(msgs[i]), cipher[i]);
if (ret != 0)
goto exit;
printf("sealed message %d (%zu bytes)\n", i, strlen(msgs[i]));
}
/* Only the ephemeral public key travels to the receiver. */
ret = wc_HpkeSerializePublicKey(hpke, ephemeralKey, pubKey, &pubKeySz);
if (ret != 0)
goto exit;
/* Receiver: decapsulate once, then open in the same order. */
ret = wc_HpkeInitOpenContext(hpke, openCtx, receiverKey, pubKey, pubKeySz,
(byte*)info, (word32)strlen(info));
if (ret != 0)
goto exit;
for (i = 0; i < NUM_MSGS; i++) {
word32 msgSz = (word32)strlen(msgs[i]);
memset(plain, 0, sizeof(plain));
ret = wc_HpkeContextOpenBase(hpke, openCtx, (byte*)aad,
(word32)strlen(aad), cipher[i], msgSz,
plain);
if (ret != 0)
goto exit;
if (memcmp(plain, msgs[i], msgSz) != 0) {
printf("message %d mismatch\n", i);
ret = -1;
goto exit;
}
printf("opened message %d: %.*s\n", i, (int)msgSz, plain);
}
/* Replaying a message out of sequence uses the wrong nonce and fails. */
ret = wc_HpkeContextOpenBase(hpke, openCtx, (byte*)aad,
(word32)strlen(aad), cipher[0],
(word32)strlen(msgs[0]), plain);
if (ret == 0) {
printf("out-of-order open succeeded unexpectedly\n");
ret = -1;
goto exit;
}
printf("out-of-order open rejected as expected\n");
ret = 0;
printf("HPKE context test success\n");
exit:
if (ret != 0)
printf("HPKE context test error %d: %s\n", ret,
wc_GetErrorString(ret));
if (ephemeralKey != NULL)
wc_HpkeFreeKey(hpke, hpke->kem, ephemeralKey, NULL);
if (receiverKey != NULL)
wc_HpkeFreeKey(hpke, hpke->kem, receiverKey, NULL);
if (rngInit)
wc_FreeRng(rng);
return ret == 0 ? 0 : 1;
}
#else
int main(void)
{
printf("Please build wolfssl with ./configure --enable-hpke "
"--enable-aesgcm --enable-curve25519 --enable-ecc\n");
return 0;
}
#endif