-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
scripts to do interoperability testing with OpenSSL
- Loading branch information
Showing
6 changed files
with
545 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
Tools that allow use of the OpenSSL Encapsulation and Decapsulation API. | ||
|
||
**Note:** this code expects draft-ietf-lamps-kyber-certificates-04 compatible behaviour. | ||
This is consistent with oqsprovider-0.8.0. | ||
|
||
|
||
OpenSSL setup | ||
------------- | ||
|
||
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 | ||
``` | ||
If you've done that properly, the `openssl list -kem-algorithms` will list | ||
ML-KEM as valid options. | ||
|
||
OpenSSL keygen | ||
-------------- | ||
|
||
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 OpenSSL helper apps: | ||
---------------------------- | ||
|
||
Compile the encapsulation and decapsulation helper apps: | ||
``` | ||
gcc -o openssl-decap -lcrypto openssl-decap.c | ||
gcc -o openssl-encap -lcrypto openssl-encap.c | ||
``` | ||
|
||
OpenSSL encapsulation | ||
--------------------- | ||
To encapsulate a shared secret: | ||
``` | ||
./openssl-encap -k public-key.pem -s secret.bin -c ciphertext.bin | ||
``` | ||
|
||
OpenSSL decapsulation | ||
--------------------- | ||
To decapsulate a shared secret: | ||
``` | ||
./openssl-decap -k private-key.pem -s secret-dec.bin -c ciphertext.bin | ||
``` | ||
|
||
kyber-py setup | ||
-------------- | ||
As the key formats use ASN.1 and PEM encoding, they require presence | ||
of the `ecdsa` library. Install it using your distribution package manager | ||
or using `pip`: | ||
``` | ||
pip install ecdsa | ||
``` | ||
|
||
Kyber-py key gen | ||
---------------- | ||
To generate both private (decapsulation) and public (encapsulation) keys | ||
with kyber-py, run: | ||
``` | ||
PYTHONPATH=../src python ml_kem_keygen.py ML-KEM-512 public-key.pem private-key.pem | ||
``` | ||
|
||
Kyber-py encapsulation | ||
----------------------- | ||
To encapsulate a shared secret: | ||
``` | ||
PYTHONPATH=../src python ml_kem_encap.py public-key.pem secret.bin ciphertext.bin | ||
``` | ||
|
||
Kyber-py decapsulation | ||
---------------------- | ||
To decapsulate a shared secret: | ||
``` | ||
PYTHONPATH=../src python ml_kem_decap.py private-key.pem secret-dec.bin ciphertext.bin | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = { | ||
(2, 16, 840, 1, 101, 3, 4, 4, 1): ML_KEM_512, | ||
(2, 16, 840, 1, 101, 3, 4, 4, 2): ML_KEM_768, | ||
(2, 16, 840, 1, 101, 3, 4, 4, 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(dk, encaps) | ||
|
||
with open(sys.argv[2], "wb") as secret_file: | ||
secret_file.write(secret) | ||
|
||
print("done") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = { | ||
(2, 16, 840, 1, 101, 3, 4, 4, 1): ML_KEM_512, | ||
(2, 16, 840, 1, 101, 3, 4, 4, 2): ML_KEM_768, | ||
(2, 16, 840, 1, 101, 3, 4, 4, 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") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = (2, 16, 840, 1, 101, 3, 4, 4, 1) | ||
elif sys.argv[1] == "ML-KEM-768": | ||
from kyber_py.ml_kem.default_parameters import ML_KEM_768 as ML_KEM | ||
|
||
oid = (2, 16, 840, 1, 101, 3, 4, 4, 2) | ||
elif sys.argv[1] == "ML-KEM-1024": | ||
from kyber_py.ml_kem.default_parameters import ML_KEM_1024 as ML_KEM | ||
|
||
oid = (2, 16, 840, 1, 101, 3, 4, 4, 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")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.