diff --git a/Makefile b/Makefile index fea2f4e..4893079 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -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 diff --git a/include/keyencapsulation.h b/include/keyencapsulation.h new file mode 100644 index 0000000..0885500 --- /dev/null +++ b/include/keyencapsulation.h @@ -0,0 +1,24 @@ +#include "jssl.h" +#include + +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); diff --git a/src/init.c b/src/init.c index 6f1c7ae..714f428 100644 --- a/src/init.c +++ b/src/init.c @@ -3,17 +3,34 @@ #include #include -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); } diff --git a/src/keyencapsulation.c b/src/keyencapsulation.c new file mode 100644 index 0000000..49bc8d7 --- /dev/null +++ b/src/keyencapsulation.c @@ -0,0 +1,133 @@ +#include "keyencapsulation.h" +#include +#include +#include + +# 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; +} diff --git a/test/keyencapsulation.c b/test/keyencapsulation.c new file mode 100644 index 0000000..471782f --- /dev/null +++ b/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; +}