Commit 41caf04e authored by Matthew Campagna's avatar Matthew Campagna
Browse files

Merge branch 'update_rand_derand' into 'master'

ETSI TS 103 744  V 1.2.1

See merge request !1
parents 4dc60091 45b4076e
Loading
Loading
Loading
Loading

Makefile

0 → 100644
+136 −0
Original line number Diff line number Diff line
# Makefile for ETSI TS 103 744

# Detect the operating system
UNAME_S := $(shell uname -s)

# Compiler and flags
CC := gcc
CFLAGS := -Wall
LDFLAGS := -lcrypto -loqs

# Directories
WORKSPACE := $(shell pwd)/quantumsafe
BUILD_DIR := $(WORKSPACE)/build
LIB_DIR := $(BUILD_DIR)/lib
SRC_DIR := $(shell pwd)
OBJ_DIR := $(shell pwd)/obj
BIN_DIR := $(shell pwd)/bin

# Source and object files
SRCS := $(wildcard $(SRC_DIR)/*.c)
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)

# Target executable
TARGET := $(BIN_DIR)/etsi-hkex-test

# Dependencies
DEPS := openssl liboqs oqs-provider

# Phony targets
.PHONY: all update install setup openssl liboqs oqs-provider test-oqs update-ldconfig compile update-ldconfig clean
#

# Default target
all: update install setup openssl liboqs oqs-provider test-oqs compile run
# all: update install setup openssl liboqs oqs-provider test-oqs compile 


# Update and install necessary packages (OS-specific)
update:
ifeq ($(UNAME_S),Linux)
	sudo apt update
	sudo apt -y install git build-essential perl cmake autoconf libtool zlib1g-dev
else ifeq ($(UNAME_S),Darwin)
	brew update
	brew install git cmake autoconf automake libtool
endif

# Setup workspace and build directory
setup:
	mkdir -p $(LIB_DIR)

# Clone and build OpenSSL
openssl:
	@if [ ! -d $(WORKSPACE)/openssl ]; then \
		echo "Cloning and building OpenSSL..."; \
		cd $(WORKSPACE) && \
		git clone -b openssl-3.2 https://github.com/openssl/openssl && \
		cd openssl && \
		./Configure \
 			--prefix=$(BUILD_DIR) \
  			no-ssl no-tls1 no-tls1_1 no-afalgeng \
  			no-shared threads -lm && \
        make && \
		echo "OpenSSL cloned and built successfully."; \
	else \
		echo "OpenSSL directory already exists. Skipping clone and build."; \
	fi

# Clone and build liboqs
liboqs:
	@if [ ! -d $(WORKSPACE)/liboqs ]; then \
		echo "Cloning and building liboqs..."; \
		cd $(WORKSPACE) && \
		git clone https://github.com/open-quantum-safe/liboqs  && \
		cd liboqs  && \
		git checkout 0.13.0-release && \
		mkdir build && cd build  && \
		cmake \
			-DBUILD_SHARED_LIBS=ON \
			-DOQS_USE_OPENSSL=OFF \
			-DCMAKE_BUILD_TYPE=Release \
			-DOQS_BUILD_ONLY_LIB=ON \
			-DOQS_DIST_BUILD=ON \
			..   && \
		make  && \
		echo "liboqs cloned and built successfully."; \
	else \
		echo "liboqs directory already exists. Skipping clone and build."; \
	fi

# Clone and build oqs-provider
oqs-provider:
	@if [ ! -d $(WORKSPACE)/oqs-provider ]; then \
		echo "Cloning and building oqs-provider..." ; \
		cd $(WORKSPACE) && \
		git clone https://github.com/open-quantum-safe/oqs-provider  && \
		cd oqs-provider && \
		git checkout 0.7.0-release && \
		liboqs_DIR=$(BUILD_DIR) cmake \
			-DOPENSSL_ROOT_DIR=$(WORKSPACE)/openssl/ \
			-DCMAKE_BUILD_TYPE=Release \
			-S . \
			-B $(BUILD_DIR)  && \
		sudo cmake --build $(BUILD_DIR) ; \
		echo "oqs-provider cloned, built, and configured successfully."; \
	else \
		echo "oqs-provider directory already exists. Skipping clone and build."; \
	fi

# Test OQS provider
test-oqs:
	@echo "Testing OQS provider..."
	@export OPENSSL_MODULES=$(BUILD_DIR)/lib && openssl list -kem-algorithms -provider oqsprovider

# Compile the project
compile:
	@echo "Compiling the project..."
	gcc -Wall -o etsi-hkex-test main.c crypto.c qshkex.c -lcrypto -loqs \
		-I$(WORKSPACE)/liboqs/build/include/ \
		-L$(BUILD_DIR)/lib
	@echo "Compilation completed. Executable: etsi-hkex-test"


# Run the compiled program
run: compile
	@echo "Running etsi-hkex-test..."
ifeq ($(UNAME_S),Linux)
	@export OPENSSL_MODULES=$(BUILD_DIR)/lib  && ./etsi-hkex-test
else ifeq ($(UNAME_S),Darwin)
	@DYLD_LIBRARY_PATH=$(BUILD_DIR)/lib:$$DYLD_LIBRARY_PATH ./etsi-hkex-test
endif

# Clean up
clean:
	rm -rf $(WORKSPACE)
	rm -f etsi-hkex-test
+13 −4
Original line number Diff line number Diff line
@@ -14,11 +14,20 @@
 This is not intended for production use.  It is intended to be a reference
 implementation for test vectors for the specification.
  
  git clone ssh://git.amazon.com/pkg/Etsi-hkex-test
  git checkout 
 ### Build instructions ###
 
This library requires OpenSSL version 1.1.1d libcrypto.
This library requires OpenSSL version 3.2.4-dev libcrypto.
 
     gcc -Wall -o etsi-hkex-test main.c qshkex.c -lcrypto
    To clone and build dependencies (openssl, liboqs, and oqs-provider), run:
    make

    To build and run etsi-hkex-test:
    make run

    Or:
    gcc -Wall -o etsi-hkex-test main.c crypto.c qshkex.c -lcrypto -loqs
    ./etsi-hkex-test
     
### License ###     

crypto.c

0 → 100644
+422 −0
Original line number Diff line number Diff line
/*
    This file implements ETSI TC CYBER QSC Quantum-safe Hybrid Key Exchanges
    (Version 1.1.1)

    This is not intended for production use.  It is intended to be a reference
    implementation for test vectors for the specification.

    It uses OpenSSL version 3.4.0 libcrypto.

    gcc -Wall -o etsi-hkex-test main.c crypto.c qshkex.c -lcrypto -loqs
    ./etsi-hkex-test

    Copyright 2020 ETSI. All rights reserved
    SPDX-License-Identifier: BSD-3-Clause
*/

#include "crypto.h"
#include "qshkex.h"

#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/provider.h>
#include <openssl/err.h>

#include <oqs/oqs.h>
#include <oqs/rand.h>

// Custom deterministic RNG function
void deterministic_randombytes(unsigned char *random_array, size_t bytes_to_generate) 
{   
    uint32_t out_len = SEED_LEN_BYTES;
    ascii_hex_strings_to_uint8(random_array, &out_len, 1, deterministic_seed[current_seed_index]);
    if (out_len != bytes_to_generate) {
        return;
    }
    current_seed_index++;
}

int test_qhkex_derand_ecdh(const int curve, const char *priv_dataA, const char *peerB, uint8_t *pubA, size_t *PA1length, uint8_t *pubB, size_t *PB1length, uint8_t *ss, uint32_t *ss_len)
{
    int            rval = FAILURE;
    BIGNUM         *privA = NULL, *x = NULL;
    EC_POINT       *peer_pointB = NULL, *shared_secret_point = NULL, *pub_keyA = NULL;
    EC_GROUP       *groupA = NULL;
    EVP_PKEY_CTX   *ctxA = NULL;
    EVP_PKEY       *pkeyA = NULL, *pkeyB = NULL;
    size_t         field_len, ss_lenA;
    uint8_t        shared_secretA[X448_KEY_LEN_BYTES];
    uint8_t        hex_private_keyA[X448_KEY_LEN_BYTES];
    uint8_t        pubA_cpy[MAX_KEY_BYTE_LEN], pubB_cpy[MAX_KEY_BYTE_LEN];
    uint8_t        hex_exp_shared[MAX_KEY_BYTE_LEN];

    do {
       if (curve == EVP_PKEY_X25519 || curve == EVP_PKEY_X448) {
            field_len = X25519_KEY_LEN_BYTES;
            if (curve == EVP_PKEY_X448) {
                field_len = X448_KEY_LEN_BYTES;
            }
            for (int i = 0; i < field_len; i++) {
                sscanf(&priv_dataA[i * 2], "%2hhx", &hex_private_keyA[i]);
            }
            if (!(pkeyA = EVP_PKEY_new_raw_private_key(curve, NULL, hex_private_keyA, field_len))) {
                break;
            }
            if (EVP_PKEY_get_raw_public_key(pkeyA, pubA, &field_len) <= 0) {
                break;
            }
            *PA1length = field_len;
            for (int i = 0; i < field_len; i++) {
                sscanf(&peerB[i * 2], "%2hhx", &pubB[i]);
            }
            if(!(pkeyB = EVP_PKEY_new_raw_public_key(curve, NULL, pubB, field_len))) {
                break;
            }
            *PB1length = field_len;
            if (!(ctxA = EVP_PKEY_CTX_new(pkeyA, NULL))) {
                break;
            }
            if (EVP_PKEY_derive_init(ctxA) <= 0) {
                break;
            }
            if (EVP_PKEY_derive_set_peer(ctxA, pkeyB) <= 0) {
                break;
            }
            ss_lenA = field_len;
            if (EVP_PKEY_derive(ctxA, shared_secretA, &ss_lenA) <= 0) {
                break;
            }
            memcpy(ss, shared_secretA, ss_lenA); 
            *ss_len = ss_lenA;
            ascii_hex_strings_to_uint8(hex_exp_shared, (uint32_t *)&field_len, 1, strk1[current_seed_index/2]);
            if (memcmp(ss, hex_exp_shared, field_len) != 0) {
                    break;
            }
            rval = SUCCESS;
        } else {
            if (BN_hex2bn(&privA, priv_dataA) <= 0) {
                break;
            }
            if (!(groupA = EC_GROUP_new_by_curve_name(curve))) {
                break;
            }
            if (!(pub_keyA = EC_POINT_new(groupA))) {
                break;
            }
            if (!EC_POINT_mul(groupA, pub_keyA, privA, NULL, NULL, NULL)) {
                break;
            }
            if (!EC_POINT_point2oct(groupA, pub_keyA, POINT_CONVERSION_UNCOMPRESSED, pubA_cpy, MAX_KEY_BUF_BYTES, NULL)) {
                break;
            }
            if (!(peer_pointB = EC_POINT_hex2point(groupA, peerB, NULL, NULL))) {
                break;
            }
            if (!EC_POINT_point2oct(groupA, peer_pointB, POINT_CONVERSION_UNCOMPRESSED, pubB_cpy, MAX_KEY_BUF_BYTES, NULL)) {
                break;
            }
            if (!(shared_secret_point = EC_POINT_new(groupA))) {
                break;
            }
            if (!EC_POINT_mul(groupA, shared_secret_point, NULL, peer_pointB, privA, NULL)) {
                break;
            }
            if (!(x = BN_new())) {
                break;
            }
            if (!EC_POINT_get_affine_coordinates(groupA, shared_secret_point, x, NULL,  NULL)) {
                break;
            }
            if (!(field_len = EC_GROUP_get_degree(groupA)/8)) {
                break;
            }
            memcpy(pubA, pubA_cpy + 1, field_len*2);
            *PA1length = field_len*2;
            memcpy(pubB, pubB_cpy + 1, field_len*2);
            *PB1length = field_len*2;
            BN_bn2bin(x, ss);
            *ss_len = field_len;
            ascii_hex_strings_to_uint8(hex_exp_shared, (uint32_t *)&field_len, 1, strk1[current_seed_index/2]);
            if (memcmp(ss, hex_exp_shared, field_len) != 0) {
                    break;
            }
            rval = SUCCESS;
            }   
    } while (0);
    if (privA) {
        BN_free(privA);
    }
    if (x) {
        BN_free(x);
    }
    if (peer_pointB) {
        EC_POINT_free(peer_pointB);
    }
    if (shared_secret_point) {
        EC_POINT_free(shared_secret_point);
    }
    if (pub_keyA) {
        EC_POINT_free(pub_keyA);
    }
    if (groupA) {
        EC_GROUP_free(groupA);
    }
    if (ctxA) {
        EVP_PKEY_CTX_free(ctxA);
    }
    if (pkeyA) {
        EVP_PKEY_free(pkeyA);
    }
    if (pkeyB) {
        EVP_PKEY_free(pkeyB);
    }
    return rval;
}

int test_qhkex_derand_mlkem(const char * alg_name, uint8_t *pubA, size_t *PA2length, uint8_t *ctB, size_t *CTB2length, uint8_t *ss, uint32_t *ss_len)
{
    int       rval = FAILURE;
    OQS_KEM   *kem;
    uint8_t   sk[OQS_KEM_ml_kem_1024_length_secret_key], sharedB[OQS_KEM_ml_kem_1024_length_shared_secret], hex_exp_shared[OQS_KEM_ml_kem_1024_length_shared_secret];
    
    do {
        OQS_randombytes_custom_algorithm(deterministic_randombytes);
        if (!(kem = OQS_KEM_new(alg_name))) {
            break;
        }
        if (OQS_KEM_keypair(kem, pubA, sk) != OQS_SUCCESS) {
            break;
        }
        *PA2length = kem->length_public_key;
        if (OQS_KEM_encaps(kem, ctB, ss, pubA) != OQS_SUCCESS) {
            break;
        }
        *CTB2length = kem->length_ciphertext;
        if (OQS_KEM_decaps(kem, sharedB, ctB, sk) != OQS_SUCCESS) {
            break;
        }
        *ss_len = OQS_KEM_ml_kem_1024_length_shared_secret;
        ascii_hex_strings_to_uint8(hex_exp_shared, ss_len, 1, strk2[current_seed_index/2 - 1]);
        if (memcmp(ss, sharedB, kem->length_shared_secret) != 0 || 
            memcmp(ss, hex_exp_shared, kem->length_shared_secret) != 0) {
                break;
        }
        rval = SUCCESS;
        } while (0);
        if (kem) {
            OQS_KEM_free(kem);
        }
    return rval;
}

int test_qhkex_rand_ecdh(int curve, uint8_t *pubA, size_t *PA1length, uint8_t *pubB, size_t *PB1length, uint8_t *ss, uint32_t *ss_len)
{
    int             rval    = FAILURE;
    EVP_PKEY_CTX    *ctxA = NULL, *ctxB = NULL;
    EVP_PKEY        *pkeyA = NULL, *pkeyB = NULL;
    uint8_t         ssB[MAX_KEY_BYTE_LEN];
    size_t          secret_lenA = 0, secret_lenB = 0;
    size_t          pubA_len = 0, pubB_len = 0;

    do {
        // Create entity A keys
        if (curve == EVP_PKEY_X25519 || curve == EVP_PKEY_X448) {
            if (!(ctxA = EVP_PKEY_CTX_new_id(curve, NULL))) {
                break;
            }
        } else {
            if (!(ctxA = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))) {
                break;
            }
        }
        if (EVP_PKEY_keygen_init(ctxA) <= 0) {
            break;
        }
        if (curve != EVP_PKEY_X25519 || curve != EVP_PKEY_X448) {
            if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctxA, curve) <= 0) {
                break;
            }
        }
        if (EVP_PKEY_keygen(ctxA, &pkeyA) <= 0) {
            break;
        }
        if (curve != EVP_PKEY_X25519 || curve != EVP_PKEY_X448) {
            if (EVP_PKEY_get_octet_string_param(pkeyA, "pub", pubA, MAX_KEY_BYTE_LEN, &pubA_len) <=0 ) {
                break;
            }
        } else {
            if (EVP_PKEY_get_raw_public_key(pkeyA, pubA, &pubA_len) <= 0) {
                break;
            }
        }
        *PA1length = pubA_len;

        // Create entity B keys
        if (curve == EVP_PKEY_X25519 || curve == EVP_PKEY_X448) {
            if (!(ctxB = EVP_PKEY_CTX_new_id(curve, NULL))) {
                break;
            }
        } else {
            if (!(ctxB = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))) {
                break;
            }
        }
        if (EVP_PKEY_keygen_init(ctxB) <= 0) {
            break;
        }
        if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctxB, curve) <= 0) {
            break;
        }
        if (EVP_PKEY_keygen(ctxB, &pkeyB) <= 0) {
            break;
        }
        if (curve != EVP_PKEY_X25519 || curve != EVP_PKEY_X448) {
            if (EVP_PKEY_get_octet_string_param(pkeyB, "pub", pubB, MAX_KEY_BYTE_LEN, &pubB_len) <= 0) {
                break;
            }
        } else {
        if (EVP_PKEY_get_raw_public_key(pkeyB, pubB, &pubB_len) <= 0) {
                break;
            }
        }
        *PB1length = pubB_len;

        // Derive entity A shared secret
        ctxA = EVP_PKEY_CTX_new(pkeyA, NULL);
        if (!ctxA) {
            break;
        }
        if (EVP_PKEY_derive_init(ctxA) <= 0) {
             break;
        }
        if (EVP_PKEY_derive_set_peer(ctxA, pkeyB) <= 0) {
             break;
        }
        if (EVP_PKEY_derive(ctxA, NULL, &secret_lenA) <= 0) {
             break;
        }
        if (EVP_PKEY_derive(ctxA, ss, &secret_lenA) <= 0) {
             break;
        }

        // Derive entity B shared secret
        ctxB = EVP_PKEY_CTX_new(pkeyB, NULL);
        if (!ctxB) {
            break;
        }
        if (EVP_PKEY_derive_init(ctxB) <= 0) {
            break;
        }
        if (EVP_PKEY_derive_set_peer(ctxB, pkeyA) <= 0) {
            break;
        }
        if (EVP_PKEY_derive(ctxB, NULL, &secret_lenB) <= 0) {
            break;
        }
        if (EVP_PKEY_derive(ctxB, ssB, &secret_lenB) <= 0) {
            break;
        }
        // Check if entities shared secrets match
        if (memcmp(ss, ssB, secret_lenB) != 0 || (secret_lenA != secret_lenB)) {
            break;
        }
        *ss_len = secret_lenA;
        rval     = SUCCESS;
    } while (0);
    if (ctxA) {
        EVP_PKEY_CTX_free(ctxA);
    }
    if (ctxB) {
        EVP_PKEY_CTX_free(ctxB);
    }
    if (pkeyA) {
        EVP_PKEY_free(pkeyA);
    }
    if (pkeyB) {
        EVP_PKEY_free(pkeyB);
    }
    return rval;
}

