diff --git a/ncs/Kconfig b/ncs/Kconfig index d4af825..e157812 100755 --- a/ncs/Kconfig +++ b/ncs/Kconfig @@ -176,6 +176,9 @@ config SUIT_ENVELOPE_TARGET_SIGN_ALG_ECDSA_384 config SUIT_ENVELOPE_TARGET_SIGN_ALG_ECDSA_521 bool "Use the ECDSA algorithm with key length of 521 bits" +config SUIT_ENVELOPE_TARGET_SIGN_ALG_HASHED_EDDSA + bool "Use the Hashed EdDSA algorithm (specifically: ed25519ph)" + endchoice config SUIT_ENVELOPE_TARGET_SIGN_ALG_NAME @@ -184,5 +187,6 @@ config SUIT_ENVELOPE_TARGET_SIGN_ALG_NAME default "es-256" if SUIT_ENVELOPE_TARGET_SIGN_ALG_ECDSA_256 default "es-384" if SUIT_ENVELOPE_TARGET_SIGN_ALG_ECDSA_384 default "es-521" if SUIT_ENVELOPE_TARGET_SIGN_ALG_ECDSA_521 + default "hashed-eddsa" if SUIT_ENVELOPE_TARGET_SIGN_ALG_HASHED_EDDSA endif # SUIT_ENVELOPE_TARGET_SIGN diff --git a/ncs/basic_kms.py b/ncs/basic_kms.py index 323c54e..0e53244 100644 --- a/ncs/basic_kms.py +++ b/ncs/basic_kms.py @@ -17,6 +17,10 @@ from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature +# eddsa25519ph is not supported by cryptography, so we need to use pycryptodome +from Crypto.PublicKey import ECC +from Crypto.Signature import eddsa +from Crypto.Hash import SHA512 import math from suit_generator.suit_kms_base import SuitKMSBase @@ -87,7 +91,7 @@ def _verify_signing_key_type(self, private_key, algorithm: str) -> bool: if isinstance(private_key, EllipticCurvePrivateKey): return f"es-{private_key.key_size}" == algorithm elif isinstance(private_key, Ed25519PrivateKey) or isinstance(private_key, Ed448PrivateKey): - return "eddsa" == algorithm + return "eddsa" == algorithm or "hashed-eddsa" == algorithm else: raise ValueError(f"Key {type(private_key)} not supported") @@ -104,11 +108,19 @@ def _create_cose_ed_signature(self, input_data, private_key) -> bytes: """Create ECDSA signature and return signature bytes.""" return private_key.sign(input_data) - def _get_sign_method(self, private_key) -> bool: + def _create_cose_ed_prehashed_signature(self, input_data, private_key) -> bytes: + prehashed_message = SHA512.new(input_data) + key = ECC.import_key(private_key) + signer = eddsa.new(key, 'rfc8032') + return signer.sign(prehashed_message) + + def _get_sign_method(self, private_key, algorithm) -> bool: """Return sign method based on key type.""" if isinstance(private_key, EllipticCurvePrivateKey): return self._create_cose_es_signature elif isinstance(private_key, Ed25519PrivateKey) or isinstance(private_key, Ed448PrivateKey): + if algorithm == "hashed-eddsa": + return self._create_cose_ed_prehashed_signature return self._create_cose_ed_signature else: raise ValueError(f"Key {type(private_key)} not supported") @@ -146,7 +158,13 @@ def sign(self, data: bytes, key_name: str, algorithm: str, context: str) -> byte if not self._verify_signing_key_type(private_key, algorithm): raise ValueError(f"Key {key_file_name} is not compatible with algorithm {algorithm}") - sign_method = self._get_sign_method(private_key) + sign_method = self._get_sign_method(private_key, algorithm) + + if algorithm == "hashed-eddsa": + # In the special case of hashed-eddsa, we need to use pycryptodome, which needs the raw key + # data to import the key + private_key = open(private_key_path).read() + signature = sign_method(data, private_key) return signature diff --git a/ncs/sign_script.py b/ncs/sign_script.py index a81919f..67ee433 100644 --- a/ncs/sign_script.py +++ b/ncs/sign_script.py @@ -36,6 +36,7 @@ class SuitCoseSignAlgorithms(Enum): COSE_ALG_ES_384 = -35 COSE_ALG_ES_521 = -36 COSE_ALG_EdDSA = -8 + COSE_ALG_VS_HashedEdDSA = -65537 class SuitIds(Enum): diff --git a/suit_generator/suit/security.py b/suit_generator/suit/security.py index dd8687b..3498e15 100644 --- a/suit_generator/suit/security.py +++ b/suit_generator/suit/security.py @@ -47,6 +47,7 @@ cose_alg_es_384, cose_alg_es_521, cose_alg_eddsa, + cose_alg_vs_hashed_eddsa, cose_alg_aes_gcm_128, cose_alg_aes_gcm_192, cose_alg_aes_gcm_256, @@ -183,6 +184,7 @@ class SuitcoseAlg(SuitEnum): cose_alg_es_384, cose_alg_es_521, cose_alg_eddsa, + cose_alg_vs_hashed_eddsa, cose_alg_aes_gcm_128, cose_alg_aes_gcm_192, cose_alg_aes_gcm_256, diff --git a/suit_generator/suit/types/keys.py b/suit_generator/suit/types/keys.py index e98721d..251631a 100644 --- a/suit_generator/suit/types/keys.py +++ b/suit_generator/suit/types/keys.py @@ -693,6 +693,11 @@ class cose_alg_eddsa(suit_key): id = -8 name = "cose-alg-eddsa" +class cose_alg_vs_hashed_eddsa(suit_key): + """Cose algorithm metadata.""" + + id = -65537 + name = "cose-alg-vs-hashed-eddsa" class cose_alg_aes_gcm_128(suit_key): """Cose algorithm metadata.""" diff --git a/suit_generator/suit_sign_script_base.py b/suit_generator/suit_sign_script_base.py index c5711e0..6d1ca4f 100644 --- a/suit_generator/suit_sign_script_base.py +++ b/suit_generator/suit_sign_script_base.py @@ -19,6 +19,7 @@ class SuitSignAlgorithms(Enum): ES_384 = "es-384" ES_521 = "es-521" EdDSA = "eddsa" + VS_HashedEdDSA = "hashed-eddsa" def __str__(self): return self.value