Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

scripts to do interoperability testing with OpenSSL #80

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions interop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Tools that allow use of the OpenSSL Encapsulation and Decapsulation API.

To enable support for PQC algorithms in OpenSSL install oqsprovider and
modify the `openssl.cnf` file to enable the oqsprovider:
```
[provider_sect]
default = default_sect
oqsprovider = oqsprovider_sect
[oqsprovider_sect]
activate = 1
```

To generate the private (decapsulation) key using OpenSSL:
```
openssl genpkey -out private-key.pem -algorithm mlkem512
```
(See other algorithm names in https://github.com/open-quantum-safe/oqs-provider)

To extract the public (encapsulation) key using OpenSSL:
```
openssl pkey -pubout -in public-key.pem -out pub.pem
```

Compile the encapsulation and decapsulation helper apps:
```
gcc -o openssl-decap -lcrypto openssl-decap.c
gcc -o openssl-encap -lcrypto openssl-encap.c
```

To encapsulate a shared secret:
```
./openssl-encap -k public-key.pem -s secret.bin -c ciphertext.bin
```

To decapsulate a shared secret:
```
./openssl-decap -k private-key.pem -s secret-dec.bin -c ciphertext.bin
```
60 changes: 60 additions & 0 deletions interop/ml_kem_decap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import sys

if len(sys.argv) != 4:
raise ValueError(f"Usage: {sys.argv[0]} dk.pem secret.bin ciphertext.bin")

from kyber_py.ml_kem import ML_KEM_512, ML_KEM_768, ML_KEM_1024

OIDS = {
(1, 3, 6, 1, 4, 1, 22554, 5, 6, 1): ML_KEM_512,
(1, 3, 6, 1, 4, 1, 22554, 5, 6, 2): ML_KEM_768,
(1, 3, 6, 1, 4, 1, 22554, 5, 6, 3): ML_KEM_1024,
}

import ecdsa.der as der

with open(sys.argv[1], "rt") as ek_file:
ek_pem = ek_file.read()

ek_der = der.unpem(ek_pem)

s1, empty = der.remove_sequence(ek_der)
if empty != b"":
raise der.UnexpectedDER("Trailing junk after DER public key")

ver, rest = der.remove_integer(s1)

if ver != 0:
raise der.UnexpectedDER("Unexpected format version")

alg_id, rest = der.remove_sequence(rest)

alg_id, empty = der.remove_object(alg_id)
if alg_id not in OIDS:
raise der.UnexpectedDER(f"Not recognised algoritm OID: {alg_id}")
if empty != b"":
raise der.UnexpectedDER("parameters specified for ML-KEM OID")

kem = OIDS[alg_id]

key_der, empty = der.remove_octet_string(rest)
if empty != b"":
raise der.UnexpectedDER("Trailing junk after the key")

keys, empty = der.remove_octet_string(key_der)
if empty != b"":
raise der.UnexpectedDER("Trailing junk after the key")

dk_len = 768 * kem.k + 96
dk, ek = keys[:dk_len], keys[dk_len:]
assert len(ek) == 384 * kem.k + 32

with open(sys.argv[3], "rb") as encaps_file:
encaps = encaps_file.read()

secret = kem.decaps(encaps, dk)

with open(sys.argv[2], "wb") as secret_file:
secret_file.write(secret)

print("done")
48 changes: 48 additions & 0 deletions interop/ml_kem_encap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import sys

if len(sys.argv) != 4:
raise ValueError(f"Usage: {sys.argv[0]} ek.pem secret.bin ciphertext.bin")

from kyber_py.ml_kem import ML_KEM_512, ML_KEM_768, ML_KEM_1024

OIDS = {
(1, 3, 6, 1, 4, 1, 22554, 5, 6, 1): ML_KEM_512,
(1, 3, 6, 1, 4, 1, 22554, 5, 6, 2): ML_KEM_768,
(1, 3, 6, 1, 4, 1, 22554, 5, 6, 3): ML_KEM_1024,
}

import ecdsa.der as der

with open(sys.argv[1], "rt") as ek_file:
ek_pem = ek_file.read()

ek_der = der.unpem(ek_pem)

s1, empty = der.remove_sequence(ek_der)
if empty != b"":
raise der.UnexpectedDER("Trailing junk after DER public key")

alg_id, rem = der.remove_sequence(s1)

alg_id, rest = der.remove_object(alg_id)
if alg_id not in OIDS:
raise der.UnexpectedDER(f"Not recognised algoritm OID: {alg_id}")

if rest != b"":
raise der.UnexpectedDER("parameters specified for ML-KEM OID")

kem = OIDS[alg_id]

key, empty = der.remove_bitstring(rem, 0)
if empty != b"":
raise der.UnexpectedDER("Trailing junk after the public key bitstring")

secret, encaps = kem.encaps(key)

with open(sys.argv[2], "wb") as secret_file:
secret_file.write(secret)

with open(sys.argv[3], "wb") as encaps_file:
encaps_file.write(encaps)

print("done")
40 changes: 40 additions & 0 deletions interop/ml_kem_keygen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import sys

if len(sys.argv) != 4:
raise ValueError(
f"Usage: {sys.argv[0]} ML-KEM-(512|768|1024) ek.pem dk.pem"
)

if sys.argv[1] == "ML-KEM-512":
from kyber_py.ml_kem.default_parameters import ML_KEM_512 as ML_KEM

oid = (1, 3, 6, 1, 4, 1, 22554, 5, 6, 1)
elif sys.argv[1] == "ML-KEM-768":
from kyber_py.ml_kem.default_parameters import ML_KEM_768 as ML_KEM

oid = (1, 3, 6, 1, 4, 1, 22554, 5, 6, 2)
elif sys.argv[1] == "ML-KEM-1024":
from kyber_py.ml_kem.default_parameters import ML_KEM_1024 as ML_KEM

oid = (1, 3, 6, 1, 4, 1, 22554, 5, 6, 3)
else:
raise ValueError(f"Unrecognised algorithm: {sys.argv[1]}")

import ecdsa.der as der

ek, dk = ML_KEM.keygen()

with open(sys.argv[2], "wb") as ek_file:
encoded = der.encode_sequence(
der.encode_sequence(der.encode_oid(*oid)),
der.encode_bitstring(ek, 0),
)
ek_file.write(der.topem(encoded, "PUBLIC KEY"))

with open(sys.argv[3], "wb") as dk_file:
encoded = der.encode_sequence(
der.encode_integer(0),
der.encode_sequence(der.encode_oid(*oid)),
der.encode_octet_string(der.encode_octet_string(dk + ek)),
)
dk_file.write(der.topem(encoded, "PRIVATE KEY"))
156 changes: 156 additions & 0 deletions interop/openssl-decap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <fcntl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>

void help(char *name) {
printf("Usage: %s -k key.pem -s secret-out.pem -c ciphertext-out.pem\n",
name);
printf("\n");
printf(" -k file File with the decapsulation key\n");
printf(" -s file File to write the secret\n");
printf(" -c file File to read the ciphertext\n");
}

int
main(int argc, char** argv) {
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *pub_key = NULL;
size_t secretlen = 0, outlen = 0;
unsigned char *out = NULL, *secret = NULL;
char *key_file_name = NULL, *secret_file_name = NULL;
char *ciphertext_file_name = NULL;
int sec_fd = -1, cip_fd = -1;
FILE *fp;
int opt;
int result = 0;

while ((opt = getopt(argc, argv, "k:s:c:")) != -1 ) {
switch (opt) {
case 'k':
key_file_name = optarg;
break;
case 's':
secret_file_name = optarg;
break;
case 'c':
ciphertext_file_name = optarg;
break;
default:
fprintf(stderr, "Unknown option: %c\n", opt);
help(argv[0]);
exit(1);
break;
}
}

if (key_file_name == NULL || secret_file_name == NULL ||
ciphertext_file_name == NULL) {
fprintf(stderr, "All options must be specified!\n");
help(argv[0]);
exit(1);
}

if ((sec_fd = open(secret_file_name, O_WRONLY|O_TRUNC|O_CREAT, 0666))
== -1){
fprintf(stderr, "can't open output file: %s\n", secret_file_name);
goto err;
}

if ((cip_fd = open(ciphertext_file_name, O_RDONLY)) == -1) {
fprintf(stderr, "Can't open output file: %s\n", ciphertext_file_name);
goto err;
}

fp = fopen(key_file_name, "r");
if (!fp) {
fprintf(stderr, "Can't open key file: %s\n", key_file_name);
goto err;
}

if ((pub_key = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) {
//if ((pub_key = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
fprintf(stderr, "Can't read parse private key\n");
goto err;
}

if (fclose(fp) != 0) {
fprintf(stderr, "can't close key file\n");
goto err;
}
fp = NULL;

ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pub_key, NULL);
if (ctx == NULL) {
fprintf(stderr, "Can't init key context\n");
goto err;
}
if (EVP_PKEY_decapsulate_init(ctx, NULL) <= 0) {
fprintf(stderr, "Can't init encapsulation\n");
goto err;
}

secretlen = 4096;
secret = OPENSSL_malloc(secretlen);
secretlen = read(cip_fd, secret, secretlen);
if (secretlen <= 0) {
fprintf(stderr, "Can't read ciphertext\n");
goto err;
}

/* Determine buffer length */
if (EVP_PKEY_decapsulate(ctx, NULL, &outlen, secret, secretlen) <= 0) {
fprintf(stderr, "Can't fetch memory size\n");
}

out = OPENSSL_malloc(outlen);
if (out == NULL || secret == NULL) {
fprintf(stderr, "memory allocation failure\n");
goto err;
}

/*
* The decapsulated 'out' can be used as key material.
*/
if (EVP_PKEY_decapsulate(ctx, out, &outlen, secret, secretlen) <= 0) {
fprintf(stderr, "decapsulation failure\n");
goto err;
}

if (write(sec_fd, out, outlen) <= 0) {
fprintf(stderr, "Error writing secret\n");
goto err;
}

printf("done\n");

goto out;

err:
result = 1;
fprintf(stderr, "operation failed\n");
ERR_print_errors_fp(stderr);

out:
if (sec_fd >= 0)
close(sec_fd);
if (cip_fd >= 0)
close(cip_fd);
if (fp)
fclose(fp);
if (out)
OPENSSL_free(out);
if (secret)
OPENSSL_free(secret);
if (ctx)
EVP_PKEY_CTX_free(ctx);
if (pub_key)
EVP_PKEY_free(pub_key);

return result;
}
Loading