2018-09-30 13:40:18 +00:00
|
|
|
#!/usr/bin/env python
|
2018-07-16 12:38:09 +00:00
|
|
|
import ctypes
|
|
|
|
import json
|
2018-07-16 10:53:55 +00:00
|
|
|
import os
|
2018-07-16 12:38:09 +00:00
|
|
|
from binascii import hexlify, unhexlify
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
from pyasn1.codec.ber.decoder import decode as ber_decode
|
2018-07-16 10:53:55 +00:00
|
|
|
from pyasn1.codec.der.decoder import decode as der_decode
|
|
|
|
from pyasn1.codec.der.encoder import encode as der_encode
|
2018-07-16 12:38:09 +00:00
|
|
|
from pyasn1.type import namedtype, univ
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
class EcSignature(univ.Sequence):
|
|
|
|
componentType = namedtype.NamedTypes(
|
2018-07-16 12:38:09 +00:00
|
|
|
namedtype.NamedType("r", univ.Integer()),
|
|
|
|
namedtype.NamedType("s", univ.Integer()),
|
2018-07-16 10:53:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class EcKeyInfo(univ.Sequence):
|
|
|
|
componentType = namedtype.NamedTypes(
|
2018-07-16 12:38:09 +00:00
|
|
|
namedtype.NamedType("key_type", univ.ObjectIdentifier()),
|
|
|
|
namedtype.NamedType("curve_name", univ.ObjectIdentifier()),
|
2018-07-16 10:53:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class EcPublicKey(univ.Sequence):
|
|
|
|
componentType = namedtype.NamedTypes(
|
2018-07-16 12:38:09 +00:00
|
|
|
namedtype.NamedType("key_info", EcKeyInfo()),
|
|
|
|
namedtype.NamedType("public_key", univ.BitString()),
|
2018-07-16 10:53:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class EdKeyInfo(univ.Sequence):
|
|
|
|
componentType = namedtype.NamedTypes(
|
2018-07-16 12:38:09 +00:00
|
|
|
namedtype.NamedType("key_type", univ.ObjectIdentifier())
|
2018-07-16 10:53:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class EdPublicKey(univ.Sequence):
|
|
|
|
componentType = namedtype.NamedTypes(
|
2018-07-16 12:38:09 +00:00
|
|
|
namedtype.NamedType("key_info", EdKeyInfo()),
|
|
|
|
namedtype.NamedType("public_key", univ.BitString()),
|
2018-07-16 10:53:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class ParseError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class NotSupported(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class DataError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class curve_info(ctypes.Structure):
|
|
|
|
_fields_ = [("bip32_name", ctypes.c_char_p), ("params", ctypes.c_void_p)]
|
|
|
|
|
|
|
|
|
|
|
|
def keys_in_dict(dictionary, keys):
|
|
|
|
return keys <= set(dictionary.keys())
|
|
|
|
|
|
|
|
|
|
|
|
def parse_eddsa_signature(signature):
|
|
|
|
if len(signature) != 64:
|
|
|
|
raise ParseError("Not a valid EdDSA signature")
|
|
|
|
return signature
|
|
|
|
|
|
|
|
|
|
|
|
def parse_ecdh256_privkey(private_key):
|
|
|
|
if private_key < 0 or private_key.bit_length() > 256:
|
|
|
|
raise ParseError("Not a valid 256 bit ECDH private key")
|
2018-07-16 12:38:09 +00:00
|
|
|
return private_key.to_bytes(32, byteorder="big")
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
def parse_signed_hex(string):
|
|
|
|
if len(string) % 2 == 1:
|
2018-07-16 12:38:09 +00:00
|
|
|
string = "0" + string
|
2018-07-16 10:53:55 +00:00
|
|
|
number = int(string, 16)
|
|
|
|
if int(string[0], 16) & 8:
|
|
|
|
return -number
|
|
|
|
else:
|
|
|
|
return number
|
|
|
|
|
|
|
|
|
|
|
|
def parse_result(result):
|
|
|
|
if result == "valid":
|
|
|
|
return True
|
|
|
|
elif result == "invalid":
|
|
|
|
return False
|
|
|
|
elif result == "acceptable":
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
|
|
|
|
def is_valid_der(data):
|
|
|
|
try:
|
|
|
|
structure, _ = der_decode(data)
|
|
|
|
return data == der_encode(structure)
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def parse_ed_pubkey(public_key):
|
|
|
|
try:
|
|
|
|
public_key, _ = ber_decode(public_key, asn1Spec=EdPublicKey())
|
|
|
|
except Exception:
|
|
|
|
raise ParseError("Not a BER encoded Edwards curve public key")
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if not public_key["key_info"]["key_type"] == univ.ObjectIdentifier("1.3.101.112"):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise ParseError("Not a BER encoded Edwards curve public key")
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
public_key = bytes(public_key["public_key"].asOctets())
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
return public_key
|
|
|
|
|
|
|
|
|
|
|
|
def parse_ec_pubkey(public_key):
|
|
|
|
try:
|
|
|
|
public_key, _ = ber_decode(public_key, asn1Spec=EcPublicKey())
|
|
|
|
except Exception:
|
|
|
|
raise ParseError("Not a BER encoded named elliptic curve public key")
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if not public_key["key_info"]["key_type"] == univ.ObjectIdentifier(
|
|
|
|
"1.2.840.10045.2.1"
|
|
|
|
):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise ParseError("Not a BER encoded named elliptic curve public key")
|
2018-07-16 12:38:09 +00:00
|
|
|
curve_identifier = public_key["key_info"]["curve_name"]
|
2018-07-16 10:53:55 +00:00
|
|
|
curve_name = get_curve_name_by_identifier(curve_identifier)
|
|
|
|
|
|
|
|
if curve_name is None:
|
2018-07-16 12:38:09 +00:00
|
|
|
raise NotSupported(
|
|
|
|
"Unsupported named elliptic curve: {}".format(curve_identifier)
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
public_key = bytes(public_key["public_key"].asOctets())
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise ParseError("Not a BER encoded named elliptic curve public key")
|
|
|
|
|
|
|
|
return curve_name, public_key
|
|
|
|
|
|
|
|
|
|
|
|
def parse_ecdsa256_signature(signature):
|
|
|
|
s = signature
|
|
|
|
if not is_valid_der(signature):
|
|
|
|
raise ParseError("Not a valid DER")
|
|
|
|
try:
|
|
|
|
signature, _ = der_decode(signature, asn1Spec=EcSignature())
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise ParseError("Not a valid DER encoded ECDSA signature")
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
r = int(signature["r"]).to_bytes(32, byteorder="big")
|
|
|
|
s = int(signature["s"]).to_bytes(32, byteorder="big")
|
2018-07-16 10:53:55 +00:00
|
|
|
signature = r + s
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise ParseError("Not a valid DER encoded 256 bit ECDSA signature")
|
|
|
|
return signature
|
|
|
|
|
|
|
|
|
|
|
|
def parse_digest(name):
|
|
|
|
if name == "SHA-256":
|
|
|
|
return 0
|
|
|
|
else:
|
|
|
|
raise NotSupported("Unsupported hash function: {}".format(name))
|
|
|
|
|
|
|
|
|
|
|
|
def get_curve_by_name(name):
|
|
|
|
lib.get_curve_by_name.restype = ctypes.c_void_p
|
2018-07-16 12:38:09 +00:00
|
|
|
curve = lib.get_curve_by_name(bytes(name, "ascii"))
|
|
|
|
if curve is None:
|
2018-07-16 10:53:55 +00:00
|
|
|
return None
|
|
|
|
curve = ctypes.cast(curve, ctypes.POINTER(curve_info))
|
|
|
|
return ctypes.c_void_p(curve.contents.params)
|
|
|
|
|
|
|
|
|
|
|
|
def parse_curve_name(name):
|
2018-07-16 12:38:09 +00:00
|
|
|
if name == "secp256r1":
|
|
|
|
return "nist256p1"
|
|
|
|
elif name == "secp256k1":
|
|
|
|
return "secp256k1"
|
|
|
|
elif name == "curve25519":
|
|
|
|
return "curve25519"
|
2018-07-16 10:53:55 +00:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def get_curve_name_by_identifier(identifier):
|
2018-07-16 12:38:09 +00:00
|
|
|
if identifier == univ.ObjectIdentifier("1.3.132.0.10"):
|
|
|
|
return "secp256k1"
|
|
|
|
elif identifier == univ.ObjectIdentifier("1.2.840.10045.3.1.7"):
|
|
|
|
return "nist256p1"
|
2018-07-16 10:53:55 +00:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def chacha_poly_encrypt(key, iv, associated_data, plaintext):
|
|
|
|
context = bytes(context_structure_length)
|
|
|
|
tag = bytes(16)
|
|
|
|
ciphertext = bytes(len(plaintext))
|
|
|
|
lib.rfc7539_init(context, key, iv)
|
|
|
|
lib.rfc7539_auth(context, associated_data, len(associated_data))
|
|
|
|
lib.chacha20poly1305_encrypt(context, plaintext, ciphertext, len(plaintext))
|
|
|
|
lib.rfc7539_finish(context, len(associated_data), len(plaintext), tag)
|
|
|
|
return ciphertext, tag
|
|
|
|
|
|
|
|
|
|
|
|
def chacha_poly_decrypt(key, iv, associated_data, ciphertext, tag):
|
|
|
|
context = bytes(context_structure_length)
|
|
|
|
computed_tag = bytes(16)
|
|
|
|
plaintext = bytes(len(ciphertext))
|
|
|
|
lib.rfc7539_init(context, key, iv)
|
|
|
|
lib.rfc7539_auth(context, associated_data, len(associated_data))
|
|
|
|
lib.chacha20poly1305_decrypt(context, ciphertext, plaintext, len(ciphertext))
|
|
|
|
lib.rfc7539_finish(context, len(associated_data), len(ciphertext), computed_tag)
|
|
|
|
return plaintext if tag == computed_tag else False
|
|
|
|
|
|
|
|
|
|
|
|
def add_pkcs_padding(data):
|
|
|
|
padding_length = 16 - len(data) % 16
|
2018-07-16 12:38:09 +00:00
|
|
|
return data + bytes([padding_length] * padding_length)
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
def remove_pkcs_padding(data):
|
|
|
|
padding_length = data[-1]
|
2018-07-16 12:38:09 +00:00
|
|
|
if not (
|
|
|
|
0 < padding_length <= 16
|
|
|
|
and data[-padding_length:] == bytes([padding_length] * padding_length)
|
|
|
|
):
|
2018-07-16 10:53:55 +00:00
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return data[:-padding_length]
|
|
|
|
|
|
|
|
|
|
|
|
def aes_encrypt_initialise(key, context):
|
2018-07-16 12:38:09 +00:00
|
|
|
if len(key) == (128 / 8):
|
2018-07-16 10:53:55 +00:00
|
|
|
lib.aes_encrypt_key128(key, context)
|
2018-07-16 12:38:09 +00:00
|
|
|
elif len(key) == (192 / 8):
|
2018-07-16 10:53:55 +00:00
|
|
|
lib.aes_encrypt_key192(key, context)
|
2018-07-16 12:38:09 +00:00
|
|
|
elif len(key) == (256 / 8):
|
2018-07-16 10:53:55 +00:00
|
|
|
lib.aes_encrypt_key256(key, context)
|
|
|
|
else:
|
2018-07-16 12:38:09 +00:00
|
|
|
raise NotSupported("Unsupported key length: {}".format(len(key) * 8))
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
def aes_cbc_encrypt(key, iv, plaintext):
|
|
|
|
plaintext = add_pkcs_padding(plaintext)
|
|
|
|
context = bytes(context_structure_length)
|
|
|
|
ciphertext = bytes(len(plaintext))
|
|
|
|
aes_encrypt_initialise(key, context)
|
2018-07-16 12:38:09 +00:00
|
|
|
lib.aes_cbc_encrypt(
|
|
|
|
plaintext, ciphertext, len(plaintext), bytes(bytearray(iv)), context
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
return ciphertext
|
|
|
|
|
|
|
|
|
|
|
|
def aes_decrypt_initialise(key, context):
|
2018-07-16 12:38:09 +00:00
|
|
|
if len(key) == (128 / 8):
|
2018-07-16 10:53:55 +00:00
|
|
|
lib.aes_decrypt_key128(key, context)
|
2018-07-16 12:38:09 +00:00
|
|
|
elif len(key) == (192 / 8):
|
2018-07-16 10:53:55 +00:00
|
|
|
lib.aes_decrypt_key192(key, context)
|
2018-07-16 12:38:09 +00:00
|
|
|
elif len(key) == (256 / 8):
|
2018-07-16 10:53:55 +00:00
|
|
|
lib.aes_decrypt_key256(key, context)
|
|
|
|
else:
|
2018-07-16 12:38:09 +00:00
|
|
|
raise NotSupported("Unsupported AES key length: {}".format(len(key) * 8))
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
def aes_cbc_decrypt(key, iv, ciphertext):
|
|
|
|
context = bytes(context_structure_length)
|
|
|
|
plaintext = bytes(len(ciphertext))
|
|
|
|
aes_decrypt_initialise(key, context)
|
|
|
|
lib.aes_cbc_decrypt(ciphertext, plaintext, len(ciphertext), iv, context)
|
|
|
|
return remove_pkcs_padding(plaintext)
|
|
|
|
|
|
|
|
|
|
|
|
def load_json_testvectors(filename):
|
|
|
|
try:
|
|
|
|
result = json.loads(open(os.path.join(testvectors_directory, filename)).read())
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def generate_aes(filename):
|
|
|
|
vectors = []
|
|
|
|
|
|
|
|
data = load_json_testvectors(filename)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if not keys_in_dict(data, {"algorithm", "testGroups"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if data["algorithm"] != "AES-CBC-PKCS5":
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test_group in data["testGroups"]:
|
|
|
|
if not keys_in_dict(test_group, {"tests"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test in test_group["tests"]:
|
|
|
|
if not keys_in_dict(test, {"key", "iv", "msg", "ct", "result"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
key = unhexlify(test["key"])
|
|
|
|
iv = unhexlify(test["iv"])
|
|
|
|
plaintext = unhexlify(test["msg"])
|
|
|
|
ciphertext = unhexlify(test["ct"])
|
|
|
|
result = parse_result(test["result"])
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if len(key) not in [128 / 8, 192 / 8, 256 / 8]:
|
2018-07-16 10:53:55 +00:00
|
|
|
continue
|
|
|
|
|
|
|
|
if result is None:
|
|
|
|
continue
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
vectors.append(
|
|
|
|
(
|
|
|
|
hexlify(key),
|
|
|
|
hexlify(iv),
|
|
|
|
hexlify(plaintext),
|
|
|
|
hexlify(ciphertext),
|
|
|
|
result,
|
|
|
|
)
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
return vectors
|
|
|
|
|
|
|
|
|
|
|
|
def generate_chacha_poly(filename):
|
|
|
|
vectors = []
|
|
|
|
|
|
|
|
data = load_json_testvectors(filename)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if not keys_in_dict(data, {"algorithm", "testGroups"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if data["algorithm"] != "CHACHA20-POLY1305":
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test_group in data["testGroups"]:
|
|
|
|
if not keys_in_dict(test_group, {"tests"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test in test_group["tests"]:
|
|
|
|
if not keys_in_dict(
|
|
|
|
test, {"key", "iv", "aad", "msg", "ct", "tag", "result"}
|
|
|
|
):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
key = unhexlify(test["key"])
|
|
|
|
iv = unhexlify(test["iv"])
|
|
|
|
associated_data = unhexlify(test["aad"])
|
|
|
|
plaintext = unhexlify(test["msg"])
|
|
|
|
ciphertext = unhexlify(test["ct"])
|
|
|
|
tag = unhexlify(test["tag"])
|
|
|
|
result = parse_result(test["result"])
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
if result is None:
|
|
|
|
continue
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
vectors.append(
|
|
|
|
(
|
|
|
|
hexlify(key),
|
|
|
|
hexlify(iv),
|
|
|
|
hexlify(associated_data),
|
|
|
|
hexlify(plaintext),
|
|
|
|
hexlify(ciphertext),
|
|
|
|
hexlify(tag),
|
|
|
|
result,
|
|
|
|
)
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
return vectors
|
|
|
|
|
|
|
|
|
|
|
|
def generate_curve25519_dh(filename):
|
|
|
|
vectors = []
|
|
|
|
|
|
|
|
data = load_json_testvectors(filename)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if not keys_in_dict(data, {"algorithm", "testGroups"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2022-01-05 13:00:12 +00:00
|
|
|
if data["algorithm"] != "XDH":
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test_group in data["testGroups"]:
|
2022-01-05 13:00:12 +00:00
|
|
|
if not keys_in_dict(test_group, {"tests", "curve"}):
|
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
|
|
|
curve_name = parse_curve_name(test_group["curve"])
|
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test in test_group["tests"]:
|
2022-01-05 13:00:12 +00:00
|
|
|
if not keys_in_dict(test, {"public", "private", "shared", "result"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
public_key = unhexlify(test["public"])
|
|
|
|
private_key = unhexlify(test["private"])
|
|
|
|
shared = unhexlify(test["shared"])
|
|
|
|
result = parse_result(test["result"])
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if curve_name != "curve25519":
|
2018-07-16 10:53:55 +00:00
|
|
|
continue
|
|
|
|
if result is None:
|
|
|
|
continue
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
vectors.append(
|
|
|
|
(hexlify(public_key), hexlify(private_key), hexlify(shared), result)
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
return vectors
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
|
2018-07-16 10:53:55 +00:00
|
|
|
def generate_ecdh(filename):
|
|
|
|
vectors = []
|
|
|
|
|
|
|
|
data = load_json_testvectors(filename)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if not keys_in_dict(data, {"algorithm", "testGroups"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if data["algorithm"] != "ECDH":
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test_group in data["testGroups"]:
|
2022-01-05 13:00:12 +00:00
|
|
|
if not keys_in_dict(test_group, {"tests", "curve"}):
|
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
|
|
|
curve_name = parse_curve_name(test_group["curve"])
|
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test in test_group["tests"]:
|
2022-01-05 13:00:12 +00:00
|
|
|
if not keys_in_dict(test, {"public", "private", "shared", "result"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
public_key = unhexlify(test["public"])
|
|
|
|
private_key = parse_signed_hex(test["private"])
|
|
|
|
shared = unhexlify(test["shared"])
|
|
|
|
result = parse_result(test["result"])
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
|
|
|
private_key = parse_ecdh256_privkey(private_key)
|
|
|
|
except ParseError:
|
|
|
|
continue
|
|
|
|
|
|
|
|
try:
|
|
|
|
key_curve_name, public_key = parse_ec_pubkey(public_key)
|
|
|
|
except NotSupported:
|
|
|
|
continue
|
|
|
|
except ParseError:
|
|
|
|
continue
|
|
|
|
|
|
|
|
if key_curve_name != curve_name:
|
|
|
|
continue
|
|
|
|
if result is None:
|
|
|
|
continue
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
vectors.append(
|
|
|
|
(
|
|
|
|
curve_name,
|
|
|
|
hexlify(public_key),
|
|
|
|
hexlify(private_key),
|
|
|
|
hexlify(shared),
|
|
|
|
result,
|
|
|
|
)
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
return vectors
|
|
|
|
|
|
|
|
|
|
|
|
def generate_ecdsa(filename):
|
|
|
|
vectors = []
|
|
|
|
|
|
|
|
data = load_json_testvectors(filename)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if not keys_in_dict(data, {"algorithm", "testGroups"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if data["algorithm"] != "ECDSA":
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test_group in data["testGroups"]:
|
|
|
|
if not keys_in_dict(test_group, {"tests", "keyDer", "sha"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
public_key = unhexlify(test_group["keyDer"])
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
|
|
|
curve_name, public_key = parse_ec_pubkey(public_key)
|
|
|
|
except NotSupported:
|
|
|
|
continue
|
|
|
|
except ParseError:
|
|
|
|
continue
|
|
|
|
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
hasher = parse_digest(test_group["sha"])
|
2018-07-16 10:53:55 +00:00
|
|
|
except NotSupported:
|
|
|
|
continue
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test in test_group["tests"]:
|
|
|
|
if not keys_in_dict(test, {"sig", "msg", "result"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
signature = unhexlify(test["sig"])
|
|
|
|
message = unhexlify(test["msg"])
|
|
|
|
result = parse_result(test["result"])
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
if result is None:
|
|
|
|
continue
|
|
|
|
|
|
|
|
try:
|
|
|
|
signature = parse_ecdsa256_signature(signature)
|
|
|
|
except ParseError:
|
|
|
|
continue
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
vectors.append(
|
|
|
|
(
|
|
|
|
curve_name,
|
|
|
|
hexlify(public_key),
|
|
|
|
hasher,
|
|
|
|
hexlify(message),
|
|
|
|
hexlify(signature),
|
|
|
|
result,
|
|
|
|
)
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
return vectors
|
|
|
|
|
|
|
|
|
|
|
|
def generate_eddsa(filename):
|
|
|
|
vectors = []
|
|
|
|
|
|
|
|
data = load_json_testvectors(filename)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if not keys_in_dict(data, {"algorithm", "testGroups"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
if data["algorithm"] != "EDDSA":
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test_group in data["testGroups"]:
|
|
|
|
if not keys_in_dict(test_group, {"tests", "keyDer"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
public_key = unhexlify(test_group["keyDer"])
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
|
|
|
public_key = parse_ed_pubkey(public_key)
|
|
|
|
except ParseError:
|
|
|
|
continue
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
for test in test_group["tests"]:
|
|
|
|
if not keys_in_dict(test, {"sig", "msg", "result"}):
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
try:
|
2018-07-16 12:38:09 +00:00
|
|
|
signature = unhexlify(test["sig"])
|
|
|
|
message = unhexlify(test["msg"])
|
|
|
|
result = parse_result(test["result"])
|
2019-04-18 14:27:27 +00:00
|
|
|
except Exception:
|
2018-07-16 10:53:55 +00:00
|
|
|
raise DataError()
|
|
|
|
|
|
|
|
if result is None:
|
|
|
|
continue
|
|
|
|
|
|
|
|
try:
|
|
|
|
signature = parse_eddsa_signature(signature)
|
|
|
|
except ParseError:
|
|
|
|
continue
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
vectors.append(
|
|
|
|
(hexlify(public_key), hexlify(message), hexlify(signature), result)
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
return vectors
|
|
|
|
|
|
|
|
|
|
|
|
dir = os.path.abspath(os.path.dirname(__file__))
|
2018-07-16 12:38:09 +00:00
|
|
|
lib = ctypes.cdll.LoadLibrary(os.path.join(dir, "libtrezor-crypto.so"))
|
2021-06-16 12:34:34 +00:00
|
|
|
if not lib.zkp_context_is_initialized():
|
2021-11-03 21:25:16 +00:00
|
|
|
assert lib.zkp_context_init() == 0
|
2018-07-16 12:38:09 +00:00
|
|
|
testvectors_directory = os.path.join(dir, "wycheproof/testvectors")
|
2018-07-16 10:53:55 +00:00
|
|
|
context_structure_length = 1024
|
|
|
|
|
|
|
|
curve25519_dh_vectors = generate_curve25519_dh("x25519_test.json")
|
|
|
|
eddsa_vectors = generate_eddsa("eddsa_test.json")
|
2018-07-16 12:38:09 +00:00
|
|
|
ecdsa_vectors = (
|
|
|
|
generate_ecdsa("ecdsa_test.json")
|
|
|
|
+ generate_ecdsa("ecdsa_secp256k1_sha256_test.json")
|
|
|
|
+ generate_ecdsa("ecdsa_secp256r1_sha256_test.json")
|
|
|
|
)
|
|
|
|
ecdh_vectors = (
|
|
|
|
generate_ecdh("ecdh_test.json")
|
|
|
|
+ generate_ecdh("ecdh_secp256k1_test.json")
|
|
|
|
+ generate_ecdh("ecdh_secp256r1_test.json")
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
chacha_poly_vectors = generate_chacha_poly("chacha20_poly1305_test.json")
|
|
|
|
aes_vectors = generate_aes("aes_cbc_pkcs5_test.json")
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("public_key, message, signature, result", eddsa_vectors)
|
|
|
|
def test_eddsa(public_key, message, signature, result):
|
|
|
|
public_key = unhexlify(public_key)
|
|
|
|
signature = unhexlify(signature)
|
|
|
|
message = unhexlify(message)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
computed_result = (
|
|
|
|
lib.ed25519_sign_open(message, len(message), public_key, signature) == 0
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
assert result == computed_result
|
|
|
|
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"curve_name, public_key, hasher, message, signature, result", ecdsa_vectors
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
def test_ecdsa(curve_name, public_key, hasher, message, signature, result):
|
|
|
|
curve = get_curve_by_name(curve_name)
|
|
|
|
if curve is None:
|
2018-07-16 12:38:09 +00:00
|
|
|
raise NotSupported("Curve not supported: {}".format(curve_name))
|
2018-07-16 10:53:55 +00:00
|
|
|
|
|
|
|
public_key = unhexlify(public_key)
|
|
|
|
signature = unhexlify(signature)
|
|
|
|
message = unhexlify(message)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
computed_result = (
|
|
|
|
lib.ecdsa_verify(curve, hasher, public_key, signature, message, len(message))
|
|
|
|
== 0
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
assert result == computed_result
|
|
|
|
|
|
|
|
|
2021-06-16 12:34:34 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"curve_name, public_key, hasher, message, signature, result",
|
|
|
|
filter(lambda v: v[0] == "secp256k1", ecdsa_vectors),
|
|
|
|
)
|
|
|
|
def test_ecdsa_zkp(curve_name, public_key, hasher, message, signature, result):
|
|
|
|
curve = get_curve_by_name(curve_name)
|
|
|
|
if curve is None:
|
|
|
|
raise NotSupported("Curve not supported: {}".format(curve_name))
|
|
|
|
|
|
|
|
public_key = unhexlify(public_key)
|
|
|
|
signature = unhexlify(signature)
|
|
|
|
message = unhexlify(message)
|
|
|
|
|
|
|
|
computed_result = (
|
|
|
|
lib.zkp_ecdsa_verify(
|
|
|
|
curve, hasher, public_key, signature, message, len(message)
|
|
|
|
)
|
|
|
|
== 0
|
|
|
|
)
|
|
|
|
assert result == computed_result
|
|
|
|
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"public_key, private_key, shared, result", curve25519_dh_vectors
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
def test_curve25519_dh(public_key, private_key, shared, result):
|
|
|
|
public_key = unhexlify(public_key)
|
|
|
|
private_key = unhexlify(private_key)
|
|
|
|
shared = unhexlify(shared)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
computed_shared = bytes([0] * 32)
|
2018-07-16 10:53:55 +00:00
|
|
|
lib.curve25519_scalarmult(computed_shared, private_key, public_key)
|
|
|
|
computed_result = shared == computed_shared
|
|
|
|
assert result == computed_result
|
|
|
|
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"curve_name, public_key, private_key, shared, result", ecdh_vectors
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
def test_ecdh(curve_name, public_key, private_key, shared, result):
|
|
|
|
curve = get_curve_by_name(curve_name)
|
|
|
|
if curve is None:
|
|
|
|
raise NotSupported("Curve not supported: {}".format(curve_name))
|
|
|
|
|
|
|
|
public_key = unhexlify(public_key)
|
|
|
|
private_key = unhexlify(private_key)
|
|
|
|
shared = unhexlify(shared)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
computed_shared = bytes([0] * 2 * 32)
|
2018-07-16 10:53:55 +00:00
|
|
|
lib.ecdh_multiply(curve, private_key, public_key, computed_shared)
|
|
|
|
computed_shared = computed_shared[1:33]
|
|
|
|
computed_result = shared == computed_shared
|
|
|
|
assert result == computed_result
|
|
|
|
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"key, iv, associated_data, plaintext, ciphertext, tag, result", chacha_poly_vectors
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
def test_chacha_poly(key, iv, associated_data, plaintext, ciphertext, tag, result):
|
|
|
|
key = unhexlify(key)
|
|
|
|
iv = unhexlify(iv)
|
|
|
|
associated_data = unhexlify(associated_data)
|
|
|
|
plaintext = unhexlify(plaintext)
|
|
|
|
ciphertext = unhexlify(ciphertext)
|
|
|
|
tag = unhexlify(tag)
|
|
|
|
|
2018-07-16 12:38:09 +00:00
|
|
|
computed_ciphertext, computed_tag = chacha_poly_encrypt(
|
|
|
|
key, iv, associated_data, plaintext
|
|
|
|
)
|
2018-07-16 10:53:55 +00:00
|
|
|
computed_result = ciphertext == computed_ciphertext and tag == computed_tag
|
|
|
|
assert result == computed_result
|
|
|
|
|
|
|
|
computed_plaintext = chacha_poly_decrypt(key, iv, associated_data, ciphertext, tag)
|
|
|
|
computed_result = plaintext == computed_plaintext
|
|
|
|
assert result == computed_result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("key, iv, plaintext, ciphertext, result", aes_vectors)
|
|
|
|
def test_aes(key, iv, plaintext, ciphertext, result):
|
|
|
|
key = unhexlify(key)
|
|
|
|
iv = unhexlify(iv)
|
|
|
|
plaintext = unhexlify(plaintext)
|
|
|
|
ciphertext = unhexlify(ciphertext)
|
|
|
|
|
|
|
|
computed_ciphertext = aes_cbc_encrypt(key, iv, plaintext)
|
|
|
|
computed_result = ciphertext == computed_ciphertext
|
|
|
|
assert result == computed_result
|
|
|
|
|
|
|
|
computed_plaintext = aes_cbc_decrypt(key, bytes(iv), ciphertext)
|
|
|
|
computed_result = plaintext == computed_plaintext
|
|
|
|
assert result == computed_result
|