Skip to content
Merged
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
3 changes: 1 addition & 2 deletions ncs/encrypt_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ def generate_kms_artifacts(self, asset_plaintext: bytes, key_name: str, context:

This method encrypts asset_plaintext bytes using the specified key wrap algorithm,
and returns the encrypted asset and encrypted content encryption key (CEK).

"""
# Enc structure:
# {
Expand Down Expand Up @@ -255,7 +254,7 @@ def generate(
if kw_alg == SuitKWAlgorithms.A256KW:
if encrypted_cek is None:
raise ValueError("Encrypted CEK is required for AES Key Wrap 256")
self.kw_alg_convert(kw_alg)
self._kw_alg_convert(kw_alg)
return self.generate_encryption_info_and_encrypted_payload(encrypted_asset, encrypted_cek, key_id)


Expand Down
2 changes: 1 addition & 1 deletion ncs/sign_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class SuitCoseSignAlgorithms(Enum):
COSE_ALG_ES_384 = -35
COSE_ALG_ES_521 = -36
COSE_ALG_EdDSA = -8
COSE_ALG_VS_HashedEdDSA = -65537
COSE_ALG_VS_HashEdDSA = -65537


class SuitIds(Enum):
Expand Down
6 changes: 3 additions & 3 deletions suit_generator/cmd_encrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ def encrypt_and_generate(**kwargs):
kwargs["key_name"],
kwargs["key_id"],
kwargs["context"],
kwargs["hash_alg"],
kwargs["kw_alg"],
SuitDigestAlgorithms(kwargs["hash_alg"]),
SuitKWAlgorithms(kwargs["kw_alg"]),
kwargs["kms_script"],
)
with open(os.path.join(kwargs["output_dir"], "plain_text_digest.bin"), "wb") as file:
Expand All @@ -200,7 +200,7 @@ def generate_info(**kwargs):
encrypted_firmware,
encrypted_key,
kwargs["key_id"],
kwargs["kw_alg"],
SuitKWAlgorithms(kwargs["kw_alg"]),
)
with open(os.path.join(kwargs["output_dir"], "suit_encryption_info.bin"), "wb") as file:
file.write(encryption_info)
Expand Down
2 changes: 1 addition & 1 deletion suit_generator/suit_sign_script_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class SuitSignAlgorithms(Enum):
ES_384 = "es-384"
ES_521 = "es-521"
EdDSA = "eddsa"
VS_HashedEdDSA = "hash-eddsa"
VS_HashEdDSA = "hash-eddsa"

def __str__(self):
return self.value
Expand Down
78 changes: 78 additions & 0 deletions tests/encrypt_script_mock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
"""Script mocking the Encrypt script."""
import json

from pathlib import Path
from suit_generator.suit_encrypt_script_base import (
SuitEncryptorBase,
SuitDigestAlgorithms,
SuitKWAlgorithms,
)


class EncryptorMock(SuitEncryptorBase):
"""Encryptor mock implementation."""

def encrypt_and_generate(
self,
firmware: bytes,
key_name: str,
key_id: int,
context: str,
hash_alg: SuitDigestAlgorithms,
kw_alg: SuitKWAlgorithms,
kms_script: Path,
) -> tuple[bytes, bytes, bytes, bytes, int]:
"""Mock encrypting the payload and generation of encryption artifacts."""
context_loaded = json.loads(context)
self.output_file = f"test_output_{key_name}.json"
self.json_data = {"firmware": firmware.hex()}
self.json_data["key_name"] = key_name
self.json_data["key_id"] = key_id
self.json_data["kw_alg"] = kw_alg.value
self.json_data["hash_alg"] = hash_alg.value
self.json_data["context"] = context_loaded["ctx"]
self.json_data["kms_script"] = kms_script
with open(self.output_file, "w") as f:
json.dump(self.json_data, f)

return (
bytes.fromhex(context_loaded["encrypted_data"]),
bytes.fromhex(context_loaded["tag"]),
bytes.fromhex(context_loaded["encryption_info"]),
bytes.fromhex(context_loaded["digest"]),
context_loaded["plaintext_length"],
)

def generate(
self, encrypted_asset: bytes, encrypted_cek: bytes, key_id: int, kw_alg: SuitKWAlgorithms
) -> tuple[bytes, bytes, bytes]:
"""
Mock generation of encryption artifacts.

The encrypted asset for this mock is a json object, containing the return values.
"""
# :return: The encrypted payload, tag, encryption info.
context_loaded = json.loads(encrypted_asset.decode())
self.output_file = f"test_output_{key_id}.json"

self.json_data = {"key_id": key_id}
self.json_data["kw_alg"] = kw_alg.value
self.json_data["encrypted_cek"] = encrypted_cek.hex()
with open(self.output_file, "w") as f:
json.dump(self.json_data, f)

return (
bytes.fromhex(context_loaded["encrypted_data"]),
bytes.fromhex(context_loaded["tag"]),
bytes.fromhex(context_loaded["encryption_info"]),
)


def suit_encryptor_factory():
"""Get an Encryptor object."""
return EncryptorMock()
62 changes: 62 additions & 0 deletions tests/kms_script_mock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
"""Script mocking the KMS script."""
from __future__ import annotations

from suit_generator.suit_kms_base import SuitKMSBase
import json


class SuitMockKMS(SuitKMSBase):
"""Implementation mocking a KMS."""

def init_kms(self, context: str) -> None:
"""
Initialize the KMS.

:param context: The context to be used - json string with keys {"output_file": "<path>", "ctx": "<ctx>"}

For signing mocking it also has to contain a "signature" key with the mocked signature to be returned.
For encryption mocking it also has to contain "iv", "encryption_key" and "encrypted_data" keys
with the mocked values to be returned.
All the data will be stored into the file pointed by the output_file key.

"""
context_loaded = json.loads(context)
self.output_file = context_loaded["output_file"]
self.json_data = {"init_kms_ctx": context_loaded["ctx"]}

def encrypt(self, plaintext: bytes, key_name: str, context: str, aad: bytes) -> tuple[bytes, bytes, bytes]:
"""Mock of the KMS script encrypt function."""
context_loaded = json.loads(context)
self.json_data["encrypt_plaintext"] = plaintext.hex()
self.json_data["encrypt_key_name"] = key_name
self.json_data["encrypt_context"] = context_loaded["ctx"]
self.json_data["encrypt_aad"] = aad.hex()
with open(self.output_file, "w") as f:
json.dump(self.json_data, f)

return (
bytes.fromhex(context_loaded["iv"]),
bytes.fromhex(context_loaded["tag"]),
bytes.fromhex(context_loaded["encrypted_data"]),
)

def sign(self, data: bytes, key_name: str, algorithm: str, context: str) -> bytes:
"""Mock of the KMS script sign function."""
context_loaded = json.loads(context)
self.json_data["sign_data"] = data.hex()
self.json_data["sign_key_name"] = key_name
self.json_data["sign_algorithm"] = algorithm
self.json_data["sign_context"] = context_loaded["ctx"]
with open(self.output_file, "w") as f:
json.dump(self.json_data, f)
return context_loaded["signature"].encode()


def suit_kms_factory():
"""Get a KMS object."""
return SuitMockKMS()
65 changes: 65 additions & 0 deletions tests/sign_script_mock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
"""Script mocking the Sign script."""
from __future__ import annotations
from suit_generator.suit_sign_script_base import (
SuitEnvelopeSignerBase,
SignatureAlreadyPresentActions,
SuitSignAlgorithms,
)
import cbor2
import json
from pathlib import Path
from enum import Enum


class SuitIds(Enum):
"""Suit elements identifiers."""

SUIT_AUTHENTICATION_WRAPPER = 2


class SignerMock(SuitEnvelopeSignerBase):
"""Signer mock implementation."""

def mock_add_signature(self, mocked_signature: bytes) -> None:
"""Add signature object to the envelope."""
data = [cbor2.dumps({}), {}, None, mocked_signature]
new_auth = cbor2.CBORTag(18, data)
auth_block = cbor2.loads(self.envelope.value[SuitIds.SUIT_AUTHENTICATION_WRAPPER.value])
auth_block.append(cbor2.dumps(new_auth))
self.envelope.value[SuitIds.SUIT_AUTHENTICATION_WRAPPER.value] = cbor2.dumps(auth_block)

def sign_envelope(
self,
input_envelope: cbor2.CBORTag,
key_name: str,
key_id: int,
algorithm: SuitSignAlgorithms,
context: str,
kms_script: Path,
already_signed_action: SignatureAlreadyPresentActions,
) -> cbor2.CBORTag:
"""Mock adding signature to the envelope."""
context_loaded = json.loads(context)
self.output_file = f"test_output_{key_name}.json"
self.json_data = {"key_name": key_name}
self.json_data["key_id"] = key_id
self.json_data["algorithm"] = algorithm.value
self.json_data["context"] = context_loaded["ctx"]
self.json_data["kms_script"] = kms_script
self.json_data["already_signed_action"] = already_signed_action.value
with open(self.output_file, "w") as f:
json.dump(self.json_data, f)

self.envelope = input_envelope
self.mock_add_signature(context_loaded["signature"].encode())
return self.envelope


def suit_signer_factory():
"""Get a Signer object."""
return SignerMock()
Loading