Skip to content

Commit

Permalink
Add an api, implementation and tests for Key Transport
Browse files Browse the repository at this point in the history
  • Loading branch information
pushkarnk committed Nov 27, 2023
1 parent a68b000 commit 554ec9a
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 9 deletions.
10 changes: 8 additions & 2 deletions Makefile
Expand Up @@ -3,11 +3,13 @@ build:
cc -I/usr/local/include/openssl/ -I./include -c -fPIC src/init.c -o build/bin/init.o && \
cc -I/usr/local/include/openssl/ -I./include -c -fPIC src/cipher.c -o build/bin/cipher.o && \
cc -I/usr/local/include/openssl/ -I./include -c -fPIC src/keyagreement.c -o build/bin/keyagreement.o && \
cc -I/usr/local/include/openssl/ -I./include -c -fPIC src/keyencapsulation.c -o build/bin/keyencapsulation.o && \
cc -shared -fPIC -Wl,-soname,libjssl.so -o build/bin/libjssl.so \
build/bin/init.o \
build/bin/drbg.o \
build/bin/cipher.o \
build/bin/keyagreement.o \
build/bin/keyencapsulation.o \
-L/usr/local/lib64 -lcrypto -lssl

test-drbg: build
Expand All @@ -16,10 +18,14 @@ test-drbg: build

test-cipher: build
@mkdir -p build/test && cc -I./include/ -L./build/bin/ -o build/test/cipher_test test/cipher_test.c -ljssl && \
build/test/cipher_test 2>/dev/null
LD_LIBRARY_PATH=./build/bin build/test/cipher_test 2>/dev/null

test-ka: build
@mkdir -p build/test && cc -I./include/ -L./build/bin/ -o build/test/keyagreement test/keyagreement.c -ljssl && \
build/test/keyagreement 2>/dev/null
LD_LIBRARY_PATH=./build/bin build/test/keyagreement 2>/dev/null

test-ke: build
@mkdir -p build/test && cc -I./include/ -L./build/bin/ -L/usr/local/lib64 -o build/test/keyencapsulation test/keyencapsulation.c -ljssl && \
LD_LIBRARY_PATH=./build/bin ./build/test/keyencapsulation 2>/dev/null
clean:
@rm -rf build
24 changes: 24 additions & 0 deletions include/keyencapsulation.h
@@ -0,0 +1,24 @@
#include "jssl.h"
#include <openssl/evp.h>

typedef struct kem_keyspec {
OSSL_LIB_CTX *libctx;
EVP_PKEY *public_key;
EVP_PKEY *private_key;
byte *secret;
size_t secret_length;
byte *wrapped_key;
size_t wrapped_key_length;
} kem_keyspec;

void free_kem_keyspec(kem_keyspec *spec);

kem_keyspec *init_kem_keyspec(OSSL_LIB_CTX *libctx);

kem_keyspec *init_kem_keyspec_with_key(EVP_PKEY *rsa_public_key, EVP_PKEY *rsa_private_key, OSSL_LIB_CTX *libctx);

int generate_and_wrap(kem_keyspec *spec);

int unwrap(kem_keyspec *spec);

void free_kem_keyspec(kem_keyspec *spec);
31 changes: 24 additions & 7 deletions src/init.c
Expand Up @@ -3,17 +3,34 @@
#include <openssl/err.h>
#include <stdio.h>

