diff --git a/cyclonedx/model/crypto.py b/cyclonedx/model/crypto.py
index 44bdc182..eca00eee 100644
--- a/cyclonedx/model/crypto.py
+++ b/cyclonedx/model/crypto.py
@@ -344,8 +344,10 @@ class AlgorithmProperties:
def __init__(
self, *,
primitive: Optional[CryptoPrimitive] = None,
+ algorithm_family: Optional[str] = None,
parameter_set_identifier: Optional[str] = None,
curve: Optional[str] = None,
+ elliptic_curve: Optional[str] = None,
execution_environment: Optional[CryptoExecutionEnvironment] = None,
implementation_platform: Optional[CryptoImplementationPlatform] = None,
certification_levels: Optional[Iterable[CryptoCertificationLevel]] = None,
@@ -356,8 +358,10 @@ def __init__(
nist_quantum_security_level: Optional[int] = None,
) -> None:
self.primitive = primitive
+ self.algorithm_family = algorithm_family
self.parameter_set_identifier = parameter_set_identifier
self.curve = curve
+ self.elliptic_curve = elliptic_curve
self.execution_environment = execution_environment
self.implementation_platform = implementation_platform
self.certification_levels = certification_levels or []
@@ -392,7 +396,18 @@ def primitive(self, primitive: Optional[CryptoPrimitive]) -> None:
self._primitive = primitive
@property
+ @serializable.view(SchemaVersion1Dot7)
@serializable.xml_sequence(2)
+ def algorithm_family(self) -> Optional[str]:
+ """The standardized family identifier for the algorithm."""
+ return self._algorithm_family
+
+ @algorithm_family.setter
+ def algorithm_family(self, algorithm_family: Optional[str]) -> None:
+ self._algorithm_family = algorithm_family
+
+ @property
+ @serializable.xml_sequence(3)
def parameter_set_identifier(self) -> Optional[str]:
"""
An identifier for the parameter set of the cryptographic algorithm. Examples: in AES128, '128' identifies the
@@ -409,7 +424,7 @@ def parameter_set_identifier(self, parameter_set_identifier: Optional[str]) -> N
self._parameter_set_identifier = parameter_set_identifier
@property
- @serializable.xml_sequence(3)
+ @serializable.xml_sequence(4)
def curve(self) -> Optional[str]:
"""
The specific underlying Elliptic Curve (EC) definition employed which is an indicator of the level of security
@@ -427,7 +442,18 @@ def curve(self, curve: Optional[str]) -> None:
self._curve = curve
@property
- @serializable.xml_sequence(4)
+ @serializable.view(SchemaVersion1Dot7)
+ @serializable.xml_sequence(5)
+ def elliptic_curve(self) -> Optional[str]:
+ """The standardized identifier of the elliptic curve used by the algorithm."""
+ return self._elliptic_curve
+
+ @elliptic_curve.setter
+ def elliptic_curve(self, elliptic_curve: Optional[str]) -> None:
+ self._elliptic_curve = elliptic_curve
+
+ @property
+ @serializable.xml_sequence(6)
def execution_environment(self) -> Optional[CryptoExecutionEnvironment]:
"""
The target and execution environment in which the algorithm is implemented in.
@@ -442,7 +468,7 @@ def execution_environment(self, execution_environment: Optional[CryptoExecutionE
self._execution_environment = execution_environment
@property
- @serializable.xml_sequence(4)
+ @serializable.xml_sequence(7)
def implementation_platform(self) -> Optional[CryptoImplementationPlatform]:
"""
The target platform for which the algorithm is implemented. The implementation can be 'generic', running on
@@ -462,7 +488,7 @@ def implementation_platform(self, implementation_platform: Optional[CryptoImplem
@serializable.view(SchemaVersion1Dot6)
@serializable.view(SchemaVersion1Dot7)
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, child_name='certificationLevel')
- @serializable.xml_sequence(5)
+ @serializable.xml_sequence(8)
def certification_levels(self) -> 'SortedSet[CryptoCertificationLevel]':
"""
The certification that the implementation of the cryptographic algorithm has received, if any. Certifications
@@ -478,7 +504,7 @@ def certification_levels(self, certification_levels: Iterable[CryptoCertificatio
self._certification_levels = SortedSet(certification_levels)
@property
- @serializable.xml_sequence(6)
+ @serializable.xml_sequence(9)
def mode(self) -> Optional[CryptoMode]:
"""
The mode of operation in which the cryptographic algorithm (block cipher) is used.
@@ -493,7 +519,7 @@ def mode(self, mode: Optional[CryptoMode]) -> None:
self._mode = mode
@property
- @serializable.xml_sequence(8)
+ @serializable.xml_sequence(10)
def padding(self) -> Optional[CryptoPadding]:
"""
The padding scheme that is used for the cryptographic algorithm.
@@ -509,7 +535,7 @@ def padding(self, padding: Optional[CryptoPadding]) -> None:
@property
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, child_name='cryptoFunction')
- @serializable.xml_sequence(9)
+ @serializable.xml_sequence(11)
def crypto_functions(self) -> 'SortedSet[CryptoFunction]':
"""
The cryptographic functions implemented by the cryptographic algorithm.
@@ -524,7 +550,7 @@ def crypto_functions(self, crypto_functions: Iterable[CryptoFunction]) -> None:
self._crypto_functions = SortedSet(crypto_functions)
@property
- @serializable.xml_sequence(10)
+ @serializable.xml_sequence(12)
def classical_security_level(self) -> Optional[int]:
"""
The classical security level that a cryptographic algorithm provides (in bits).
@@ -539,7 +565,7 @@ def classical_security_level(self, classical_security_level: Optional[int]) -> N
self._classical_security_level = classical_security_level
@property
- @serializable.xml_sequence(11)
+ @serializable.xml_sequence(13)
def nist_quantum_security_level(self) -> Optional[int]:
"""
The NIST security strength category as defined in
@@ -564,9 +590,10 @@ def nist_quantum_security_level(self, nist_quantum_security_level: Optional[int]
def __comparable_tuple(self) -> _ComparableTuple:
return _ComparableTuple((
- self.primitive, self._parameter_set_identifier, self.curve, self.execution_environment,
- self.implementation_platform, _ComparableTuple(self.certification_levels), self.mode, self.padding,
- _ComparableTuple(self.crypto_functions), self.classical_security_level, self.nist_quantum_security_level,
+ self.primitive, self.algorithm_family, self.parameter_set_identifier, self.curve, self.elliptic_curve,
+ self.execution_environment, self.implementation_platform, _ComparableTuple(self.certification_levels),
+ self.mode, self.padding, _ComparableTuple(self.crypto_functions), self.classical_security_level,
+ self.nist_quantum_security_level,
))
def __eq__(self, other: object) -> bool:
diff --git a/tests/_data/models.py b/tests/_data/models.py
index e2052878..81d469c7 100644
--- a/tests/_data/models.py
+++ b/tests/_data/models.py
@@ -171,8 +171,10 @@ def get_crypto_properties_algorithm() -> CryptoProperties:
asset_type=CryptoAssetType.ALGORITHM,
algorithm_properties=AlgorithmProperties(
primitive=CryptoPrimitive.KEM,
+ algorithm_family='ML-KEM',
parameter_set_identifier='a-parameter-set-id',
curve='9n8y2oxty3ao83n8qc2g2x3qcw4jt4wj',
+ elliptic_curve='nist/P-256',
execution_environment=CryptoExecutionEnvironment.SOFTWARE_PLAIN_RAM,
implementation_platform=CryptoImplementationPlatform.GENERIC,
certification_levels=[
diff --git a/tests/_data/snapshots/get_bom_v1_6_with_crypto_algorithm-1.7.json.bin b/tests/_data/snapshots/get_bom_v1_6_with_crypto_algorithm-1.7.json.bin
index 819b1821..bb9215d8 100644
--- a/tests/_data/snapshots/get_bom_v1_6_with_crypto_algorithm-1.7.json.bin
+++ b/tests/_data/snapshots/get_bom_v1_6_with_crypto_algorithm-1.7.json.bin
@@ -4,6 +4,7 @@
"bom-ref": "8182921e-0588-472e-b8f9-9c527c68f067",
"cryptoProperties": {
"algorithmProperties": {
+ "algorithmFamily": "ML-KEM",
"certificationLevel": [
"fips140-1-l1",
"fips140-2-l3",
@@ -15,6 +16,7 @@
"unknown"
],
"curve": "9n8y2oxty3ao83n8qc2g2x3qcw4jt4wj",
+ "ellipticCurve": "nist/P-256",
"executionEnvironment": "software-plain-ram",
"implementationPlatform": "generic",
"mode": "ecb",
diff --git a/tests/_data/snapshots/get_bom_v1_6_with_crypto_algorithm-1.7.xml.bin b/tests/_data/snapshots/get_bom_v1_6_with_crypto_algorithm-1.7.xml.bin
index a31cf67d..73db0a46 100644
--- a/tests/_data/snapshots/get_bom_v1_6_with_crypto_algorithm-1.7.xml.bin
+++ b/tests/_data/snapshots/get_bom_v1_6_with_crypto_algorithm-1.7.xml.bin
@@ -11,8 +11,10 @@
algorithm
kem
+ ML-KEM
a-parameter-set-id
9n8y2oxty3ao83n8qc2g2x3qcw4jt4wj
+ nist/P-256
software-plain-ram
generic
fips140-1-l1
diff --git a/tests/test_model_crypto.py b/tests/test_model_crypto.py
index 12265ee1..1ee394a1 100644
--- a/tests/test_model_crypto.py
+++ b/tests/test_model_crypto.py
@@ -15,6 +15,7 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.
+from json import loads as json_loads
from unittest import TestCase
from cyclonedx.model.bom_ref import BomRef
@@ -29,6 +30,7 @@
RelatedCryptoMaterialSecuredBy,
RelatedCryptoMaterialType,
)
+from cyclonedx.schema.schema import SchemaVersion1Dot6, SchemaVersion1Dot7
class TestModelAlgorithmProperties(TestCase):
@@ -134,3 +136,22 @@ def test_protocol_properties_sorting(self) -> None:
proto_list = [proto3, proto1, proto2]
sorted_protos = sorted(proto_list)
self.assertEqual(len(sorted_protos), 3)
+
+
+class TestModelAlgorithmPropertiesV17(TestCase):
+
+ def test_new_algorithm_identifiers_and_legacy_curve(self) -> None:
+ properties = AlgorithmProperties(
+ algorithm_family='AES',
+ curve='legacy-curve',
+ elliptic_curve='nist/P-256',
+ )
+ data_1_7 = json_loads(properties.as_json(view_=SchemaVersion1Dot7))
+ self.assertEqual('AES', data_1_7['algorithmFamily'])
+ self.assertEqual('nist/P-256', data_1_7['ellipticCurve'])
+ self.assertEqual('legacy-curve', data_1_7['curve'])
+
+ data_1_6 = json_loads(properties.as_json(view_=SchemaVersion1Dot6))
+ self.assertNotIn('algorithmFamily', data_1_6)
+ self.assertNotIn('ellipticCurve', data_1_6)
+ self.assertEqual('legacy-curve', data_1_6['curve'])