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

suit: Add support for Ed25519PH #167

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# created by virtualenv automatically
/venv/*
.vscode/*
/.idea/*
/suit_generator.egg-info/*
/suit_generator/_version.py
Expand Down
5 changes: 5 additions & 0 deletions ncs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ 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)"
select EXPERIMENTAL

endchoice

config SUIT_ENVELOPE_TARGET_SIGN_ALG_NAME
Expand All @@ -184,5 +188,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
25 changes: 22 additions & 3 deletions ncs/basic_kms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
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
Expand Down Expand Up @@ -87,7 +92,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")

Expand All @@ -104,11 +109,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")
Expand Down Expand Up @@ -146,7 +159,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
Expand Down
3 changes: 2 additions & 1 deletion ncs/sign_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -157,7 +158,7 @@ def sign_envelope(
self.already_signed_action(already_signed_action)

if self._skip_signing:
return
return self.envelope
protected = {
SuitIds.COSE_ALG.value: SuitCoseSignAlgorithms["COSE_ALG_" + self._algorithm.name].value,
SuitIds.COSE_KEY_ID.value: cbor2.dumps(self._key_id),
Expand Down
4 changes: 2 additions & 2 deletions suit_generator/cmd_sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def __init__(
self.key_id = int(envelope_json["key-id"], 0)

if "sign-script" in envelope_json:
self.sign_script = envelope_json["sign_script"]
self.sign_script = envelope_json["sign-script"]
elif self.sign_script is None:
if os.environ.get("NCS_SUIT_SIGN_SCRIPT"):
self.sign_script = os.environ.get("NCS_SUIT_SIGN_SCRIPT")
Expand All @@ -102,7 +102,7 @@ def __init__(
self.signer = _import_signer(self.sign_script)

if "kms-script" in envelope_json:
self.kms_script = envelope_json["kms_script"]
self.kms_script = envelope_json["kms-script"]
elif self.kms_script is None:
if os.environ.get("NCS_SUIT_KMS_SCRIPT"):
self.kms_script = os.environ.get("NCS_SUIT_KMS_SCRIPT")
Expand Down
2 changes: 2 additions & 0 deletions suit_generator/suit/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions suit_generator/suit/types/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,13 @@ class cose_alg_eddsa(suit_key):
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."""

Expand Down
1 change: 1 addition & 0 deletions suit_generator/suit_sign_script_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class SuitSignAlgorithms(Enum):
ES_384 = "es-384"
ES_521 = "es-521"
EdDSA = "eddsa"
VS_HashedEdDSA = "hashed-eddsa"
tomchy marked this conversation as resolved.
Show resolved Hide resolved

def __str__(self):
return self.value
Expand Down
Loading