From 8b7b37aa23e666d066919a1b6ddb8803794a1553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicklas=20K=C3=B6rtge?= Date: Fri, 6 Sep 2024 10:37:31 +0200 Subject: [PATCH] Update CBOM generation (#138) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add keylength fix Signed-off-by: Nicklas Körtge * add ciphersuite to cbom Signed-off-by: Nicklas Körtge * update cipher sute tests Signed-off-by: Nicklas Körtge * update cipher sute tests Signed-off-by: Nicklas Körtge --------- Signed-off-by: Nicklas Körtge --- .../mapper/mapper/ssl/CipherSuiteMapper.java | 38 ++-- .../com/ibm/mapper/model/algorithms/DSA.java | 5 + .../com/ibm/mapper/model/algorithms/RSA.java | 4 +- .../collections/AbstractAssetCollection.java | 11 +- .../ibm/output/cyclondx/CBOMOutputFile.java | 38 +++- .../builder/ProtocolComponentBuilder.java | 78 ++++++-- .../main/java/com/ibm/output/util/Utils.java | 18 +- .../ibm/output/cyclonedx/AlgorithmTest.java | 4 +- .../com/ibm/output/cyclonedx/KeyTest.java | 42 ++++ .../ibm/output/cyclonedx/ProtocolTest.java | 185 ++++++++++++++++++ 10 files changed, 378 insertions(+), 45 deletions(-) diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/ssl/CipherSuiteMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/ssl/CipherSuiteMapper.java index e694fdaa..d87b61c7 100644 --- a/mapper/src/main/java/com/ibm/mapper/mapper/ssl/CipherSuiteMapper.java +++ b/mapper/src/main/java/com/ibm/mapper/mapper/ssl/CipherSuiteMapper.java @@ -25,6 +25,7 @@ import com.ibm.mapper.model.CipherSuite; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.Identifier; +import com.ibm.mapper.model.MessageDigest; import com.ibm.mapper.model.collections.AssetCollection; import com.ibm.mapper.model.collections.IdentifierCollection; import com.ibm.mapper.utils.DetectionLocation; @@ -49,7 +50,7 @@ public Optional parse( Optional possibleJsonCipherSuite = findCipherSuite(str); if (possibleJsonCipherSuite.isEmpty()) { // return a 'simple' cipher object - return Optional.empty(); // TODO + return Optional.of(new CipherSuite(str, detectionLocation)); } final JsonCipherSuite jsonCipherSuite = possibleJsonCipherSuite.get(); @@ -64,16 +65,6 @@ public Optional parse( return keyExchangeAlgorithmMapper.parse(algoStr, detectionLocation); }) .ifPresent(assets::add); - // authentication agreement - jsonCipherSuite - .getAuthAlgorithm() - .flatMap( - algoStr -> { - final AuthenticationAlgorithmMapper authenticationAlgorithmMapper = - new AuthenticationAlgorithmMapper(); - return authenticationAlgorithmMapper.parse(algoStr, detectionLocation); - }) - .ifPresent(assets::add); // encryption algorithm jsonCipherSuite .getEncAlgorithm() @@ -85,15 +76,30 @@ public Optional parse( }) .ifPresent(assets::add); // hash algorithm + final Optional hash = + jsonCipherSuite + .getHashAlgorithm() + .flatMap( + algoStr -> { + final HashAlgorithmMapper hashAlgorithmMapper = + new HashAlgorithmMapper(); + return hashAlgorithmMapper.parse(algoStr, detectionLocation); + }); + // authentication agreement jsonCipherSuite - .getHashAlgorithm() + .getAuthAlgorithm() .flatMap( algoStr -> { - final HashAlgorithmMapper hashAlgorithmMapper = - new HashAlgorithmMapper(); - return hashAlgorithmMapper.parse(algoStr, detectionLocation); + final AuthenticationAlgorithmMapper authenticationAlgorithmMapper = + new AuthenticationAlgorithmMapper(); + return authenticationAlgorithmMapper.parse(algoStr, detectionLocation); }) - .ifPresent(assets::add); + .ifPresentOrElse( + sign -> { + hash.ifPresent(sign::put); + assets.add(sign); + }, + () -> hash.ifPresent(assets::add)); final CipherSuite cipherSuite = new CipherSuite( diff --git a/mapper/src/main/java/com/ibm/mapper/model/algorithms/DSA.java b/mapper/src/main/java/com/ibm/mapper/model/algorithms/DSA.java index 74548fc7..68285501 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/algorithms/DSA.java +++ b/mapper/src/main/java/com/ibm/mapper/model/algorithms/DSA.java @@ -40,4 +40,9 @@ public DSA(@NotNull DetectionLocation detectionLocation) { super(NAME, Signature.class, detectionLocation); this.put(new Oid("1.2.840.10040.4.1", detectionLocation)); } + + public DSA(@NotNull MessageDigest messageDigest) { + this(messageDigest.getDetectionContext()); + this.put(messageDigest); + } } diff --git a/mapper/src/main/java/com/ibm/mapper/model/algorithms/RSA.java b/mapper/src/main/java/com/ibm/mapper/model/algorithms/RSA.java index 4f60f24f..89cebb25 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/algorithms/RSA.java +++ b/mapper/src/main/java/com/ibm/mapper/model/algorithms/RSA.java @@ -61,9 +61,9 @@ public RSA(@NotNull DetectionLocation detectionLocation) { this.put(new Oid(OID, detectionLocation)); } - public RSA(@Nonnull KeyLength keyLength, @Nonnull DetectionLocation detectionLocation) { + public RSA(int keyLength, @Nonnull DetectionLocation detectionLocation) { super(NAME, PublicKeyEncryption.class, detectionLocation); - this.put(keyLength); + this.put(new KeyLength(keyLength, detectionLocation)); this.put(new Oid(OID, detectionLocation)); } diff --git a/mapper/src/main/java/com/ibm/mapper/model/collections/AbstractAssetCollection.java b/mapper/src/main/java/com/ibm/mapper/model/collections/AbstractAssetCollection.java index 1992a1ef..421fd60b 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/collections/AbstractAssetCollection.java +++ b/mapper/src/main/java/com/ibm/mapper/model/collections/AbstractAssetCollection.java @@ -74,11 +74,14 @@ public Class getKind() { @NotNull @Override public String asString() { - final StringBuilder sb = new StringBuilder(); - for (K c : collection) { - sb.append(c.asString()).append(", "); + final StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < this.collection.size(); i++) { + sb.append(this.collection.get(i).asString()); + if (i != this.collection.size() - 1) { + sb.append(", "); + } } - return sb.toString(); + return sb.append("]").toString(); } @NotNull @Override diff --git a/output/src/main/java/com/ibm/output/cyclondx/CBOMOutputFile.java b/output/src/main/java/com/ibm/output/cyclondx/CBOMOutputFile.java index 2f399d2b..9b3878fc 100644 --- a/output/src/main/java/com/ibm/output/cyclondx/CBOMOutputFile.java +++ b/output/src/main/java/com/ibm/output/cyclondx/CBOMOutputFile.java @@ -21,6 +21,7 @@ import com.ibm.mapper.model.Algorithm; import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.CipherSuite; import com.ibm.mapper.model.DigestSize; import com.ibm.mapper.model.EllipticCurve; import com.ibm.mapper.model.IAsset; @@ -50,6 +51,7 @@ import com.ibm.mapper.model.functionality.Tag; import com.ibm.mapper.model.functionality.Verify; import com.ibm.mapper.model.padding.OAEP; +import com.ibm.mapper.model.protocol.TLS; import com.ibm.mapper.utils.DetectionLocation; import com.ibm.output.Constants; import com.ibm.output.IOutputFile; @@ -115,6 +117,8 @@ private void add(@Nullable final String parentBomRef, @Nonnull List nodes createKeyComponent(parentBomRef, key); } else if (node instanceof Protocol protocol) { createProtocolComponent(parentBomRef, protocol); + } else if (node instanceof CipherSuite cipherSuite) { + createCipherSuiteComponent(parentBomRef, cipherSuite); } else if (node instanceof SaltLength || node instanceof PasswordLength) { final IProperty property = (IProperty) node; createRelatedCryptoMaterialComponent(parentBomRef, property); @@ -124,7 +128,8 @@ private void add(@Nullable final String parentBomRef, @Nonnull List nodes }); } - private void createAlgorithmComponent(@Nullable String parentBomRef, @Nonnull Algorithm node) { + @Nullable private String createAlgorithmComponent( + @Nullable String parentBomRef, @Nonnull Algorithm node) { Map, INode> children = node.getChildren(); Component algorithm = AlgorithmComponentBuilder.create() @@ -157,22 +162,29 @@ private void createAlgorithmComponent(@Nullable String parentBomRef, @Nonnull Al .build(); final Optional optionalId = getIdentifierFunction().apply(algorithm); if (optionalId.isEmpty()) { - return; + return null; } addComponentAndDependencies(algorithm, optionalId.get(), parentBomRef, node); + return this.components.get(optionalId.get()).getBomRef(); } private void createKeyComponent(@Nullable String parentBomRef, @Nonnull Key node) { // if functionality nodes are placed under the key node, - // they will be moved under the corresponding primitive node + // they will be moved under the corresponding primitive node. Utils.pushNodesDownToFirstMatch(node, IPrimitive.getKinds(), Functionality.getKinds()); + // if a key length is defined under the key node, this function makes sure that the + // underlying primitive + // will get the same key length associated. + Utils.pushNodesDownToFirstMatch( + node, IPrimitive.getKinds(), List.of(KeyLength.class), false); + createRelatedCryptoMaterialComponent(parentBomRef, node); } private void createProtocolComponent(@Nullable String parentBomRef, @Nonnull Protocol node) { Map, INode> children = node.getChildren(); Component protocol = - ProtocolComponentBuilder.create() + ProtocolComponentBuilder.create(this::createAlgorithmComponent) .name(node) .type(node) .version(children.get(com.ibm.mapper.model.Version.class)) @@ -186,6 +198,24 @@ private void createProtocolComponent(@Nullable String parentBomRef, @Nonnull Pro addComponentAndDependencies(protocol, optionalId.get(), parentBomRef, node); } + private void createCipherSuiteComponent( + @Nullable String parentBomRef, @Nonnull CipherSuite node) { + final TLS tls = new TLS(node.getDetectionContext()); + Component protocol = + ProtocolComponentBuilder.create(this::createAlgorithmComponent) + .name(tls) + .type(tls) + .version(null) + .cipherSuites(new CipherSuiteCollection(List.of(node))) + .occurrences(createOccurrenceForm(node.getDetectionContext())) + .build(); + final Optional optionalId = getIdentifierFunction().apply(protocol); + if (optionalId.isEmpty()) { + return; + } + addComponentAndDependencies(protocol, optionalId.get(), parentBomRef, node); + } + private void createRelatedCryptoMaterialComponent( @Nullable String parentBomRef, @Nonnull INode node) { Map, INode> children = node.getChildren(); diff --git a/output/src/main/java/com/ibm/output/cyclondx/builder/ProtocolComponentBuilder.java b/output/src/main/java/com/ibm/output/cyclondx/builder/ProtocolComponentBuilder.java index 7b0c754f..e60eec6c 100644 --- a/output/src/main/java/com/ibm/output/cyclondx/builder/ProtocolComponentBuilder.java +++ b/output/src/main/java/com/ibm/output/cyclondx/builder/ProtocolComponentBuilder.java @@ -19,13 +19,17 @@ */ package com.ibm.output.cyclondx.builder; +import com.ibm.mapper.model.Algorithm; import com.ibm.mapper.model.CipherSuite; import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Identifier; import com.ibm.mapper.model.Protocol; import com.ibm.mapper.model.collections.CipherSuiteCollection; import com.ibm.mapper.model.protocol.TLS; +import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.function.BiFunction; import javax.annotation.Nonnull; import org.cyclonedx.model.Component; import org.cyclonedx.model.Evidence; @@ -41,42 +45,51 @@ public class ProtocolComponentBuilder implements IProtocolComponentBuilder { @Nonnull private final Component component; @Nonnull private final CryptoProperties cryptoProperties; @Nonnull private final ProtocolProperties protocolProperties; + @Nonnull private final BiFunction algorithmComponentBuilder; - protected ProtocolComponentBuilder() { + protected ProtocolComponentBuilder( + @Nonnull BiFunction algorithmComponentBuilder) { this.component = new Component(); this.cryptoProperties = new CryptoProperties(); this.protocolProperties = new ProtocolProperties(); + this.algorithmComponentBuilder = algorithmComponentBuilder; } private ProtocolComponentBuilder( @Nonnull Component component, @Nonnull CryptoProperties cryptoProperties, - @Nonnull ProtocolProperties protocolProperties) { + @Nonnull ProtocolProperties protocolProperties, + @Nonnull BiFunction algorithmComponentBuilder) { this.component = component; this.cryptoProperties = cryptoProperties; this.protocolProperties = protocolProperties; + this.algorithmComponentBuilder = algorithmComponentBuilder; } @Nonnull - public static IProtocolComponentBuilder create() { - return new ProtocolComponentBuilder(); + public static IProtocolComponentBuilder create( + @Nonnull BiFunction algorithmComponentBuilder) { + return new ProtocolComponentBuilder(algorithmComponentBuilder); } @NotNull @Override public IProtocolComponentBuilder name(@Nullable Protocol name) { if (name == null) { - return new ProtocolComponentBuilder(component, cryptoProperties, protocolProperties); + return new ProtocolComponentBuilder( + component, cryptoProperties, protocolProperties, algorithmComponentBuilder); } this.component.setName(name.asString()); - return new ProtocolComponentBuilder(component, cryptoProperties, protocolProperties); + return new ProtocolComponentBuilder( + component, cryptoProperties, protocolProperties, algorithmComponentBuilder); } @NotNull @Override public IProtocolComponentBuilder type(@Nullable Protocol type) { if (type == null) { protocolProperties.setType(ProtocolType.UNKNOWN); - return new ProtocolComponentBuilder(component, cryptoProperties, protocolProperties); + return new ProtocolComponentBuilder( + component, cryptoProperties, protocolProperties, algorithmComponentBuilder); } if (type instanceof TLS) { @@ -85,46 +98,83 @@ public IProtocolComponentBuilder type(@Nullable Protocol type) { protocolProperties.setType(ProtocolType.OTHER); } - return new ProtocolComponentBuilder(component, cryptoProperties, protocolProperties); + return new ProtocolComponentBuilder( + component, cryptoProperties, protocolProperties, algorithmComponentBuilder); } @NotNull @Override public IProtocolComponentBuilder version(@Nullable INode version) { if (version == null) { - return new ProtocolComponentBuilder(component, cryptoProperties, protocolProperties); + return new ProtocolComponentBuilder( + component, cryptoProperties, protocolProperties, algorithmComponentBuilder); } protocolProperties.setVersion(version.asString()); - return new ProtocolComponentBuilder(component, cryptoProperties, protocolProperties); + return new ProtocolComponentBuilder( + component, cryptoProperties, protocolProperties, algorithmComponentBuilder); } @NotNull @Override public IProtocolComponentBuilder cipherSuites(@Nullable INode node) { if (node == null) { - return new ProtocolComponentBuilder(component, cryptoProperties, protocolProperties); + return new ProtocolComponentBuilder( + component, cryptoProperties, protocolProperties, algorithmComponentBuilder); } if (node instanceof CipherSuiteCollection cipherSuiteCollection) { + List suites = new ArrayList<>(); for (CipherSuite cipherSuite : cipherSuiteCollection.getCollection()) { final org.cyclonedx.model.component.crypto.CipherSuite suite = new org.cyclonedx.model.component.crypto.CipherSuite(); // name suite.setName(cipherSuite.getName()); // algorithms + cipherSuite + .getAssetCollection() + .ifPresent( + assetCollection -> { + final List algorithmRefs = new ArrayList<>(); + for (final INode asset : assetCollection.getCollection()) { + if (asset instanceof Algorithm algorithm) { + final String ref = + this.algorithmComponentBuilder.apply( + "", algorithm); + algorithmRefs.add(ref); + } + } + suite.setAlgorithms(algorithmRefs); + }); + // identifiers + cipherSuite + .getIdentifierCollection() + .ifPresent( + identifierCollection -> { + final List identifiers = new ArrayList<>(); + for (final Identifier identifier : + identifierCollection.getCollection()) { + identifiers.add(identifier.getValue()); + } + suite.setIdentifiers(identifiers); + }); + suites.add(suite); } + protocolProperties.setCipherSuites(suites); } - return new ProtocolComponentBuilder(component, cryptoProperties, protocolProperties); + return new ProtocolComponentBuilder( + component, cryptoProperties, protocolProperties, algorithmComponentBuilder); } @NotNull @Override public IProtocolComponentBuilder occurrences(@Nullable Occurrence... occurrences) { if (occurrences == null) { - return new ProtocolComponentBuilder(component, cryptoProperties, protocolProperties); + return new ProtocolComponentBuilder( + component, cryptoProperties, protocolProperties, algorithmComponentBuilder); } final Evidence evidence = new Evidence(); evidence.setOccurrences(List.of(occurrences)); this.component.setEvidence(evidence); - return new ProtocolComponentBuilder(component, cryptoProperties, protocolProperties); + return new ProtocolComponentBuilder( + component, cryptoProperties, protocolProperties, algorithmComponentBuilder); } @NotNull @Override diff --git a/output/src/main/java/com/ibm/output/util/Utils.java b/output/src/main/java/com/ibm/output/util/Utils.java index 7e96e87b..729e4727 100644 --- a/output/src/main/java/com/ibm/output/util/Utils.java +++ b/output/src/main/java/com/ibm/output/util/Utils.java @@ -51,9 +51,18 @@ public static void pushNodesDownToFirstMatch( @Nonnull INode root, @Nonnull List> pushToNodeOfFirstMatchClazz, @Nonnull List> kindsOfNodesToPushDown) { + Utils.pushNodesDownToFirstMatch( + root, pushToNodeOfFirstMatchClazz, kindsOfNodesToPushDown, true); + } + + public static void pushNodesDownToFirstMatch( + @Nonnull INode root, + @Nonnull List> pushToNodeOfFirstMatchClazz, + @Nonnull List> kindsOfNodesToPushDown, + boolean remove) { for (Class kind : pushToNodeOfFirstMatchClazz) { if (root.hasChildOfType(kind).isPresent()) { - pushNodesDown(root, kind, kindsOfNodesToPushDown); + pushNodesDown(root, kind, kindsOfNodesToPushDown, remove); return; } } @@ -62,7 +71,8 @@ public static void pushNodesDownToFirstMatch( public static void pushNodesDown( @Nonnull INode root, @Nonnull Class pushToNodeOfClazz, - @Nonnull List> kindsOfNodesToPushDown) { + @Nonnull List> kindsOfNodesToPushDown, + boolean remove) { final Optional possiblePushToNode = root.hasChildOfType(pushToNodeOfClazz); if (possiblePushToNode.isEmpty()) { return; @@ -74,7 +84,9 @@ public static void pushNodesDown( .ifPresent( n -> { pushToNode.put(n); - root.removeChildOfType(kind); + if (remove) { + root.removeChildOfType(kind); + } }); } } diff --git a/output/src/test/java/com/ibm/output/cyclonedx/AlgorithmTest.java b/output/src/test/java/com/ibm/output/cyclonedx/AlgorithmTest.java index 83de5436..6db29f2b 100644 --- a/output/src/test/java/com/ibm/output/cyclonedx/AlgorithmTest.java +++ b/output/src/test/java/com/ibm/output/cyclonedx/AlgorithmTest.java @@ -187,7 +187,7 @@ void signature() { bom -> { assertThat(bom.getComponents()).hasSize(3); assertThat(bom.getComponents().stream().map(Component::getName)) - .contains("DSAwithSHA256", "SHA256"); + .contains("SHA256withDSA", "SHA256"); for (Component component : bom.getComponents()) { asserts(component.getEvidence()); @@ -209,7 +209,7 @@ void signature() { final AlgorithmProperties algorithmProperties = cryptoProperties.getAlgorithmProperties(); if (algorithmProperties.getPrimitive().equals(Primitive.SIGNATURE)) { - assertThat(component.getName()).isEqualTo("DSAwithSHA256"); + assertThat(component.getName()).isEqualTo("SHA256withDSA"); assertThat(cryptoProperties.getOid()) .isEqualTo("2.16.840.1.101.3.4.3.2"); assertThat(algorithmProperties.getCryptoFunctions()) diff --git a/output/src/test/java/com/ibm/output/cyclonedx/KeyTest.java b/output/src/test/java/com/ibm/output/cyclonedx/KeyTest.java index 7cf8c0c8..1dadbee2 100644 --- a/output/src/test/java/com/ibm/output/cyclonedx/KeyTest.java +++ b/output/src/test/java/com/ibm/output/cyclonedx/KeyTest.java @@ -22,6 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.ibm.mapper.model.AuthenticatedEncryption; +import com.ibm.mapper.model.KeyLength; import com.ibm.mapper.model.Oid; import com.ibm.mapper.model.PublicKey; import com.ibm.mapper.model.PublicKeyEncryption; @@ -130,4 +131,45 @@ void secretKey() { } }); } + + @Test + void updateKeySizeInAlgorithm() { + this.assertsNode( + () -> { + final RSA rsa = new RSA(2048, detectionLocation); + final PublicKey publicKey = new PublicKey((PublicKeyEncryption) rsa); + publicKey.put(new KeyLength(4096, detectionLocation)); + return publicKey; + }, + bom -> { + assertThat(bom.getComponents()).hasSize(2); + for (Component component : bom.getComponents()) { + asserts(component.getEvidence()); + assertThat(component.getCryptoProperties()).isNotNull(); + final CryptoProperties cryptoProperties = component.getCryptoProperties(); + + if (cryptoProperties.getAssetType().equals(AssetType.ALGORITHM)) { + assertThat(component.getName()).isEqualTo("RSA-4096"); + assertThat(cryptoProperties.getAlgorithmProperties()).isNotNull(); + final AlgorithmProperties algorithmProperties = + cryptoProperties.getAlgorithmProperties(); + assertThat(algorithmProperties.getPrimitive()).isEqualTo(Primitive.PKE); + assertThat(algorithmProperties.getParameterSetIdentifier()) + .isEqualTo("4096"); + assertThat(cryptoProperties.getOid()).isEqualTo("1.2.840.113549.1.1.1"); + } else if (cryptoProperties + .getAssetType() + .equals(AssetType.RELATED_CRYPTO_MATERIAL)) { + assertThat(cryptoProperties.getRelatedCryptoMaterialProperties()) + .isNotNull(); + final RelatedCryptoMaterialProperties relatedCryptoMaterialProperties = + cryptoProperties.getRelatedCryptoMaterialProperties(); + assertThat(relatedCryptoMaterialProperties.getType()) + .isEqualTo(RelatedCryptoMaterialType.PUBLIC_KEY); + } else { + throw new AssertionError(); + } + } + }); + } } diff --git a/output/src/test/java/com/ibm/output/cyclonedx/ProtocolTest.java b/output/src/test/java/com/ibm/output/cyclonedx/ProtocolTest.java index 2ff9413a..355913de 100644 --- a/output/src/test/java/com/ibm/output/cyclonedx/ProtocolTest.java +++ b/output/src/test/java/com/ibm/output/cyclonedx/ProtocolTest.java @@ -21,9 +21,28 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.ibm.mapper.model.CipherSuite; +import com.ibm.mapper.model.Identifier; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; import com.ibm.mapper.model.Version; +import com.ibm.mapper.model.algorithms.AES; +import com.ibm.mapper.model.algorithms.DH; +import com.ibm.mapper.model.algorithms.DSA; +import com.ibm.mapper.model.algorithms.SHA2; +import com.ibm.mapper.model.collections.AssetCollection; +import com.ibm.mapper.model.collections.CipherSuiteCollection; +import com.ibm.mapper.model.collections.IdentifierCollection; +import com.ibm.mapper.model.mode.CBC; import com.ibm.mapper.model.protocol.TLS; +import java.util.List; +import org.cyclonedx.model.Component; +import org.cyclonedx.model.component.crypto.AlgorithmProperties; import org.cyclonedx.model.component.crypto.CryptoProperties; +import org.cyclonedx.model.component.crypto.ProtocolProperties; +import org.cyclonedx.model.component.crypto.enums.AssetType; +import org.cyclonedx.model.component.crypto.enums.Mode; +import org.cyclonedx.model.component.crypto.enums.Primitive; import org.cyclonedx.model.component.crypto.enums.ProtocolType; import org.junit.jupiter.api.Test; @@ -50,4 +69,170 @@ void base() { }); }); } + + @Test + void cipherSuite() { + this.assertsNode( + () -> { + final CipherSuite cipherSuite = + new CipherSuite( + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", detectionLocation); + final DH dh = new DH(KeyAgreement.class, detectionLocation); + final AES aes = new AES(256, new CBC(detectionLocation), detectionLocation); + final DSA dsa = new DSA(new SHA2(256, detectionLocation)); + dsa.put(new Oid("2.16.840.1.101.3.4.3.2", detectionLocation)); + final AssetCollection assetCollection = + new AssetCollection(List.of(dh, aes, dsa)); + final IdentifierCollection identifierCollection = + new IdentifierCollection( + List.of( + new Identifier("0x00", detectionLocation), + new Identifier("0x6A", detectionLocation))); + cipherSuite.put(assetCollection); + cipherSuite.put(identifierCollection); + return cipherSuite; + }, + bom -> { + assertThat(bom.getComponents()).hasSize(5); + assertThat(bom.getComponents().stream().map(Component::getName)) + .contains("SHA256withDSA", "SHA256", "AES256-CBC", "TLS", "DH"); + + for (Component component : bom.getComponents()) { + asserts(component.getEvidence()); + assertThat(component.getCryptoProperties()).isNotNull(); + final CryptoProperties cryptoProperties = component.getCryptoProperties(); + + if (cryptoProperties.getAssetType().equals(AssetType.ALGORITHM)) { + assertThat(cryptoProperties.getAlgorithmProperties()).isNotNull(); + final AlgorithmProperties algorithmProperties = + cryptoProperties.getAlgorithmProperties(); + if (algorithmProperties.getPrimitive().equals(Primitive.SIGNATURE)) { + assertThat(component.getName()).isEqualTo("SHA256withDSA"); + assertThat(cryptoProperties.getOid()) + .isEqualTo("2.16.840.1.101.3.4.3.2"); + } else if (algorithmProperties.getPrimitive().equals(Primitive.HASH)) { + assertThat(component.getName()).isEqualTo("SHA256"); + assertThat(algorithmProperties.getParameterSetIdentifier()) + .isEqualTo("256"); + } else if (algorithmProperties + .getPrimitive() + .equals(Primitive.KEY_AGREE)) { + assertThat(component.getName()).isEqualTo("DH"); + assertThat(cryptoProperties.getOid()) + .isEqualTo("1.2.840.113549.1.3.1"); + } else if (algorithmProperties + .getPrimitive() + .equals(Primitive.BLOCK_CIPHER)) { + assertThat(component.getName()).isEqualTo("AES256-CBC"); + assertThat(algorithmProperties.getMode()).isEqualTo(Mode.CBC); + assertThat(algorithmProperties.getParameterSetIdentifier()) + .isEqualTo("256"); + } else { + throw new AssertionError(); + } + } else if (cryptoProperties.getAssetType().equals(AssetType.PROTOCOL)) { + assertThat(cryptoProperties.getProtocolProperties()).isNotNull(); + final ProtocolProperties protocolProperties = + cryptoProperties.getProtocolProperties(); + assertThat(protocolProperties.getType()).isEqualTo(ProtocolType.TLS); + assertThat(protocolProperties.getVersion()).isNull(); + assertThat(protocolProperties.getCipherSuites()).isNotNull(); + assertThat(protocolProperties.getCipherSuites()).hasSize(1); + + final org.cyclonedx.model.component.crypto.CipherSuite cipherSuite = + protocolProperties.getCipherSuites().get(0); + assertThat(cipherSuite.getName()) + .isEqualTo("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"); + + assertThat(cipherSuite.getAlgorithms()).hasSize(3); + assertThat(cipherSuite.getIdentifiers()).contains("0x00", "0x6A"); + } + } + }); + } + + @Test + void protocolWithCipherSuite() { + this.assertsNode( + () -> { + final TLS tls = new TLS(new Version("1.3", detectionLocation)); + + final CipherSuite cipherSuite = + new CipherSuite( + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", detectionLocation); + final DH dh = new DH(KeyAgreement.class, detectionLocation); + final AES aes = new AES(256, new CBC(detectionLocation), detectionLocation); + final DSA dsa = new DSA(new SHA2(256, detectionLocation)); + dsa.put(new Oid("2.16.840.1.101.3.4.3.2", detectionLocation)); + final AssetCollection assetCollection = + new AssetCollection(List.of(dh, aes, dsa)); + final IdentifierCollection identifierCollection = + new IdentifierCollection( + List.of( + new Identifier("0x00", detectionLocation), + new Identifier("0x6A", detectionLocation))); + cipherSuite.put(assetCollection); + cipherSuite.put(identifierCollection); + + tls.put(new CipherSuiteCollection(List.of(cipherSuite))); + return tls; + }, + bom -> { + assertThat(bom.getComponents()).hasSize(5); + assertThat(bom.getComponents().stream().map(Component::getName)) + .contains("SHA256withDSA", "SHA256", "AES256-CBC", "TLSv1.3", "DH"); + + for (Component component : bom.getComponents()) { + asserts(component.getEvidence()); + assertThat(component.getCryptoProperties()).isNotNull(); + final CryptoProperties cryptoProperties = component.getCryptoProperties(); + + if (cryptoProperties.getAssetType().equals(AssetType.ALGORITHM)) { + assertThat(cryptoProperties.getAlgorithmProperties()).isNotNull(); + final AlgorithmProperties algorithmProperties = + cryptoProperties.getAlgorithmProperties(); + if (algorithmProperties.getPrimitive().equals(Primitive.SIGNATURE)) { + assertThat(component.getName()).isEqualTo("SHA256withDSA"); + assertThat(cryptoProperties.getOid()) + .isEqualTo("2.16.840.1.101.3.4.3.2"); + } else if (algorithmProperties.getPrimitive().equals(Primitive.HASH)) { + assertThat(component.getName()).isEqualTo("SHA256"); + assertThat(algorithmProperties.getParameterSetIdentifier()) + .isEqualTo("256"); + } else if (algorithmProperties + .getPrimitive() + .equals(Primitive.KEY_AGREE)) { + assertThat(component.getName()).isEqualTo("DH"); + assertThat(cryptoProperties.getOid()) + .isEqualTo("1.2.840.113549.1.3.1"); + } else if (algorithmProperties + .getPrimitive() + .equals(Primitive.BLOCK_CIPHER)) { + assertThat(component.getName()).isEqualTo("AES256-CBC"); + assertThat(algorithmProperties.getMode()).isEqualTo(Mode.CBC); + assertThat(algorithmProperties.getParameterSetIdentifier()) + .isEqualTo("256"); + } else { + throw new AssertionError(); + } + } else if (cryptoProperties.getAssetType().equals(AssetType.PROTOCOL)) { + assertThat(cryptoProperties.getProtocolProperties()).isNotNull(); + final ProtocolProperties protocolProperties = + cryptoProperties.getProtocolProperties(); + assertThat(protocolProperties.getType()).isEqualTo(ProtocolType.TLS); + assertThat(protocolProperties.getVersion()).isEqualTo("1.3"); + assertThat(protocolProperties.getCipherSuites()).isNotNull(); + assertThat(protocolProperties.getCipherSuites()).hasSize(1); + + final org.cyclonedx.model.component.crypto.CipherSuite cipherSuite = + protocolProperties.getCipherSuites().get(0); + assertThat(cipherSuite.getName()) + .isEqualTo("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"); + + assertThat(cipherSuite.getAlgorithms()).hasSize(3); + assertThat(cipherSuite.getIdentifiers()).contains("0x00", "0x6A"); + } + } + }); + } }