int test_qhkex_rand_mlkem(const char * kem, uint8_t *pubA, size_t *PA2length, uint8_t *ctB, size_t *CTB2length, uint8_t *ss, uint32_t *ss_len)
{
    int           rval = FAILURE;
    size_t        pubA_len = OQS_KEM_ml_kem_1024_length_public_key; 
    size_t        ss_out_len = OQS_KEM_ml_kem_1024_length_shared_secret;
    size_t        ciphertext_len = OQS_KEM_ml_kem_1024_length_ciphertext;
    uint8_t          shared_secretB[OQS_KEM_ml_kem_1024_length_shared_secret];
    EVP_PKEY_CTX  *pkey_ctx = NULL, *encaps_ctx = NULL, *decaps_ctx = NULL;
    EVP_PKEY      *keypair = NULL;
    OSSL_PROVIDER *oqs_provider = NULL;
    OSSL_LIB_CTX  *libctx = NULL;

    do {
        if (!(libctx = OSSL_LIB_CTX_new())) {
            break;
        }
        if (!(oqs_provider = OSSL_PROVIDER_load(libctx, "oqsprovider"))) {
            break;
        }
        if (!(pkey_ctx = EVP_PKEY_CTX_new_from_name(libctx, kem, NULL))) {
            break;
        }
        if (EVP_PKEY_keygen_init(pkey_ctx) <= 0) {
            break;
        }
        if (EVP_PKEY_keygen(pkey_ctx, &keypair) <= 0) {
            break;
        }
        if (EVP_PKEY_get_octet_string_param(keypair, "pub", pubA, OQS_KEM_ml_kem_1024_length_public_key, &pubA_len) <= 0) {
            break;
        }
        *PA2length = pubA_len;

        if (!(encaps_ctx = EVP_PKEY_CTX_new(keypair, NULL))) {
            break;
        }
        if (EVP_PKEY_encapsulate_init(encaps_ctx, NULL) <= 0) {
            break;
        }
        if (EVP_PKEY_encapsulate(encaps_ctx, NULL, &ciphertext_len, NULL, &ss_out_len) <= 0) {
            break;
        }
        *CTB2length = ciphertext_len;

        if (EVP_PKEY_encapsulate(encaps_ctx, ctB, &ciphertext_len, ss, &ss_out_len) <= 0) {
            break;
        }
        if (!(decaps_ctx = EVP_PKEY_CTX_new(keypair, NULL))) {
            break;
        }
        if (EVP_PKEY_decapsulate_init(decaps_ctx, NULL) <= 0) {
            break;
        }
        if (EVP_PKEY_decapsulate(decaps_ctx, shared_secretB, &ss_out_len, ctB, ciphertext_len) <= 0) {
            break;
        }
        if (memcmp(ss, shared_secretB, ss_out_len) != 0) {
            break;
        }
        *ss_len = ss_out_len;
        rval     = SUCCESS;
    } while (0);
    if (pkey_ctx) {
        EVP_PKEY_CTX_free(pkey_ctx);
    }
    if (encaps_ctx) {
        EVP_PKEY_CTX_free(encaps_ctx);
    }
    if (decaps_ctx) {
        EVP_PKEY_CTX_free(decaps_ctx);  
    }
    if (keypair) {
        EVP_PKEY_free(keypair);
    }
    if (oqs_provider) {
        OSSL_PROVIDER_unload(oqs_provider);
    }
    if (libctx) {
        OSSL_LIB_CTX_free(libctx);
    }
    return rval;
}
 No newline at end of file