OSSL_LIB_CTX* load_openssl_fips_provider(const char* conf_file_path) {
OSSL_LIB_CTX *fips_libctx = OSSL_LIB_CTX_new();
if (!OSSL_LIB_CTX_load_config(fips_libctx, conf_file_path)) {
/* Loading the FIPS provider is often not enough to get openssl's full functionality.
We also should load the base provider. The base provider does not provide for
any crypto functionality, but has other functionality like the encoders for example.
These two comments saved my day:
https://github.com/openssl/openssl/issues/13773#issuecomment-756225529
https://github.com/openssl/openssl/issues/13773#issuecomment-756233808
*/

OSSL_LIB_CTX* load_openssl_provider(const char *name, const char* conf_file_path) {
OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new();
if (!OSSL_LIB_CTX_load_config(libctx, conf_file_path)) {
ERR_print_errors_fp(stderr);
}

OSSL_PROVIDER *fips = OSSL_PROVIDER_load(NULL, "fips");
if (NULL == fips) {
fprintf(stderr, "Failed to load the FIPS provider:\n");
OSSL_PROVIDER *prov = OSSL_PROVIDER_load(NULL, name);
if (NULL == prov) {
fprintf(stderr, "Failed to load the %s provider:\n", name);
ERR_print_errors_fp(stderr);
}

return fips_libctx;
return libctx;
}

OSSL_LIB_CTX* load_openssl_fips_provider(const char* conf_file_path) {
load_openssl_provider("fips", conf_file_path);
}

OSSL_LIB_CTX* load_openssl_base_provider(const char* conf_file_path) {
load_openssl_provider("base", conf_file_path);
}
133 changes: 133 additions & 0 deletions src/keyencapsulation.c
@@ -0,0 +1,133 @@
#include "keyencapsulation.h"
#include <openssl/pem.h>
#include <openssl/encoder.h>
#include <stdlib.h>

# define TEST_ptr(a) (a)
# define TEST_true(a) ((a) != 0)

static int rsa_keygen(OSSL_LIB_CTX *libctx, int bits, EVP_PKEY **pub, EVP_PKEY **priv)
{
int ret = 0;
unsigned char *pub_der = NULL;
const unsigned char *pp = NULL;
size_t len = 0;
OSSL_ENCODER_CTX *ectx = NULL;

if (!TEST_ptr(*priv = EVP_PKEY_Q_keygen(libctx, NULL, "RSA", bits))
|| !TEST_ptr(ectx =
OSSL_ENCODER_CTX_new_for_pkey(*priv,
EVP_PKEY_PUBLIC_KEY,
"DER", "type-specific",
NULL))
|| !TEST_true(OSSL_ENCODER_to_data(ectx, &pub_der, &len)))
goto err;
pp = pub_der;
if (NULL == (d2i_PublicKey(EVP_PKEY_RSA, pub, &pp, len)))
goto err;
ret = 1;
err:
OSSL_ENCODER_CTX_free(ectx);
OPENSSL_free(pub_der);
return ret;
}


kem_keyspec *init_kem_keyspec(OSSL_LIB_CTX *libctx) {
EVP_PKEY *public_key = NULL;
EVP_PKEY *private_key = NULL;
rsa_keygen(libctx, 4096, &public_key, &private_key);
init_kem_keyspec_with_key(public_key, private_key, libctx);
}

kem_keyspec *init_kem_keyspec_with_key(EVP_PKEY *rsa_pub_key, EVP_PKEY *rsa_priv_key, OSSL_LIB_CTX *libctx) {
kem_keyspec *spec = (kem_keyspec*)malloc(sizeof(kem_keyspec));
spec->public_key = rsa_pub_key;
spec->private_key = rsa_priv_key;
spec->libctx = libctx;
spec->secret = NULL;
spec->wrapped_key = NULL;
spec->secret_length = 0;
spec->wrapped_key_length =0;
return spec;
}

void free_kem_keyspec(kem_keyspec *spec) {
free(spec->wrapped_key);
free(spec->secret);
free(spec);
}

int generate_and_wrap(kem_keyspec *spec) {
size_t wrapped_key_length = 0;
size_t secret_length = 0;

EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(spec->libctx, spec->public_key, NULL);
if (ctx == NULL) {
return 1;
}

if (EVP_PKEY_encapsulate_init(ctx, NULL) <= 0) {
return 1;
}

if (EVP_PKEY_CTX_set_kem_op(ctx, "RSASVE") <= 0) {
return 1;
}

if (EVP_PKEY_encapsulate(ctx, NULL, &wrapped_key_length, NULL, &secret_length) <= 0) {
return 1;
}

spec->secret = (byte *)malloc(secret_length);
spec->wrapped_key = (byte *)malloc(wrapped_key_length);

if (spec->secret == NULL || spec->wrapped_key == NULL) {
return 1;
}

if (EVP_PKEY_encapsulate(ctx, spec->wrapped_key, &wrapped_key_length, spec->secret, &secret_length) <= 0) {
return 1;
}

spec->wrapped_key_length = wrapped_key_length;
spec->secret_length = secret_length;
return 0;
}

int unwrap(kem_keyspec *spec) {
size_t secret_length = 0;
if (spec->wrapped_key == NULL || spec->wrapped_key_length <= 0) {
return 1;
}

EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(spec->libctx, spec->private_key, NULL);
if (ctx == NULL) {
return 1;
}

if (EVP_PKEY_decapsulate_init(ctx, NULL) <= 0) {
return 1;
}

if (EVP_PKEY_CTX_set_kem_op(ctx, "RSASVE") <= 0) {
return 1;
}

if (EVP_PKEY_decapsulate(ctx, NULL, &secret_length, spec->wrapped_key, spec->wrapped_key_length) <= 0) {
return 1;
}

byte *secret = OPENSSL_malloc(secret_length);
if (secret == NULL) {
return 1;
}

if (EVP_PKEY_decapsulate(ctx, secret, &secret_length, spec->wrapped_key, spec->wrapped_key_length) <= 0) {
return 1;
}

spec->secret = secret;
spec->secret_length = secret_length;
return 0;
}
48 changes: 48 additions & 0 deletions test/keyencapsulation.c
@@ -0,0 +1,48 @@
#include "jssl.h"
#include "keyencapsulation.h"

int check(byte* alice_secret, size_t as_len, byte* bob_secret, size_t bs_len) {
if (as_len != bs_len ) {
return 0;
}
for (int i = 0; i < as_len; i++) {
if (alice_secret[i] != bob_secret[i]) {
return 0;
}
}
return 1;
}

int main(int argc, char ** argv) {
printf("Testing EVP_KEM-RSA key encapsulation: ");
byte *public_key_bytes = NULL;
size_t public_key_len = 0;
OSSL_LIB_CTX *libctx = load_openssl_fips_provider("/usr/local/ssl/openssl.cnf");

// Alice creates a KEM key specification
kem_keyspec *spec_alice = init_kem_keyspec(libctx);

// Alice sends the public key to Bob
// Bob generates and encapsulates a secret key using the public key
kem_keyspec *spec_bob = init_kem_keyspec_with_key(spec_alice->public_key, NULL, libctx);
if (generate_and_wrap(spec_bob)) {
printf("generate_and_wrap failed\n");
return 1;
}
// Bob sends the wrapped key to Alice
// Alice uses her private key to decapsulate the secret key
spec_alice->wrapped_key = spec_bob->wrapped_key;
spec_alice->wrapped_key_length = spec_bob->wrapped_key_length;
if (unwrap(spec_alice)) {
printf("unwrap failed\n");
return 1;
}

// Test: the secrets should match
if (check(spec_alice->secret, spec_alice->secret_length, spec_bob->secret, spec_bob->secret_length)) {
printf("PASSED\n");
} else {
printf("FAILED\n");
}
return 0;
}

0 comments on commit 554ec9a

Please sign in to comment.