Skip to content

Commit

Permalink
scripts to do interoperability testing with OpenSSL
Browse files Browse the repository at this point in the history
  • Loading branch information
tomato42 committed Jul 26, 2024
1 parent 4b4e6bb commit 4b01456
Show file tree
Hide file tree
Showing 6 changed files with 494 additions and 0 deletions.
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")
39 changes: 39 additions & 0 deletions interop/ml_kem_keygen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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

0 comments on commit 4b01456

Please sign in to comment.