crypto.h

0 → 100644
+39 −0
Original line number Diff line number Diff line
/*
    Header file for a reference implementation of
    ETSI TC CYBER QSC Quantum-safe Hybrid Key Exchanges (Version 1.1.1)

    This is not intended for production use.  It is intended to be a reference
    implementation for test vectors for the specification.

    Copyright 2020 ETSI. All rights reserved
    SPDX-License-Identifier: BSD-3-Clause
*/

#ifndef _QS_H_CRYPTO_KEX_H_
#define _QS_H_CRYPTO_KEX_H_

#include <stdio.h>
#include <string.h>
#include <openssl/crypto.h>
#include "qshkex.h"

#define SEED_LEN_BYTES             64
#define X25519_KEY_LEN_BYTES       32
#define X448_KEY_LEN_BYTES         56
#define MAX_KEY_BUF_BYTES          128

extern int current_seed_index;
extern const char *deterministic_seed[];
extern const char *strk1[];
extern const char *strk2[];

int test_qhkex_derand_ecdh(const int curve, const char *priv_dataA, const char *peerB, 
                        uint8_t *pubA, size_t *PA1length, uint8_t *pubB, size_t *PB1length, 
                        uint8_t *ss, uint32_t *ss_len);
int test_qhkex_derand_mlkem(const char * alg_name, uint8_t *pubA, size_t *PA2length, 
                        uint8_t *ctB, size_t *CTB2length, uint8_t *ss, uint32_t *ss_len);
int test_qhkex_rand_ecdh(int curve, uint8_t *pubA, size_t *PA1length, 
                        uint8_t *pubB, size_t *PB1length, uint8_t *ss, uint32_t *ss_len);
int test_qhkex_rand_mlkem(const char * kem, uint8_t *pubA, size_t *PA2length, 
                        uint8_t *ctB, size_t *CTB2length, uint8_t *ss, uint32_t *ss_len);
#endif /*_QS_H_CRYPTO_KEX_H_*/
+3403 −558

File changed.

Preview size limit exceeded, changes collapsed.

Loading