diff --git a/engine/src/main/java/com/ibm/engine/utils/Utils.java b/engine/src/main/java/com/ibm/engine/utils/Utils.java index e3ac33f17..50af9e565 100644 --- a/engine/src/main/java/com/ibm/engine/utils/Utils.java +++ b/engine/src/main/java/com/ibm/engine/utils/Utils.java @@ -118,7 +118,11 @@ public static Optional bitSizeValueToBitSizeInteger( if (resolvedValue.value() instanceof Integer i) { return Optional.of(i); } else if (resolvedValue.value() instanceof String str) { - return Optional.of(Utils.byteSizeToBitSize(str.getBytes().length)); + try { + return Optional.of(Integer.parseInt(str)); + } catch (NumberFormatException e) { + return Optional.of(Utils.byteSizeToBitSize(str.getBytes().length)); + } } else { return Optional.empty(); } diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCipherMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCipherMapper.java index 5461d068d..6098f1b16 100644 --- a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCipherMapper.java +++ b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCipherMapper.java @@ -26,12 +26,15 @@ import com.ibm.mapper.model.algorithms.Camellia; import com.ibm.mapper.model.algorithms.ChaCha20; import com.ibm.mapper.model.algorithms.ChaCha20Poly1305; +import com.ibm.mapper.model.algorithms.DES; import com.ibm.mapper.model.algorithms.Fernet; import com.ibm.mapper.model.algorithms.IDEA; +import com.ibm.mapper.model.algorithms.RC2; import com.ibm.mapper.model.algorithms.RC4; import com.ibm.mapper.model.algorithms.RSA; import com.ibm.mapper.model.algorithms.SEED; import com.ibm.mapper.model.algorithms.SM4; +import com.ibm.mapper.model.algorithms.Salsa20; import com.ibm.mapper.model.algorithms.TripleDES; import com.ibm.mapper.model.algorithms.cast.CAST128; import com.ibm.mapper.utils.DetectionLocation; @@ -53,17 +56,21 @@ public final class PycaCipherMapper implements IMapper { case "AES128" -> Optional.of(new AES(128, detectionLocation)); case "AES256" -> Optional.of(new AES(256, detectionLocation)); case "CAMELLIA" -> Optional.of(new Camellia(detectionLocation)); - case "TRIPLEDES" -> Optional.of(new TripleDES(detectionLocation)); + case "TRIPLEDES", "3DES" -> Optional.of(new TripleDES(detectionLocation)); + case "DES" -> Optional.of(new DES(detectionLocation)); case "CAST5" -> Optional.of(new CAST128(detectionLocation)); case "SEED" -> Optional.of(new SEED(detectionLocation)); case "SM4" -> Optional.of(new SM4(detectionLocation)); case "BLOWFISH" -> Optional.of(new Blowfish(detectionLocation)); case "IDEA" -> Optional.of(new IDEA(detectionLocation)); case "CHACHA20" -> Optional.of(new ChaCha20(detectionLocation)); - case "ARC4" -> Optional.of(new RC4(detectionLocation)); + case "SALSA20" -> Optional.of(new Salsa20(detectionLocation)); + case "ARC4", "RC4" -> Optional.of(new RC4(detectionLocation)); + case "RC2" -> Optional.of(new RC2(detectionLocation)); case "FERNET" -> Optional.of(new Fernet(detectionLocation)); case "RSA" -> Optional.of(new RSA(detectionLocation)); - case "CHACHA20POLY1305" -> Optional.of(new ChaCha20Poly1305(detectionLocation)); + case "CHACHA20POLY1305", "CHACHA20_POLY1305" -> + Optional.of(new ChaCha20Poly1305(detectionLocation)); default -> Optional.empty(); }; } diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCurveMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCurveMapper.java new file mode 100644 index 000000000..26b1229ff --- /dev/null +++ b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaCurveMapper.java @@ -0,0 +1,109 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.mapper.mapper.pyca; + +import com.ibm.mapper.mapper.IMapper; +import com.ibm.mapper.model.Algorithm; +import com.ibm.mapper.model.EllipticCurveAlgorithm; +import com.ibm.mapper.model.algorithms.Ed25519; +import com.ibm.mapper.model.algorithms.Ed448; +import com.ibm.mapper.model.algorithms.X25519; +import com.ibm.mapper.model.algorithms.X448; +import com.ibm.mapper.model.curves.Brainpoolp256r1; +import com.ibm.mapper.model.curves.Brainpoolp384r1; +import com.ibm.mapper.model.curves.Brainpoolp512r1; +import com.ibm.mapper.model.curves.Secp192r1; +import com.ibm.mapper.model.curves.Secp224r1; +import com.ibm.mapper.model.curves.Secp256k1; +import com.ibm.mapper.model.curves.Secp256r1; +import com.ibm.mapper.model.curves.Secp384r1; +import com.ibm.mapper.model.curves.Secp521r1; +import com.ibm.mapper.model.curves.Sect163k1; +import com.ibm.mapper.model.curves.Sect163r2; +import com.ibm.mapper.model.curves.Sect233k1; +import com.ibm.mapper.model.curves.Sect233r1; +import com.ibm.mapper.model.curves.Sect283k1; +import com.ibm.mapper.model.curves.Sect283r1; +import com.ibm.mapper.model.curves.Sect409k1; +import com.ibm.mapper.model.curves.Sect409r1; +import com.ibm.mapper.model.curves.Sect571k1; +import com.ibm.mapper.model.curves.Sect571r1; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public final class PycaCurveMapper implements IMapper { + + @Nonnull + @Override + public Optional parse( + @Nullable String str, @Nonnull DetectionLocation detectionLocation) { + if (str == null) { + return Optional.empty(); + } + + return switch (str.toUpperCase().trim()) { + case "SECP192R1", "PRIME192V1", "P-192", "P192", "NIST P-192" -> + Optional.of(new EllipticCurveAlgorithm(new Secp192r1(detectionLocation))); + case "SECP224R1", "PRIME224V1", "P-224", "P224", "NIST P-224" -> + Optional.of(new EllipticCurveAlgorithm(new Secp224r1(detectionLocation))); + case "SECP256R1", "PRIME256V1", "P-256", "P256", "NIST P-256" -> + Optional.of(new EllipticCurveAlgorithm(new Secp256r1(detectionLocation))); + case "SECP384R1", "PRIME384V1", "P-384", "P384", "NIST P-384" -> + Optional.of(new EllipticCurveAlgorithm(new Secp384r1(detectionLocation))); + case "SECP521R1", "PRIME521V1", "P-521", "P521", "NIST P-521" -> + Optional.of(new EllipticCurveAlgorithm(new Secp521r1(detectionLocation))); + case "SECP256K1" -> + Optional.of(new EllipticCurveAlgorithm(new Secp256k1(detectionLocation))); + case "CURVE25519" -> Optional.of(new X25519(detectionLocation)); + case "ED25519" -> Optional.of(new Ed25519(detectionLocation)); + case "CURVE448" -> Optional.of(new X448(detectionLocation)); + case "ED448" -> Optional.of(new Ed448(detectionLocation)); + case "BRAINPOOLP256R1" -> + Optional.of(new EllipticCurveAlgorithm(new Brainpoolp256r1(detectionLocation))); + case "BRAINPOOLP384R1" -> + Optional.of(new EllipticCurveAlgorithm(new Brainpoolp384r1(detectionLocation))); + case "BRAINPOOLP512R1" -> + Optional.of(new EllipticCurveAlgorithm(new Brainpoolp512r1(detectionLocation))); + case "SECT571K1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect571k1(detectionLocation))); + case "SECT409K1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect409k1(detectionLocation))); + case "SECT283K1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect283k1(detectionLocation))); + case "SECT233K1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect233k1(detectionLocation))); + case "SECT163K1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect163k1(detectionLocation))); + case "SECT571R1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect571r1(detectionLocation))); + case "SECT409R1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect409r1(detectionLocation))); + case "SECT283R1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect283r1(detectionLocation))); + case "SECT233R1" -> + Optional.of(new EllipticCurveAlgorithm(new Sect233r1(detectionLocation))); + case "SECT163R2" -> + Optional.of(new EllipticCurveAlgorithm(new Sect163r2(detectionLocation))); + default -> Optional.empty(); + }; + } +} diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaDigestMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaDigestMapper.java index 8e2a1ca3e..5ab5b40e2 100644 --- a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaDigestMapper.java +++ b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaDigestMapper.java @@ -21,14 +21,20 @@ import com.ibm.mapper.mapper.IMapper; import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.algorithms.KangarooTwelve; +import com.ibm.mapper.model.algorithms.Keccak; +import com.ibm.mapper.model.algorithms.MD2; +import com.ibm.mapper.model.algorithms.MD4; import com.ibm.mapper.model.algorithms.MD5; -import com.ibm.mapper.model.algorithms.Poly1305; +import com.ibm.mapper.model.algorithms.RIPEMD; import com.ibm.mapper.model.algorithms.SHA; import com.ibm.mapper.model.algorithms.SHA2; import com.ibm.mapper.model.algorithms.SHA3; import com.ibm.mapper.model.algorithms.SM3; +import com.ibm.mapper.model.algorithms.TupleHash; import com.ibm.mapper.model.algorithms.blake.BLAKE2b; import com.ibm.mapper.model.algorithms.blake.BLAKE2s; +import com.ibm.mapper.model.algorithms.shake.CSHAKE; import com.ibm.mapper.model.algorithms.shake.SHAKE; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -59,11 +65,19 @@ public final class PycaDigestMapper implements IMapper { case "SHA3_512" -> Optional.of(new SHA3(512, detectionLocation)); case "SHAKE128" -> Optional.of(new SHAKE(128, detectionLocation)); case "SHAKE256" -> Optional.of(new SHAKE(256, detectionLocation)); + case "MD2" -> Optional.of(new MD2(detectionLocation)); + case "MD4" -> Optional.of(new MD4(detectionLocation)); case "MD5" -> Optional.of(new MD5(detectionLocation)); case "BLAKE2B" -> Optional.of(new BLAKE2b(false, detectionLocation)); case "BLAKE2S" -> Optional.of(new BLAKE2s(false, detectionLocation)); case "SM3" -> Optional.of(new SM3(detectionLocation)); - case "POLY1305" -> Optional.of(new Poly1305(detectionLocation)); + case "RIPEMD160" -> Optional.of(new RIPEMD(160, detectionLocation)); + case "TUPLEHASH128" -> Optional.of(new TupleHash(128, detectionLocation)); + case "TUPLEHASH256" -> Optional.of(new TupleHash(256, detectionLocation)); + case "KECCAK" -> Optional.of(new Keccak(detectionLocation)); + case "CSHAKE128" -> Optional.of(new CSHAKE(128, detectionLocation)); + case "CSHAKE256" -> Optional.of(new CSHAKE(256, detectionLocation)); + case "KANGAROOTWELVE" -> Optional.of(new KangarooTwelve(detectionLocation)); default -> Optional.empty(); }; } diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaKeyBasedAlgorithmMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaKeyBasedAlgorithmMapper.java new file mode 100644 index 000000000..315ae9286 --- /dev/null +++ b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaKeyBasedAlgorithmMapper.java @@ -0,0 +1,64 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.mapper.mapper.pyca; + +import com.ibm.mapper.mapper.IMapper; +import com.ibm.mapper.model.Algorithm; +import com.ibm.mapper.model.EllipticCurveAlgorithm; +import com.ibm.mapper.model.algorithms.DH; +import com.ibm.mapper.model.algorithms.DSA; +import com.ibm.mapper.model.algorithms.Ed25519; +import com.ibm.mapper.model.algorithms.Ed448; +import com.ibm.mapper.model.algorithms.ElGamal; +import com.ibm.mapper.model.algorithms.Fernet; +import com.ibm.mapper.model.algorithms.RSA; +import com.ibm.mapper.model.curves.Curve25519; +import com.ibm.mapper.model.curves.Curve448; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public final class PycaKeyBasedAlgorithmMapper implements IMapper { + + @Override + public @Nonnull Optional parse( + @Nullable String str, @Nonnull DetectionLocation detectionLocation) { + if (str == null) { + return Optional.empty(); + } + + return switch (str.toUpperCase().trim()) { + case "RSA" -> Optional.of(new RSA(detectionLocation)); + case "DSA" -> Optional.of(new DSA(detectionLocation)); + case "DH" -> Optional.of(new DH(detectionLocation)); + case "EC" -> Optional.of(new EllipticCurveAlgorithm(detectionLocation)); + case "CURVE25519" -> + Optional.of(new EllipticCurveAlgorithm(new Curve25519(detectionLocation))); + case "CURVE448" -> + Optional.of(new EllipticCurveAlgorithm(new Curve448(detectionLocation))); + case "ED25519" -> Optional.of(new Ed25519(detectionLocation)); + case "ED448" -> Optional.of(new Ed448(detectionLocation)); + case "ELGAMAL" -> Optional.of(new ElGamal(detectionLocation)); + case "FERNET" -> Optional.of(new Fernet(detectionLocation)); + default -> Optional.empty(); + }; + } +} diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaMacMapper.java b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaMacMapper.java index 777eca70b..962774180 100644 --- a/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaMacMapper.java +++ b/mapper/src/main/java/com/ibm/mapper/mapper/pyca/PycaMacMapper.java @@ -28,9 +28,12 @@ import com.ibm.mapper.model.algorithms.ChaCha20; import com.ibm.mapper.model.algorithms.Fernet; import com.ibm.mapper.model.algorithms.IDEA; +import com.ibm.mapper.model.algorithms.KMAC; +import com.ibm.mapper.model.algorithms.MD2; import com.ibm.mapper.model.algorithms.MD5; import com.ibm.mapper.model.algorithms.Poly1305; import com.ibm.mapper.model.algorithms.RC4; +import com.ibm.mapper.model.algorithms.RIPEMD; import com.ibm.mapper.model.algorithms.RSA; import com.ibm.mapper.model.algorithms.SEED; import com.ibm.mapper.model.algorithms.SHA; @@ -42,6 +45,7 @@ import com.ibm.mapper.model.algorithms.blake.BLAKE2b; import com.ibm.mapper.model.algorithms.blake.BLAKE2s; import com.ibm.mapper.model.algorithms.cast.CAST128; +import com.ibm.mapper.model.algorithms.shake.CSHAKE; import com.ibm.mapper.model.algorithms.shake.SHAKE; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -104,13 +108,22 @@ public class PycaMacMapper implements IMapper { case "SHAKE128" -> Optional.of(new SHAKE(Mac.class, new SHAKE(128, detectionLocation))); case "SHAKE256" -> Optional.of(new SHAKE(Mac.class, new SHAKE(256, detectionLocation))); case "MD5" -> Optional.of(new MD5(Mac.class, detectionLocation)); + case "MD2" -> Optional.of(new MD2(Mac.class, detectionLocation)); case "BLAKE2B" -> Optional.of(new BLAKE2b(Mac.class, new BLAKE2b(false, detectionLocation))); case "BLAKE2S" -> Optional.of(new BLAKE2s(Mac.class, new BLAKE2s(false, detectionLocation))); case "SM3" -> Optional.of(new SM3(Mac.class, new SM3(detectionLocation))); + case "KMAC128" -> Optional.of(new KMAC(Mac.class, new KMAC(128, detectionLocation))); + case "KMAC256" -> Optional.of(new KMAC(Mac.class, new KMAC(256, detectionLocation))); case "POLY1305" -> Optional.of(new Poly1305(Mac.class, new Poly1305(detectionLocation))); + case "RIPEMD160" -> + Optional.of(new RIPEMD(Mac.class, new RIPEMD(160, detectionLocation))); + case "CSHAKE128" -> + Optional.of(new CSHAKE(Mac.class, new CSHAKE(128, detectionLocation))); + case "CSHAKE256" -> + Optional.of(new CSHAKE(Mac.class, new CSHAKE(256, detectionLocation))); default -> Optional.empty(); }; } diff --git a/mapper/src/main/java/com/ibm/mapper/model/DigestSize.java b/mapper/src/main/java/com/ibm/mapper/model/DigestSize.java index c5ac177c9..430cec48d 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/DigestSize.java +++ b/mapper/src/main/java/com/ibm/mapper/model/DigestSize.java @@ -31,11 +31,33 @@ public DigestSize(@Nonnull Integer value, @Nonnull DetectionLocation detectionLo this.value = value; } + private DigestSize( + @Nonnull Integer value, + @Nonnull DetectionLocation detectionLocation, + @Nonnull NodeOrigin origin) { + super(DigestSize.class, detectionLocation, origin); + this.value = value; + } + private DigestSize(@Nonnull DigestSize digestSize) { - super(digestSize.type, digestSize.detectionLocation, digestSize.children); + super( + digestSize.type, + digestSize.detectionLocation, + digestSize.children, + digestSize.origin); this.value = digestSize.value; } + /** + * Creates a DigestSize with DEFAULT origin, for use in algorithm constructors where the size is + * a known constant rather than something directly detected in source code. + */ + @Nonnull + public static DigestSize ofDefault( + @Nonnull Integer value, @Nonnull DetectionLocation detectionLocation) { + return new DigestSize(value, detectionLocation, NodeOrigin.DEFAULT); + } + @Nonnull public Integer getValue() { return value; diff --git a/mapper/src/main/java/com/ibm/mapper/model/PrivateKey.java b/mapper/src/main/java/com/ibm/mapper/model/PrivateKey.java index 3fb96f585..97f12f3d1 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/PrivateKey.java +++ b/mapper/src/main/java/com/ibm/mapper/model/PrivateKey.java @@ -30,6 +30,10 @@ public PrivateKey(@Nonnull Signature signature) { super(signature, PrivateKey.class); } + public PrivateKey(@Nonnull KeyAgreement keyAgreement) { + super(keyAgreement, PrivateKey.class); + } + public PrivateKey(@Nonnull Key key) { super(key, key.detectionLocation, PrivateKey.class); } diff --git a/mapper/src/main/java/com/ibm/mapper/model/PublicKey.java b/mapper/src/main/java/com/ibm/mapper/model/PublicKey.java index d080d7fee..6919e8db7 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/PublicKey.java +++ b/mapper/src/main/java/com/ibm/mapper/model/PublicKey.java @@ -30,6 +30,10 @@ public PublicKey(@Nonnull Signature signature) { super(signature, PublicKey.class); } + public PublicKey(@Nonnull KeyAgreement keyAgreement) { + super(keyAgreement, PublicKey.class); + } + public PublicKey(@Nonnull Key key) { super(key, key.detectionLocation, PublicKey.class); } diff --git a/mapper/src/main/java/com/ibm/mapper/model/algorithms/KMAC.java b/mapper/src/main/java/com/ibm/mapper/model/algorithms/KMAC.java index 373d538e3..a6b68583f 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/algorithms/KMAC.java +++ b/mapper/src/main/java/com/ibm/mapper/model/algorithms/KMAC.java @@ -20,7 +20,6 @@ package com.ibm.mapper.model.algorithms; import com.ibm.mapper.model.Algorithm; -import com.ibm.mapper.model.DigestSize; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.IPrimitive; import com.ibm.mapper.model.Mac; @@ -71,6 +70,10 @@ public KMAC(@Nonnull DetectionLocation detectionLocation) { this.put(new CSHAKE(detectionLocation)); } + /** + * The parameter set identifier (128 or 256) denotes the security strength only. The output + * length is independent of it and its default differs between libraries, so it is not modelled. + */ public KMAC(int parameterSetIdentifier, @Nonnull DetectionLocation detectionLocation) { /* KMAC is a hash function mostly used as a MAC */ super(NAME, Mac.class, detectionLocation); @@ -78,7 +81,6 @@ public KMAC(int parameterSetIdentifier, @Nonnull DetectionLocation detectionLoca new ParameterSetIdentifier( String.valueOf(parameterSetIdentifier), detectionLocation)); this.put(new CSHAKE(parameterSetIdentifier, detectionLocation)); - this.put(new DigestSize(2 * parameterSetIdentifier, detectionLocation)); } public KMAC(@Nonnull final Class asKind, @Nonnull KMAC kmac) { diff --git a/mapper/src/main/java/com/ibm/mapper/model/algorithms/RIPEMD.java b/mapper/src/main/java/com/ibm/mapper/model/algorithms/RIPEMD.java index 4ed5f0860..15766ae7d 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/algorithms/RIPEMD.java +++ b/mapper/src/main/java/com/ibm/mapper/model/algorithms/RIPEMD.java @@ -22,6 +22,7 @@ import com.ibm.mapper.model.Algorithm; import com.ibm.mapper.model.DigestSize; import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.IPrimitive; import com.ibm.mapper.model.MessageDigest; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -72,4 +73,8 @@ public RIPEMD(int digestSize, @Nonnull DetectionLocation detectionLocation) { this(detectionLocation); this.put(new DigestSize(digestSize, detectionLocation)); } + + public RIPEMD(@Nonnull final Class asKind, @Nonnull RIPEMD ripemd) { + super(ripemd, asKind); + } } diff --git a/mapper/src/main/java/com/ibm/mapper/model/algorithms/SHA2.java b/mapper/src/main/java/com/ibm/mapper/model/algorithms/SHA2.java index 9be9b27c8..5eb356f80 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/algorithms/SHA2.java +++ b/mapper/src/main/java/com/ibm/mapper/model/algorithms/SHA2.java @@ -52,7 +52,7 @@ public final class SHA2 extends Algorithm implements MessageDigest { public SHA2(int digestSize, @Nonnull DetectionLocation detectionLocation) { super(NAME + digestSize, MessageDigest.class, detectionLocation); - this.put(new DigestSize(digestSize, detectionLocation)); + this.put(DigestSize.ofDefault(digestSize, detectionLocation)); } public SHA2( @@ -60,7 +60,7 @@ public SHA2( @Nonnull MessageDigest preHash, @Nonnull DetectionLocation detectionLocation) { super(buildPreHashName(digestSize, preHash), MessageDigest.class, detectionLocation); - this.put(new DigestSize(digestSize, detectionLocation)); + this.put(DigestSize.ofDefault(digestSize, detectionLocation)); this.put(preHash); } diff --git a/mapper/src/main/java/com/ibm/mapper/model/algorithms/TupleHash.java b/mapper/src/main/java/com/ibm/mapper/model/algorithms/TupleHash.java index 9a765a455..f48673486 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/algorithms/TupleHash.java +++ b/mapper/src/main/java/com/ibm/mapper/model/algorithms/TupleHash.java @@ -20,9 +20,11 @@ package com.ibm.mapper.model.algorithms; import com.ibm.mapper.model.Algorithm; -import com.ibm.mapper.model.DigestSize; import com.ibm.mapper.model.ExtendableOutputFunction; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.ParameterSetIdentifier; import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; import javax.annotation.Nonnull; /** @@ -51,8 +53,25 @@ public TupleHash(@Nonnull DetectionLocation detectionLocation) { super(NAME, ExtendableOutputFunction.class, detectionLocation); } - public TupleHash(int digestSize, @Nonnull DetectionLocation detectionLocation) { + /** Returns a name of the form "TupleHashXXX" where XXX is the parameter set identifier */ + @Override + @Nonnull + public String asString() { + final StringBuilder builtName = new StringBuilder(this.name); + final Optional parameterSetIdentifier = + this.hasChildOfType(ParameterSetIdentifier.class); + parameterSetIdentifier.ifPresent(node -> builtName.append(node.asString())); + return builtName.toString(); + } + + /** + * The parameter set identifier (128 or 256) denotes the security strength of the variant, not + * the digest length. + */ + public TupleHash(int parameterSetIdentifier, @Nonnull DetectionLocation detectionLocation) { this(detectionLocation); - this.put(new DigestSize(digestSize, detectionLocation)); + this.put( + new ParameterSetIdentifier( + String.valueOf(parameterSetIdentifier), detectionLocation)); } } diff --git a/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/KeyAgreementReorganizer.java b/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/KeyAgreementReorganizer.java index bd0b22086..c4fbbef87 100644 --- a/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/KeyAgreementReorganizer.java +++ b/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/KeyAgreementReorganizer.java @@ -19,14 +19,27 @@ */ package com.ibm.mapper.reorganizer.rules; +import com.ibm.mapper.model.Algorithm; import com.ibm.mapper.model.EllipticCurve; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKey; import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.algorithms.ECDH; +import com.ibm.mapper.model.algorithms.X25519; +import com.ibm.mapper.model.algorithms.X448; +import com.ibm.mapper.model.curves.Curve25519; +import com.ibm.mapper.model.curves.Curve448; import com.ibm.mapper.reorganizer.IReorganizerRule; import com.ibm.mapper.reorganizer.builder.ReorganizerRuleBuilder; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.LinkedList; +import java.util.List; import java.util.Optional; +import java.util.function.Function; +import javax.annotation.Nonnull; public final class KeyAgreementReorganizer { @@ -55,4 +68,78 @@ private KeyAgreementReorganizer() { } return roots; }); + + public static final IReorganizerRule REPLACE_ECDH_WITH_X25519_WHEN_CURVE25519 = + replaceEcdhWithXdh(Curve25519.class, X25519::new); + + public static final IReorganizerRule REPLACE_ECDH_WITH_X448_WHEN_CURVE448 = + replaceEcdhWithXdh(Curve448.class, X448::new); + + /** + * Returns a rule that replaces an {@link ECDH} key-agreement root node with a specific XDH + * algorithm node when both its {@link PrivateKey} and {@link PublicKey} children carry the + * given {@code curveKind}. The replacement node is constructed via {@code xdhSupplier}, which + * is expected to set the correct canonical OID and curve; all other children ({@code + * KeyGeneration}, {@code PrivateKey}, {@code PublicKey}) are transferred from the old node. + * + * @param curveKind the {@link EllipticCurve} subclass to match on both key children + * @param xdhSupplier factory that builds the replacement node from a {@link + * com.ibm.mapper.utils.DetectionLocation} + */ + @Nonnull + public static IReorganizerRule replaceEcdhWithXdh( + @Nonnull Class curveKind, + @Nonnull Function xdhSupplier) { + return new ReorganizerRuleBuilder() + .createReorganizerRule("REPLACE_ECDH_WITH_XDH_WHEN_" + curveKind.getSimpleName()) + .forNodeKind(KeyAgreement.class) + .withDetectionCondition( + (node, parent, roots) -> + node instanceof ECDH + && hasCurveInKey(node, PrivateKey.class, curveKind) + && hasCurveInKey(node, PublicKey.class, curveKind)) + .perform( + (node, parent, roots) -> { + Algorithm xdh = xdhSupplier.apply(((ECDH) node).getDetectionContext()); + transferChildren(node, xdh); + return replaceRoot(roots, node, xdh); + }); + } + + // ── helpers ────────────────────────────────────────────────────────────── + + /** + * Returns {@code true} when {@code ecdh} has a {@code keyKind} child whose own {@code + * PublicKeyEncryption} child contains an {@link EllipticCurve} child that is an instance of + * {@code curveKind}. + */ + private static boolean hasCurveInKey( + @Nonnull INode ecdh, + @Nonnull Class keyKind, + @Nonnull Class curveKind) { + return ecdh.hasChildOfType(keyKind) + .flatMap(key -> key.hasChildOfType(PublicKeyEncryption.class)) + .flatMap(pke -> pke.hasChildOfType(EllipticCurve.class)) + .filter(curveKind::isInstance) + .isPresent(); + } + + /** + * Copies all children of {@code source} into {@code target}, skipping {@link Oid} so that the + * target's own canonical OID (set by its constructor) is preserved. + */ + private static void transferChildren(@Nonnull INode source, @Nonnull INode target) { + source.getChildren().entrySet().stream() + .filter(e -> !e.getKey().equals(Oid.class)) + .forEach(e -> target.put(e.getValue())); + } + + /** Returns a new roots list with {@code oldNode} replaced by {@code newNode}. */ + @Nonnull + private static List replaceRoot( + @Nonnull List roots, @Nonnull INode oldNode, @Nonnull INode newNode) { + List newRoots = new LinkedList<>(roots); + newRoots.replaceAll(r -> r == oldNode ? newNode : r); + return newRoots; + } } diff --git a/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/MessageDigestReorganizer.java b/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/MessageDigestReorganizer.java new file mode 100644 index 000000000..8796ce082 --- /dev/null +++ b/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/MessageDigestReorganizer.java @@ -0,0 +1,96 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.mapper.reorganizer.rules; + +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.algorithms.SHA2; +import com.ibm.mapper.reorganizer.IReorganizerRule; +import com.ibm.mapper.reorganizer.builder.ReorganizerRuleBuilder; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nonnull; + +public final class MessageDigestReorganizer { + + private MessageDigestReorganizer() { + // private + } + + /** + * Handles {@code SHA512.new(truncate=N)}: when a SHA-512 {@link MessageDigest} node carries a + * {@link DigestSize} child whose value differs from 512 (the truncation value), replace the + * node with a {@code SHA2(N, SHA2(512, ...))} structure, mirroring the JCA pattern for {@code + * "SHA-512/224"} / {@code "SHA-512/256"}. + */ + @Nonnull + public static final IReorganizerRule WRAP_SHA512_TRUNCATED = + new ReorganizerRuleBuilder() + .createReorganizerRule("WRAP_SHA512_TRUNCATED") + .forNodeKind(MessageDigest.class) + .withDetectionCondition( + (node, parent, roots) -> { + if (!"SHA-512".equals(node.asString())) { + return false; + } + return node.hasChildOfType(DigestSize.class) + .filter(ds -> !"512".equals(ds.asString())) + .isPresent(); + }) + .perform( + (node, parent, roots) -> { + if (!(node instanceof SHA2 sha2Node)) { + return roots; + } + final Optional digestSizeOpt = + sha2Node.hasChildOfType(DigestSize.class); + if (digestSizeOpt.isEmpty()) { + return roots; + } + final int truncateSize = + ((DigestSize) digestSizeOpt.get()).getValue(); + final SHA2 preHash512 = + new SHA2(512, sha2Node.getDetectionContext()); + final SHA2 truncated = + new SHA2( + truncateSize, + preHash512, + sha2Node.getDetectionContext()); + + // Copy remaining children (BlockSize, OID, Digest, …) onto the + // new truncated node, skipping DigestSize (already set by SHA2 + // constructor) and the SHA2(512) pre-hash. + sha2Node.getChildren().values().stream() + .filter(child -> !(child instanceof DigestSize)) + .filter(child -> !(child instanceof MessageDigest)) + .forEach(truncated::put); + + if (parent != null) { + parent.put(truncated); + return roots; + } + // Root node replacement + final List newRoots = new LinkedList<>(roots); + newRoots.replaceAll(r -> r == node ? truncated : r); + return newRoots; + }); +} diff --git a/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/SignatureReorganizer.java b/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/SignatureReorganizer.java index 5a4708993..73095321f 100644 --- a/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/SignatureReorganizer.java +++ b/mapper/src/main/java/com/ibm/mapper/reorganizer/rules/SignatureReorganizer.java @@ -35,7 +35,6 @@ import com.ibm.mapper.reorganizer.builder.ReorganizerRuleBuilder; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Optional; import javax.annotation.Nonnull; @@ -194,12 +193,19 @@ public static IReorganizerRule moveNodesFromUnderFunctionalityUnderNode( .flatMap(p -> p.hasChildOfType(underNodeClazz)) .ifPresent( n -> { - for (Map.Entry, INode> - childKeyValue : - node.getChildren().entrySet()) { - n.put(childKeyValue.getValue()); - node.removeChildOfType(childKeyValue.getKey()); - } + // Snapshot the children before mutating so that + // we neither trigger + // ConcurrentModificationException + // nor corrupt a shared map aliased by Algorithm's + // copy constructor (Algorithm.java:39). + new java.util.ArrayList<>( + node.getChildren().values()) + .forEach( + child -> { + n.put(child); + node.removeChildOfType( + child.getKind()); + }); }); return null; }); @@ -252,12 +258,19 @@ public static IReorganizerRule moveNodesFromUnderFunctionalityUnderParent( Optional.ofNullable(parent) .ifPresent( p -> { - for (Map.Entry, INode> - childKeyValue : - node.getChildren().entrySet()) { - p.put(childKeyValue.getValue()); - node.removeChildOfType(childKeyValue.getKey()); - } + // Snapshot the children before mutating so that + // we neither trigger + // ConcurrentModificationException + // nor corrupt a shared map aliased by Algorithm's + // copy constructor (Algorithm.java:39). + new java.util.ArrayList<>( + node.getChildren().values()) + .forEach( + child -> { + p.put(child); + node.removeChildOfType( + child.getKind()); + }); }); return null; }); 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 d7a6f60eb..f27f4923f 100644 --- a/output/src/test/java/com/ibm/output/cyclonedx/AlgorithmTest.java +++ b/output/src/test/java/com/ibm/output/cyclonedx/AlgorithmTest.java @@ -31,12 +31,14 @@ import com.ibm.mapper.model.SaltLength; import com.ibm.mapper.model.algorithms.DSA; import com.ibm.mapper.model.algorithms.ECDH; +import com.ibm.mapper.model.algorithms.KMAC; import com.ibm.mapper.model.algorithms.MGF1; import com.ibm.mapper.model.algorithms.PBKDF2; import com.ibm.mapper.model.algorithms.RSA; import com.ibm.mapper.model.algorithms.RSAssaPSS; import com.ibm.mapper.model.algorithms.SHA; import com.ibm.mapper.model.algorithms.SHA2; +import com.ibm.mapper.model.algorithms.TupleHash; import com.ibm.mapper.model.curves.Secp256r1; import com.ibm.mapper.model.curves.Secp384r1; import com.ibm.mapper.model.functionality.Decrypt; @@ -410,4 +412,41 @@ void eccKeyAgree() { assertThat(cryptoProperties.getOid()).isEqualTo("1.3.132.1.12"); }); } + + @Test + void kmacParameterSetIdentifierIsSecurityStrength() { + for (final int strength : new int[] {128, 256}) { + this.assertsNode( + () -> new KMAC(strength, detectionLocation), + bom -> { + final Component component = componentByName(bom, "KMAC" + strength); + final AlgorithmProperties algorithmProperties = + component.getCryptoProperties().getAlgorithmProperties(); + assertThat(algorithmProperties.getParameterSetIdentifier()) + .isEqualTo(String.valueOf(strength)); + }); + } + } + + @Test + void tupleHashParameterSetIdentifierIsSecurityStrength() { + for (final int strength : new int[] {128, 256}) { + this.assertsNode( + () -> new TupleHash(strength, detectionLocation), + bom -> { + final Component component = componentByName(bom, "TupleHash" + strength); + final AlgorithmProperties algorithmProperties = + component.getCryptoProperties().getAlgorithmProperties(); + assertThat(algorithmProperties.getParameterSetIdentifier()) + .isEqualTo(String.valueOf(strength)); + }); + } + } + + private static Component componentByName(final org.cyclonedx.model.Bom bom, final String name) { + return bom.getComponents().stream() + .filter(c -> name.equals(c.getName())) + .findFirst() + .orElseThrow(() -> new AssertionError("component not found: " + name)); + } } diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/PythonDetectionRules.java b/python/src/main/java/com/ibm/plugin/rules/detection/PythonDetectionRules.java index a004792e2..49285c613 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/PythonDetectionRules.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/PythonDetectionRules.java @@ -22,20 +22,28 @@ import com.ibm.engine.rule.DetectionRuleSet; import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.RuleSets; -import com.ibm.plugin.rules.detection.aead.PycaAEAD; -import com.ibm.plugin.rules.detection.aead.PycaAES; -import com.ibm.plugin.rules.detection.asymmetric.PycaDSA; -import com.ibm.plugin.rules.detection.asymmetric.PycaDiffieHellman; -import com.ibm.plugin.rules.detection.asymmetric.PycaEllipticCurve; -import com.ibm.plugin.rules.detection.asymmetric.PycaRSA; -import com.ibm.plugin.rules.detection.asymmetric.PycaSign; -import com.ibm.plugin.rules.detection.fernet.PycaFernet; -import com.ibm.plugin.rules.detection.hash.PycaHashWrapper; -import com.ibm.plugin.rules.detection.kdf.PycaKDF; -import com.ibm.plugin.rules.detection.keyagreement.PycaKeyAgreement; -import com.ibm.plugin.rules.detection.mac.PycaMAC; -import com.ibm.plugin.rules.detection.symmetric.PycaCipher; -import com.ibm.plugin.rules.detection.wrapping.PycaWrapping; +import com.ibm.plugin.rules.detection.pyca.aead.PycaAEAD; +import com.ibm.plugin.rules.detection.pyca.aead.PycaAES; +import com.ibm.plugin.rules.detection.pyca.asymmetric.PycaDSA; +import com.ibm.plugin.rules.detection.pyca.asymmetric.PycaDiffieHellman; +import com.ibm.plugin.rules.detection.pyca.asymmetric.PycaEllipticCurve; +import com.ibm.plugin.rules.detection.pyca.asymmetric.PycaRSA; +import com.ibm.plugin.rules.detection.pyca.asymmetric.PycaSign; +import com.ibm.plugin.rules.detection.pyca.fernet.PycaFernet; +import com.ibm.plugin.rules.detection.pyca.hash.PycaHashWrapper; +import com.ibm.plugin.rules.detection.pyca.kdf.PycaKDF; +import com.ibm.plugin.rules.detection.pyca.keyagreement.PycaKeyAgreement; +import com.ibm.plugin.rules.detection.pyca.mac.PycaMAC; +import com.ibm.plugin.rules.detection.pyca.symmetric.PycaCipher; +import com.ibm.plugin.rules.detection.pyca.wrapping.PycaWrapping; +import com.ibm.plugin.rules.detection.pycrypto.cipher.PythonCryptoCipher; +import com.ibm.plugin.rules.detection.pycrypto.hash.PythonCryptoHash; +import com.ibm.plugin.rules.detection.pycrypto.kdf.PythonCryptoKDF; +import com.ibm.plugin.rules.detection.pycrypto.keyagreement.PythonCryptoKeyAgreement; +import com.ibm.plugin.rules.detection.pycrypto.mac.PythonCryptoMac; +import com.ibm.plugin.rules.detection.pycrypto.publickey.PythonCryptoElGamal; +import com.ibm.plugin.rules.detection.pycrypto.random.PythonCryptoRandom; +import com.ibm.plugin.rules.detection.pycrypto.signature.PythonCryptoSignature; import java.util.List; import java.util.stream.Stream; import javax.annotation.Nonnull; @@ -61,7 +69,15 @@ protected List> buildRules() { RuleSets.rulesOf(PycaMAC.class).stream(), RuleSets.rulesOf(PycaWrapping.class).stream(), RuleSets.rulesOf(PycaKDF.class).stream(), - RuleSets.rulesOf(PycaFernet.class).stream()) + RuleSets.rulesOf(PycaFernet.class).stream(), + RuleSets.rulesOf(PythonCryptoHash.class).stream(), + RuleSets.rulesOf(PythonCryptoMac.class).stream(), + RuleSets.rulesOf(PythonCryptoRandom.class).stream(), + RuleSets.rulesOf(PythonCryptoCipher.class).stream(), + RuleSets.rulesOf(PythonCryptoElGamal.class).stream(), + RuleSets.rulesOf(PythonCryptoSignature.class).stream(), + RuleSets.rulesOf(PythonCryptoKDF.class).stream(), + RuleSets.rulesOf(PythonCryptoKeyAgreement.class).stream()) .flatMap(i -> i) .toList(); } diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAEAD.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAEAD.java similarity index 98% rename from python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAEAD.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAEAD.java index 51ba6d76e..e97727ee3 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAEAD.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAEAD.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.aead; +package com.ibm.plugin.rules.detection.pyca.aead; import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.KeyAction; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAES.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAES.java similarity index 98% rename from python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAES.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAES.java index 6d65f92a6..f0a6a014f 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/aead/PycaAES.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAES.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.aead; +package com.ibm.plugin.rules.detection.pyca.aead; import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.Size; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDSA.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDSA.java similarity index 97% rename from python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDSA.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDSA.java index 73efeeebd..4aa088855 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDSA.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDSA.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric; +package com.ibm.plugin.rules.detection.pyca.asymmetric; import static com.ibm.engine.detection.MethodMatcher.ANY; @@ -34,7 +34,7 @@ import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.RuleSets; import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.hash.PycaHash; +import com.ibm.plugin.rules.detection.pyca.hash.PycaHash; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDiffieHellman.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDiffieHellman.java similarity index 98% rename from python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDiffieHellman.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDiffieHellman.java index 4a3024f30..9469fa570 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaDiffieHellman.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaDiffieHellman.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric; +package com.ibm.plugin.rules.detection.pyca.asymmetric; import static com.ibm.engine.detection.MethodMatcher.ANY; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaEllipticCurve.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaEllipticCurve.java similarity index 98% rename from python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaEllipticCurve.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaEllipticCurve.java index 6d8d61836..e3aa404b9 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaEllipticCurve.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaEllipticCurve.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric; +package com.ibm.plugin.rules.detection.pyca.asymmetric; import static com.ibm.engine.detection.MethodMatcher.ANY; @@ -35,7 +35,7 @@ import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.RuleSets; import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.hash.PycaHash; +import com.ibm.plugin.rules.detection.pyca.hash.PycaHash; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaRSA.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaRSA.java similarity index 98% rename from python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaRSA.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaRSA.java index f564bd490..7cf7d2396 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaRSA.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaRSA.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric; +package com.ibm.plugin.rules.detection.pyca.asymmetric; import static com.ibm.engine.detection.MethodMatcher.ANY; @@ -38,7 +38,7 @@ import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.RuleSets; import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.hash.PycaHash; +import com.ibm.plugin.rules.detection.pyca.hash.PycaHash; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaSign.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaSign.java similarity index 98% rename from python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaSign.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaSign.java index dd59824be..4d685ee15 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/asymmetric/PycaSign.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/asymmetric/PycaSign.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric; +package com.ibm.plugin.rules.detection.pyca.asymmetric; import com.ibm.engine.model.KeyAction; import com.ibm.engine.model.context.PrivateKeyContext; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/fernet/PycaFernet.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernet.java similarity index 98% rename from python/src/main/java/com/ibm/plugin/rules/detection/fernet/PycaFernet.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernet.java index 37d26a2ce..ea621e3e8 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/fernet/PycaFernet.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernet.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.fernet; +package com.ibm.plugin.rules.detection.pyca.fernet; import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.KeyAction; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/hash/PycaHash.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHash.java similarity index 98% rename from python/src/main/java/com/ibm/plugin/rules/detection/hash/PycaHash.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHash.java index 283c32ecd..05cc7e477 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/hash/PycaHash.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHash.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.hash; +package com.ibm.plugin.rules.detection.pyca.hash; import com.ibm.engine.model.context.DigestContext; import com.ibm.engine.model.factory.AlgorithmFactory; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/hash/PycaHashWrapper.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHashWrapper.java similarity index 96% rename from python/src/main/java/com/ibm/plugin/rules/detection/hash/PycaHashWrapper.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHashWrapper.java index f66828b50..406e0f20d 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/hash/PycaHashWrapper.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHashWrapper.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.hash; +package com.ibm.plugin.rules.detection.pyca.hash; import com.ibm.engine.rule.DetectionRuleSet; import com.ibm.engine.rule.IDetectionRule; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/kdf/PycaKDF.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKDF.java similarity index 99% rename from python/src/main/java/com/ibm/plugin/rules/detection/kdf/PycaKDF.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKDF.java index d6624b334..2c7795355 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/kdf/PycaKDF.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKDF.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.kdf; +package com.ibm.plugin.rules.detection.pyca.kdf; import static com.ibm.engine.detection.MethodMatcher.ANY; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreement.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreement.java similarity index 97% rename from python/src/main/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreement.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreement.java index 832cb375f..a23734f3b 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreement.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreement.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.keyagreement; +package com.ibm.plugin.rules.detection.pyca.keyagreement; import com.ibm.engine.model.KeyAction; import com.ibm.engine.model.context.KeyAgreementContext; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/mac/PycaMAC.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMAC.java similarity index 98% rename from python/src/main/java/com/ibm/plugin/rules/detection/mac/PycaMAC.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMAC.java index b866cdd0d..e38506c48 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/mac/PycaMAC.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMAC.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.mac; +package com.ibm.plugin.rules.detection.pyca.mac; import static com.ibm.engine.detection.MethodMatcher.ANY; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/padding/PycaPadding.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPadding.java similarity index 97% rename from python/src/main/java/com/ibm/plugin/rules/detection/padding/PycaPadding.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPadding.java index 9d5b7a972..27d334a39 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/padding/PycaPadding.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPadding.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.padding; +package com.ibm.plugin.rules.detection.pyca.padding; import com.ibm.engine.model.Size; import com.ibm.engine.model.context.CipherContext; @@ -26,7 +26,7 @@ import com.ibm.engine.rule.DetectionRuleSet; import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.symmetric.PycaCipher; +import com.ibm.plugin.rules.detection.pyca.symmetric.PycaCipher; import java.util.Arrays; import java.util.LinkedList; import java.util.List; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher.java similarity index 97% rename from python/src/main/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher.java index 79a725b55..ff61e5e43 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.symmetric; +package com.ibm.plugin.rules.detection.pyca.symmetric; import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.context.CipherContext; @@ -28,7 +28,7 @@ import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.RuleSets; import com.ibm.engine.rule.builder.DetectionRuleBuilder; -import com.ibm.plugin.rules.detection.padding.PycaPadding; +import com.ibm.plugin.rules.detection.pyca.padding.PycaPadding; import java.util.Arrays; import java.util.LinkedList; import java.util.List; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/wrapping/PycaWrapping.java b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrapping.java similarity index 98% rename from python/src/main/java/com/ibm/plugin/rules/detection/wrapping/PycaWrapping.java rename to python/src/main/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrapping.java index ba51f19da..f4dde0068 100644 --- a/python/src/main/java/com/ibm/plugin/rules/detection/wrapping/PycaWrapping.java +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrapping.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.wrapping; +package com.ibm.plugin.rules.detection.pyca.wrapping; import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.context.CipherContext; diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PythonCryptoCipher.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PythonCryptoCipher.java new file mode 100644 index 000000000..72dad7457 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PythonCryptoCipher.java @@ -0,0 +1,294 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.ModeFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.RuleSets; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.pycrypto.publickey.PythonCryptoRSA; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoCipher extends DetectionRuleSet { + + private PythonCryptoCipher() { + // private + } + + private static final IDetectionRule ENCRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(ANY) + .forMethods("encrypt", "encrypt_and_digest") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule DECRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(ANY) + .forMethods("decrypt", "decrypt_and_verify") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule AES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.AES", "Cryptodome.Cipher.AES") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("AES")) + .withNamedMethodParameter("key", ANY) + .withNamedMethodParameter( + "mode", ANY) // Crypto.Cipher.AES.* or Cryptodome.Cipher.AES.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("iv", ANY) + .withOptionalNamedMethodParameter("nonce", ANY) + .withOptionalNamedMethodParameter("segment_size", "int") + .withOptionalNamedMethodParameter("mac_len", "int") + .withOptionalNamedMethodParameter("msg_len", "int") + .withOptionalNamedMethodParameter("assoc_len", "int") + .withOptionalNamedMethodParameter("initial_value", ANY) + .withOptionalNamedMethodParameter("counter", ANY) + .withOptionalNamedMethodParameter("use_aesni", ANY) + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule DES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.DES", "Cryptodome.Cipher.DES") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("DES")) + .withNamedMethodParameter("key", ANY) + .withNamedMethodParameter( + "mode", ANY) // Crypto.Cipher.DES.* or Cryptodome.Cipher.DES.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("iv", ANY) + .withOptionalNamedMethodParameter("nonce", ANY) + .withOptionalNamedMethodParameter("segment_size", "int") + .withOptionalNamedMethodParameter("mac_len", "int") + .withOptionalNamedMethodParameter("initial_value", "int") + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule DES3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.DES3", "Cryptodome.Cipher.DES3") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("3DES")) + .withNamedMethodParameter("key", ANY) + .withNamedMethodParameter( + "mode", ANY) // Crypto.Cipher.DES3.* or Cryptodome.Cipher.DES3.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("iv", ANY) + .withOptionalNamedMethodParameter("nonce", ANY) + .withOptionalNamedMethodParameter("segment_size", "int") + .withOptionalNamedMethodParameter("mac_len", "int") + .withOptionalNamedMethodParameter("initial_value", "int") + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule BLOWFISH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.Blowfish", "Cryptodome.Cipher.Blowfish") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("Blowfish")) + .withNamedMethodParameter("key", ANY) + .withNamedMethodParameter( + "mode", ANY) // Crypto.Cipher.Blowfish.* or Cryptodome.Cipher.Blowfish.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("iv", ANY) + .withOptionalNamedMethodParameter("nonce", ANY) + .withOptionalNamedMethodParameter("segment_size", "int") + .withOptionalNamedMethodParameter("mac_len", "int") + .withOptionalNamedMethodParameter("initial_value", "int") + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule CAST = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.CAST", "Cryptodome.Cipher.CAST") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("CAST5")) + .withNamedMethodParameter("key", ANY) + .withNamedMethodParameter( + "mode", ANY) // Crypto.Cipher.CAST.* or Cryptodome.Cipher.CAST.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("iv", ANY) + .withOptionalNamedMethodParameter("nonce", ANY) + .withOptionalNamedMethodParameter("segment_size", "int") + .withOptionalNamedMethodParameter("mac_len", "int") + .withOptionalNamedMethodParameter("initial_value", "int") + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule ARC2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.ARC2", "Cryptodome.Cipher.ARC2") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("RC2")) + .withNamedMethodParameter("key", ANY) + .withNamedMethodParameter( + "mode", ANY) // Crypto.Cipher.ARC2.* or Cryptodome.Cipher.ARC2.* + .shouldBeDetectedAs(new ModeFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("iv", ANY) + .withOptionalNamedMethodParameter("nonce", ANY) + .withOptionalNamedMethodParameter("effective_keylen", "int") + .withOptionalNamedMethodParameter("segment_size", "int") + .withOptionalNamedMethodParameter("mac_len", "int") + .withOptionalNamedMethodParameter("initial_value", "int") + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule ARC4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.ARC4", "Cryptodome.Cipher.ARC4") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("RC4")) + .withAnyParameters() + .buildForContext(new CipherContext(Map.of("algorithm", "RC4"))) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule CHACHA20 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.ChaCha20", "Cryptodome.Cipher.ChaCha20") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("ChaCha20")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule CHACHA20_POLY1305 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "Crypto.Cipher.ChaCha20_Poly1305", + "Cryptodome.Cipher.ChaCha20_Poly1305") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("ChaCha20Poly1305")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule SALSA20 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Cipher.Salsa20", "Cryptodome.Cipher.Salsa20") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("Salsa20")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule PKCS1_OAEP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectExactTypes("Crypto.Cipher.PKCS1_OAEP", "Cryptodome.Cipher.PKCS1_OAEP") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("PKCS1_OAEP")) + .withMethodParameter( + ANY) // Crypto.PublicKey.RSAkey or Cryptodome.PublicKey.RSAkey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(RuleSets.rulesOf(PythonCryptoRSA.class)) + .withOptionalNamedMethodParameter("hashAlgo", ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("mgfunc", ANY) + .withOptionalNamedMethodParameter("label", ANY) + .withOptionalNamedMethodParameter("randfunc", ANY) + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + private static final IDetectionRule PKCS1_V1_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectExactTypes("Crypto.Cipher.PKCS1_v1_5", "Cryptodome.Cipher.PKCS1_v1_5") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("PKCS1_v1_5")) + .withMethodParameter( + ANY) // Crypto.PublicKey.RSAkey or Cryptodome.PublicKey.RSAkey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(RuleSets.rulesOf(PythonCryptoRSA.class)) + .withOptionalNamedMethodParameter("randfunc", ANY) + .buildForContext(new CipherContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(ENCRYPT, DECRYPT)); + + @Nonnull + @Override + protected List> buildRules() { + return List.of( + AES, + DES, + DES3, + BLOWFISH, + CAST, + ARC2, + ARC4, + CHACHA20, + CHACHA20_POLY1305, + SALSA20, + PKCS1_OAEP, + PKCS1_V1_5); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/hash/PythonCryptoHash.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/hash/PythonCryptoHash.java new file mode 100644 index 000000000..8f67f0d65 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/hash/PythonCryptoHash.java @@ -0,0 +1,106 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.Size.UnitType; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.engine.model.factory.DigestSizeFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoHash extends DetectionRuleSet { + + private PythonCryptoHash() { + // private + } + + public static final List hashes = + Arrays.asList( + "MD2", + "MD4", + "MD5", + "SHA1", + "SHA224", + "SHA256", + "SHA384", + // "SHA512", + "SHA3_224", + "SHA3_256", + "SHA3_384", + "SHA3_512", + "RIPEMD160", + "keccak", + "TupleHash128", + "TupleHash256", + "SHAKE128", + "SHAKE256", + "cSHAKE128", + "cSHAKE256", + "KangarooTwelve", + "BLAKE2b", + "BLAKE2s"); + + private static final IDetectionRule SHA512 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash.SHA512", "Cryptodome.Hash.SHA512") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA512")) + .withOptionalNamedMethodParameter("data", ANY) + .withOptionalNamedMethodParameter("truncate", "str") + .shouldBeDetectedAs(new DigestSizeFactory<>(UnitType.BIT)) + .asChildOfParameterWithId(-1) + .buildForContext(new DigestContext(Map.of("preHash", "SHA512"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + @Override + protected List> buildRules() { + LinkedList> rules = new LinkedList<>(); + + rules.add(SHA512); + + for (final String hash : PythonCryptoHash.hashes) { + rules.add( + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash." + hash, "Cryptodome.Hash." + hash) + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>(hash)) + .withAnyParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules()); + } + return rules; + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PythonCryptoKDF.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PythonCryptoKDF.java new file mode 100644 index 000000000..010da7ce2 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PythonCryptoKDF.java @@ -0,0 +1,158 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.Size; +import com.ibm.engine.model.Size.UnitType; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.IterationCountFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoKDF extends DetectionRuleSet { + + private PythonCryptoKDF() { + // private + } + + // PBKDF1 - module function call + private static final IDetectionRule PBKDF1 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF1") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF1")) + .withNamedMethodParameter("password", ANY) + .withNamedMethodParameter("salt", ANY) + .withNamedMethodParameter("dkLen", "int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("count", "int") + .shouldBeDetectedAs(new IterationCountFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("hashAlgo", ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf1"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // PBKDF2 - module function call + private static final IDetectionRule PBKDF2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("PBKDF2") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF2")) + .withNamedMethodParameter("password", ANY) + .withNamedMethodParameter("salt", ANY) + .withOptionalNamedMethodParameter("dkLen", "int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("count", "int") + .shouldBeDetectedAs(new IterationCountFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("hmac_hash_module", ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-pbkdf2"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // scrypt - module function call + private static final IDetectionRule SCRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("scrypt") + .shouldBeDetectedAs(new ValueActionFactory<>("scrypt")) + .withNamedMethodParameter("password", ANY) + .withNamedMethodParameter("salt", ANY) + .withNamedMethodParameter("key_len", "int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withNamedMethodParameter("N", "int") + .withNamedMethodParameter("r", "int") + .withNamedMethodParameter("p", "int") + .withOptionalNamedMethodParameter("num_keys", "int") + .buildForContext(new KeyDerivationFunctionContext(Map.of("kind", "scrypt"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule HKDF = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("HKDF") + .shouldBeDetectedAs(new ValueActionFactory<>("HKDF")) + .withNamedMethodParameter("master", ANY) + .withNamedMethodParameter("key_len", "int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withNamedMethodParameter("salt", ANY) + .withNamedMethodParameter( + "hashmod", ANY) // Crypto.Hash.* or Cryptodome.Hash.* (hash_mod) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("num_keys", "int") + .withOptionalNamedMethodParameter("context", ANY) + .buildForContext( + new KeyDerivationFunctionContext(Map.of("kind", "pycrypto-hkdf"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // scrypt - module function call + private static final IDetectionRule SP800_108_COUNTER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.KDF", "Cryptodome.Protocol.KDF") + .forMethods("SP800_108_Counter") + .shouldBeDetectedAs(new ValueActionFactory<>("SP800_108_Counter")) + .withNamedMethodParameter("master", ANY) // master + .withNamedMethodParameter("key_len", "int") // key_len + .shouldBeDetectedAs(new KeySizeFactory<>(UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withNamedMethodParameter("prf", ANY) + .withOptionalNamedMethodParameter("num_keys", "int") + .withOptionalNamedMethodParameter("label", ANY) + .withOptionalNamedMethodParameter("context", ANY) + .buildForContext(new KeyDerivationFunctionContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + @Override + protected List> buildRules() { + return List.of(PBKDF1, PBKDF2, SCRYPT, HKDF, SP800_108_COUNTER); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/PythonCryptoKeyAgreement.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/PythonCryptoKeyAgreement.java new file mode 100644 index 000000000..637c0e583 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/PythonCryptoKeyAgreement.java @@ -0,0 +1,162 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.keyagreement; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.RuleSets; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.pycrypto.publickey.PythonCryptoECC; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoKeyAgreement extends DetectionRuleSet { + + private PythonCryptoKeyAgreement() { + // private + } + + private static final IDetectionRule IMPORT_X25519_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.DH", "Cryptodome.Protocol.DH") + .forMethods("import_x25519_public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "Curve25519"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule IMPORT_X25519_PRIVATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.DH", "Cryptodome.Protocol.DH") + .forMethods("import_x25519_private_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "Curve25519"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule IMPORT_X448_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.DH", "Cryptodome.Protocol.DH") + .forMethods("import_x448_public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "Curve448"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule IMPORT_X448_PRIVATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.DH", "Cryptodome.Protocol.DH") + .forMethods("import_x448_private_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "Curve448"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // DH.key_agreement - module function call + private static final IDetectionRule DH_KEY_AGREEMENT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Protocol.DH", "Cryptodome.Protocol.DH") + .forMethods("key_agreement") + .shouldBeDetectedAs(new ValueActionFactory<>("ECDH")) + .withNamedMethodParameter("kdf", ANY) // kdf + .withOptionalNamedMethodParameter( + "static_priv", + ANY) // Crypto.PublicKey.ECC.ECCKey or Cryptodome.PublicKey.ECC.ECCKey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules( + Stream.concat( + RuleSets.rulesOf(PythonCryptoECC.class).stream(), + Stream.of( + IMPORT_X25519_PRIVATE_KEY, + IMPORT_X448_PRIVATE_KEY)) + .toList()) + .withOptionalNamedMethodParameter( + "static_pub", + ANY) // Crypto.PublicKey.ECC.ECCKey or Cryptodome.PublicKey.ECC.ECCKey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules( + Stream.concat( + RuleSets.rulesOf(PythonCryptoECC.class).stream(), + Stream.of( + IMPORT_X25519_PUBLIC_KEY, + IMPORT_X448_PUBLIC_KEY)) + .toList()) + .withOptionalNamedMethodParameter( + "eph_priv", + ANY) // Crypto.PublicKey.ECC.ECCKey or Cryptodome.PublicKey.ECC.ECCKey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules( + Stream.concat( + RuleSets.rulesOf(PythonCryptoECC.class).stream(), + Stream.of( + IMPORT_X25519_PRIVATE_KEY, + IMPORT_X448_PRIVATE_KEY)) + .toList()) + .withOptionalNamedMethodParameter( + "eph_pub", + ANY) // Crypto.PublicKey.ECC.ECCKey or Cryptodome.PublicKey.ECC.ECCKey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules( + Stream.concat( + RuleSets.rulesOf(PythonCryptoECC.class).stream(), + Stream.of( + IMPORT_X25519_PUBLIC_KEY, + IMPORT_X448_PUBLIC_KEY)) + .toList()) + .buildForContext(new KeyAgreementContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + @Override + protected List> buildRules() { + return List.of(DH_KEY_AGREEMENT); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/mac/PythonCryptoMac.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/mac/PythonCryptoMac.java new file mode 100644 index 000000000..7d9c8dd76 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/mac/PythonCryptoMac.java @@ -0,0 +1,118 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.context.MacContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoMac extends DetectionRuleSet { + + private PythonCryptoMac() { + // private + } + + private static final IDetectionRule CMAC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash.CMAC", "Cryptodome.Hash.CMAC") + .forMethods("new") + .withNamedMethodParameter("key", ANY) + .withOptionalNamedMethodParameter("msg", ANY) + .withOptionalNamedMethodParameter( + "ciphermod", ANY) // Crypto.Cipher.* or Cryptodome.Cipher.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("cipher_params", ANY) + .withOptionalNamedMethodParameter("mac_len", "int") + .withOptionalNamedMethodParameter("update_after_digest", ANY) + .buildForContext(new MacContext(Map.of("kind", "cmac"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule HMAC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash.HMAC", "Cryptodome.Hash.HMAC") + .forMethods("new") + .withNamedMethodParameter("key", ANY) + .withOptionalNamedMethodParameter("msg", ANY) + .withOptionalNamedMethodParameter( + "digestmod", ANY) // Crypto.Hash.* or Cryptodome.Hash.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext(new MacContext(Map.of("kind", "hmac"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // The output length (mac_len) is not captured, as it cannot be exported to the CBOM. + private static final IDetectionRule KMAC128 = kmac("KMAC128"); + + private static final IDetectionRule KMAC256 = kmac("KMAC256"); + + private static final IDetectionRule POLY1305 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash.Poly1305", "Cryptodome.Hash.Poly1305") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("Poly1305")) + .withNamedMethodParameter("key", ANY) + .withOptionalNamedMethodParameter( + "cipher", ANY) // Crypto.Cipher.AES or Crypto.Cipher.ChaCha20 + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("nonce", ANY) + .withOptionalNamedMethodParameter("data", ANY) + .buildForContext(new MacContext(Map.of("kind", "poly1305"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + private static IDetectionRule kmac(@Nonnull String name) { + return new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Hash." + name, "Cryptodome.Hash." + name) + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>(name)) + .withNamedMethodParameter("key", ANY) + .withOptionalNamedMethodParameter("data", ANY) + .withOptionalNamedMethodParameter("mac_len", "int") + .withOptionalNamedMethodParameter("custom", ANY) + .buildForContext(new MacContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + } + + @Nonnull + @Override + protected List> buildRules() { + return List.of(CMAC, HMAC, KMAC128, KMAC256, POLY1305); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoDSA.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoDSA.java new file mode 100644 index 000000000..50c7c9812 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoDSA.java @@ -0,0 +1,97 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +// For RSA, DSA, and ECC keys there are corresponding signature or encryption schemes +// that use them as a parameter. In order the detect the key object in the context of +// these higher-level methods the corresponding rules are used as dependent rules. +@SuppressWarnings("java:S1192") +public final class PythonCryptoDSA extends DetectionRuleSet { + + private PythonCryptoDSA() { + // private + } + + // DSA generate -> private key + private static final IDetectionRule DSA_GENERATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.DSA", "Cryptodome.PublicKey.DSA") + .forMethods("generate") + // .shouldBeDetectedAs( + // new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withNamedMethodParameter("bits", "int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("randfunc", ANY) + .withOptionalNamedMethodParameter("domain", ANY) + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "DSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // DSA construct and import_key -> public or private key + private static final IDetectionRule DSA_CONSTRUCT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.DSA", "Cryptodome.PublicKey.DSA") + .forMethods("construct", "import_key") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("algorithm", "DSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // DsaKey public_key -> public key + private static final IDetectionRule DSA_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "Crypto.PublicKey.DSA.DsaKey", "Cryptodome.PublicKey.DSA.DsaKey") + .forMethods("public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withoutParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "DSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + @Override + protected List> buildRules() { + return List.of(DSA_CONSTRUCT, DSA_GENERATE, DSA_PUBLIC_KEY); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoECC.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoECC.java new file mode 100644 index 000000000..1c74ca04f --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoECC.java @@ -0,0 +1,114 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.factory.CurveFactory; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +// For RSA, DSA, and ECC keys there are corresponding signature or encryption schemes +// that use them as a parameter. In order the detect the key object in the context of +// these higher-level methods the corresponding rules are used as dependent rules. +@SuppressWarnings("java:S1192") +public final class PythonCryptoECC extends DetectionRuleSet { + + private PythonCryptoECC() { + // private + } + + // ECC key generation + private static final IDetectionRule ECC_GENERATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.ECC", "Cryptodome.PublicKey.ECC") + .forMethods("generate") + // .shouldBeDetectedAs(new + // KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withNamedMethodParameter("curve", "str") + .shouldBeDetectedAs(new CurveFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("randfunc", ANY) + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // ECC key construction + private static final IDetectionRule ECC_CONSTRUCT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.ECC", "Cryptodome.PublicKey.ECC") + .forMethods("construct") + // .shouldBeDetectedAs( + // new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withNamedMethodParameter("curve", "str") + .shouldBeDetectedAs(new CurveFactory<>()) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("d", "int") + .withOptionalNamedMethodParameter("seed", ANY) + .withOptionalNamedMethodParameter("point_x", "int") + .withOptionalNamedMethodParameter("point_y", "int") + .buildForContext(new KeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // ECC import + private static final IDetectionRule ECC_IMPORT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.ECC", "Cryptodome.PublicKey.ECC") + .forMethods("import_key") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // EccKey public_key -> public key + private static final IDetectionRule ECC_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "Crypto.PublicKey.ECC.EccKey", "Cryptodome.PublicKey.ECC.EccKey") + .forMethods("public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withoutParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + @Override + protected List> buildRules() { + return List.of(ECC_CONSTRUCT, ECC_GENERATE, ECC_IMPORT, ECC_PUBLIC_KEY); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoElGamal.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoElGamal.java new file mode 100644 index 000000000..26e3d9696 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoElGamal.java @@ -0,0 +1,81 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +// Only the ElGamal rules are registered as top-level detection rules. The documentation +// describes them as obsolete keys. Pycryptodome does not provide a higher-level method +// (encryption, signature) based on ElGamal keys. +@SuppressWarnings("java:S1192") +public final class PythonCryptoElGamal extends DetectionRuleSet { + + private PythonCryptoElGamal() { + // private + } + + // ElGamal + private static final IDetectionRule ELGAMAL_GENERATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.ElGamal", "Cryptodome.PublicKey.ElGamal") + .forMethods("generate") + // .shouldBeDetectedAs( + // new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withNamedMethodParameter("bits", "int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .asChildOfParameterWithId(-1) + .withNamedMethodParameter("randfunc", ANY) + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "ElGamal"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // ElGamal + private static final IDetectionRule ELGAMAL_CONSTRUCT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.ElGamal", "Cryptodome.PublicKey.ElGamal") + .forMethods("construct") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("algorithm", "ElGamal"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + @Override + protected List> buildRules() { + return List.of(ELGAMAL_CONSTRUCT, ELGAMAL_GENERATE); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoRSA.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoRSA.java new file mode 100644 index 000000000..d893e2830 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/publickey/PythonCryptoRSA.java @@ -0,0 +1,97 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +// For RSA, DSA, and ECC keys there are corresponding signature or encryption schemes +// that use them as a parameter. In order the detect the key object in the context of +// these higher-level methods the corresponding rules are used as dependent rules. +@SuppressWarnings("java:S1192") +public final class PythonCryptoRSA extends DetectionRuleSet { + + private PythonCryptoRSA() { + // private + } + + // RSA generate -> private key + private static final IDetectionRule RSA_GENERATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.RSA", "Cryptodome.PublicKey.RSA") + .forMethods("generate") + // .shouldBeDetectedAs( + // new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withNamedMethodParameter("bits", "int") + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("randfunc", ANY) + .withOptionalNamedMethodParameter("e", "int") + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "RSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // RSA construct and import_key -> public or private key + private static final IDetectionRule RSA_CONSTRUCT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.PublicKey.RSA", "Cryptodome.PublicKey.RSA") + .forMethods("construct", "import_key") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.GENERATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("algorithm", "RSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // RsaKey public_key -> public key + private static final IDetectionRule RSA_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes( + "Crypto.PublicKey.RSA.RsaKey", "Cryptodome.PublicKey.RSA.RsaKey") + .forMethods("public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withoutParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "RSA"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + @Override + protected List> buildRules() { + return List.of(RSA_CONSTRUCT, RSA_GENERATE, RSA_PUBLIC_KEY); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/random/PythonCryptoRandom.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/random/PythonCryptoRandom.java new file mode 100644 index 000000000..bb5c1cb72 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/random/PythonCryptoRandom.java @@ -0,0 +1,66 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.random; + +import com.ibm.engine.model.context.PRNGContext; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import java.util.List; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoRandom extends DetectionRuleSet { + + private PythonCryptoRandom() { + // private + } + + private static final IDetectionRule RANDOM_GET_BYTES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Random", "Cryptodome.Random") + .forMethods("get_random_bytes") + .shouldBeDetectedAs(new ValueActionFactory<>("PRNG")) + .withNamedMethodParameter("N", "int") + .buildForContext(new PRNGContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule RANDOM_FUNC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Random.random", "Cryptodome.Random.random") + .forMethods( + "getrandbits", "randrange", "randint", "choice", "shuffle", "sample") + .shouldBeDetectedAs(new ValueActionFactory<>("PRNG")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + @Nonnull + @Override + protected List> buildRules() { + return List.of(RANDOM_GET_BYTES, RANDOM_FUNC); + } +} diff --git a/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/signature/PythonCryptoSignature.java b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/signature/PythonCryptoSignature.java new file mode 100644 index 000000000..5d3b47435 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/rules/detection/pycrypto/signature/PythonCryptoSignature.java @@ -0,0 +1,290 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static com.ibm.engine.detection.MethodMatcher.ANY; + +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.Size.UnitType; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.model.context.PublicKeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.SaltSizeFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; +import com.ibm.engine.model.factory.ValueActionFactory; +import com.ibm.engine.rule.DetectionRuleSet; +import com.ibm.engine.rule.IDetectionRule; +import com.ibm.engine.rule.RuleSets; +import com.ibm.engine.rule.builder.DetectionRuleBuilder; +import com.ibm.plugin.rules.detection.pycrypto.hash.PythonCryptoHash; +import com.ibm.plugin.rules.detection.pycrypto.publickey.PythonCryptoDSA; +import com.ibm.plugin.rules.detection.pycrypto.publickey.PythonCryptoECC; +import com.ibm.plugin.rules.detection.pycrypto.publickey.PythonCryptoRSA; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1192") +public final class PythonCryptoSignature extends DetectionRuleSet { + + private PythonCryptoSignature() { + // private + } + + private static final IDetectionRule SIGN = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(ANY) + .forMethods("sign") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withNamedMethodParameter("msg_hash", ANY) // Crypto.Hash.* or Cryptodome.Hash.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(RuleSets.rulesOf(PythonCryptoHash.class)) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule VERIFY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(ANY) + .forMethods("verify") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withNamedMethodParameter("msg_hash", ANY) // Crypto.Hash.* or Cryptodome.Hash.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(RuleSets.rulesOf(PythonCryptoHash.class)) + .withNamedMethodParameter("signature", ANY) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // PSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule PKCS1V15 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.pkcs1_15", "Cryptodome.Signature.pkcs1_15") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA-PKCS1V15")) + .withNamedMethodParameter( + "rsa_key", ANY) // Crypto.PublicKey.RSA or Cryptodome.PublicKey.RSA + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(RuleSets.rulesOf(PythonCryptoRSA.class)) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // PSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule PSS = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.pss", "Cryptodome.Signature.pss") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA-PSS")) + .withNamedMethodParameter( + "rsa_key", ANY) // Crypto.PublicKey.RSA or Cryptodome.PublicKey.RSA + // .shouldBeDetectedAs(new AlgorithmFactory<>()) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(RuleSets.rulesOf(PythonCryptoRSA.class)) + .withOptionalNamedMethodParameter("mask_func", ANY) + .withOptionalNamedMethodParameter("salt_bytes", "int") + .shouldBeDetectedAs(new SaltSizeFactory<>(UnitType.BYTE)) + .asChildOfParameterWithId(-1) + .withOptionalNamedMethodParameter("rand_func", ANY) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // PSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule PSS_MGF1 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.pss", "Cryptodome.Signature.pss") + .forMethods("MGF1") + .shouldBeDetectedAs(new ValueActionFactory<>("MGF1")) + .withNamedMethodParameter("mgfSeed", ANY) + .withNamedMethodParameter("maskLen", "int") + .withNamedMethodParameter("hash_gen", ANY) // Crypto.Hash.* or Cryptodome.Hash.* + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // DSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule DSS = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.DSS") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("DSS")) + .withNamedMethodParameter("key", "Crypto.PublicKey.DSA") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(RuleSets.rulesOf(PythonCryptoDSA.class)) + .withNamedMethodParameter("mode", "str") + .withOptionalNamedMethodParameter("encoding", "str") + .withOptionalNamedMethodParameter("randfunc", ANY) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // DSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule DSS_CRYPTODOME = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Cryptodome.Signature.DSS") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("DSS")) + .withNamedMethodParameter("key", "Cryptodome.PublicKey.DSA") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(RuleSets.rulesOf(PythonCryptoDSA.class)) + .withNamedMethodParameter("mode", "str") + .withOptionalNamedMethodParameter("encoding", "str") + .withOptionalNamedMethodParameter("randfunc", ANY) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // DSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule ECDSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.DSS") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("ECDSA")) + .withNamedMethodParameter("key", "Crypto.PublicKey.ECC") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(RuleSets.rulesOf(PythonCryptoECC.class)) + .withNamedMethodParameter("mode", "str") + .withOptionalNamedMethodParameter("encoding", "str") + .withOptionalNamedMethodParameter("randfunc", ANY) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + // DSS signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule ECDSA_CRYPTODOME = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Cryptodome.Signature.DSS") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("ECDSA")) + .withNamedMethodParameter("key", "Cryptodome.PublicKey.ECC") + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules(RuleSets.rulesOf(PythonCryptoECC.class)) + .withNamedMethodParameter("mode", "str") + .withOptionalNamedMethodParameter("encoding", "str") + .withOptionalNamedMethodParameter("randfunc", ANY) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(SIGN, VERIFY)); + + private static final IDetectionRule EDDSA_SIGN = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(ANY) + .forMethods("sign") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withNamedMethodParameter("msg_or_hash", ANY) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + private static final IDetectionRule EDDSA_VERIFY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(ANY) + .forMethods("verify") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withNamedMethodParameter("msg_or_hash", ANY) + .withNamedMethodParameter("signature", ANY) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // EdDSA import private key + private static final IDetectionRule EDDSA_IMPORT_PRIVATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.eddsa", "Cryptodome.Signature.eddsa") + .forMethods("import_private_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PRIVATE_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PrivateKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // EdDSA import public key + private static final IDetectionRule EDDSA_IMPORT_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.eddsa", "Cryptodome.Signature.eddsa") + .forMethods("import_public_key") + .shouldBeDetectedAs( + new KeyActionFactory<>(KeyAction.Action.PUBLIC_KEY_GENERATION)) + .withAnyParameters() + .buildForContext(new PublicKeyContext(Map.of("algorithm", "EC"))) + .inBundle(() -> "PyCrypto") + .withoutDependingDetectionRules(); + + // EdDSA signature scheme - sign and verify methods (called on the result of .new()) + private static final IDetectionRule EDDSA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Crypto.Signature.eddsa", "Cryptodome.Signature.eddsa") + .forMethods("new") + .shouldBeDetectedAs(new ValueActionFactory<>("EDDSA")) + .withNamedMethodParameter( + "key", ANY) // Crypto.PublicKey.ECCkey or Cryptodome.PublicKey.ECCkey + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .asChildOfParameterWithId(-1) + .addDependingDetectionRules( + Stream.concat( + RuleSets.rulesOf(PythonCryptoECC.class).stream(), + Stream.of( + EDDSA_IMPORT_PRIVATE_KEY, + EDDSA_IMPORT_PUBLIC_KEY)) + .toList()) + .withNamedMethodParameter("mode", "str") + .withOptionalNamedMethodParameter("context", ANY) + .buildForContext(new SignatureContext()) + .inBundle(() -> "PyCrypto") + .withDependingDetectionRules(List.of(EDDSA_SIGN, EDDSA_VERIFY)); + + @Nonnull + @Override + protected List> buildRules() { + return List.of( + PKCS1V15, PSS, PSS_MGF1, DSS, DSS_CRYPTODOME, ECDSA, ECDSA_CRYPTODOME, EDDSA); + } +} diff --git a/python/src/main/java/com/ibm/plugin/translation/reorganizer/PythonReorganizerRules.java b/python/src/main/java/com/ibm/plugin/translation/reorganizer/PythonReorganizerRules.java index 6bb880f0e..fd6119d61 100644 --- a/python/src/main/java/com/ibm/plugin/translation/reorganizer/PythonReorganizerRules.java +++ b/python/src/main/java/com/ibm/plugin/translation/reorganizer/PythonReorganizerRules.java @@ -21,12 +21,15 @@ import com.ibm.mapper.model.BlockCipher; import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.ProbabilisticSignatureScheme; import com.ibm.mapper.model.PublicKeyEncryption; import com.ibm.mapper.model.Signature; import com.ibm.mapper.model.functionality.Sign; +import com.ibm.mapper.model.functionality.Verify; import com.ibm.mapper.reorganizer.IReorganizerRule; import com.ibm.mapper.reorganizer.rules.KeyAgreementReorganizer; import com.ibm.mapper.reorganizer.rules.KeyDerivationReorganizer; +import com.ibm.mapper.reorganizer.rules.MessageDigestReorganizer; import com.ibm.mapper.reorganizer.rules.PaddingReorganizer; import com.ibm.mapper.reorganizer.rules.SignatureReorganizer; import java.util.List; @@ -42,6 +45,15 @@ private PythonReorganizerRules() { @Nonnull public static List rules() { return Stream.of( + MessageDigestReorganizer.WRAP_SHA512_TRUNCATED, + SignatureReorganizer.moveNodesFromUnderFunctionalityUnderParent( + Verify.class, ProbabilisticSignatureScheme.class), + SignatureReorganizer.moveNodesFromUnderFunctionalityUnderParent( + Verify.class, Signature.class), + SignatureReorganizer.moveNodesFromUnderFunctionalityUnderParent( + Sign.class, ProbabilisticSignatureScheme.class), + SignatureReorganizer.moveNodesFromUnderFunctionalityUnderParent( + Sign.class, Signature.class), SignatureReorganizer.moveNodesFromUnderFunctionalityUnderNode( Sign.class, PublicKeyEncryption.class), SignatureReorganizer.moveNodesFromUnderFunctionalityUnderNode( @@ -51,6 +63,8 @@ public static List rules() { SignatureReorganizer.MAKE_RSA_TO_SIGNATURE, KeyDerivationReorganizer.moveModeFromParentToNode(BlockCipher.class), KeyDerivationReorganizer.moveModeFromParentToNode(MessageDigest.class), + KeyAgreementReorganizer.REPLACE_ECDH_WITH_X25519_WHEN_CURVE25519, + KeyAgreementReorganizer.REPLACE_ECDH_WITH_X448_WHEN_CURVE448, KeyAgreementReorganizer.MERGE_KEYAGREEMENT_WITH_PKE_UNDER_PRIVATE_KEY, PaddingReorganizer.MOVE_OAEP_UNDER_ALGORITHM) .toList(); diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/PythonTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/PythonTranslator.java index bfb1f5d9a..3de015c65 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/PythonTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/PythonTranslator.java @@ -27,6 +27,7 @@ import com.ibm.engine.model.context.KeyContext; import com.ibm.engine.model.context.KeyDerivationFunctionContext; import com.ibm.engine.model.context.MacContext; +import com.ibm.engine.model.context.PRNGContext; import com.ibm.engine.model.context.PrivateKeyContext; import com.ibm.engine.model.context.PublicKeyContext; import com.ibm.engine.model.context.SecretKeyContext; @@ -38,11 +39,12 @@ import com.ibm.plugin.translation.translator.contexts.PycaCipherContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaDigestContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaKeyAgreementContextTranslator; +import com.ibm.plugin.translation.translator.contexts.PycaKeyContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaKeyDerivationContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaMacContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaPrivateKeyContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaPublicKeyContextTranslator; -import com.ibm.plugin.translation.translator.contexts.PycaSecretContextTranslator; +import com.ibm.plugin.translation.translator.contexts.PycaRandomContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaSecretKeyContextTranslator; import com.ibm.plugin.translation.translator.contexts.PycaSignatureContextTranslator; import java.util.List; @@ -86,11 +88,6 @@ public Optional translate( new PycaKeyDerivationContextTranslator(); return pycaKeyDerivationContextTranslator.translate( bundleIdentifier, value, detectionValueContext, detectionLocation); - } else if (detectionValueContext.is(KeyContext.class)) { - final PycaSecretContextTranslator pycaSecretContextTranslator = - new PycaSecretContextTranslator(); - return pycaSecretContextTranslator.translate( - bundleIdentifier, value, detectionValueContext, detectionLocation); } else if (detectionValueContext.is(PrivateKeyContext.class)) { final PycaPrivateKeyContextTranslator pycaPrivateKeyContextTranslator = new PycaPrivateKeyContextTranslator(); @@ -106,6 +103,11 @@ public Optional translate( new PycaPublicKeyContextTranslator(); return pycaPublicKeyContextTranslator.translate( bundleIdentifier, value, detectionValueContext, detectionLocation); + } else if (detectionValueContext.is(KeyContext.class)) { + final PycaKeyContextTranslator pycaKeyContextTranslator = + new PycaKeyContextTranslator(); + return pycaKeyContextTranslator.translate( + bundleIdentifier, value, detectionValueContext, detectionLocation); } else if (detectionValueContext.is(DigestContext.class)) { final PycaDigestContextTranslator pycaDigestContextTranslator = new PycaDigestContextTranslator(); @@ -126,6 +128,11 @@ public Optional translate( new PycaMacContextTranslator(); return pycaMacContextTranslator.translate( bundleIdentifier, value, detectionValueContext, detectionLocation); + } else if (detectionValueContext.is(PRNGContext.class)) { + final PycaRandomContextTranslator pycaRandomContextTranslator = + new PycaRandomContextTranslator(); + return pycaRandomContextTranslator.translate( + bundleIdentifier, value, detectionValueContext, detectionLocation); } return Optional.empty(); } diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaCipherContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaCipherContextTranslator.java index ec5341e09..4281ecc44 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaCipherContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaCipherContextTranslator.java @@ -19,6 +19,7 @@ */ package com.ibm.plugin.translation.translator.contexts; +import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.IValue; import com.ibm.engine.model.KeySize; @@ -29,10 +30,13 @@ import com.ibm.mapper.IContextTranslation; import com.ibm.mapper.mapper.pyca.PycaCipherMapper; import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.EllipticCurveAlgorithm; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyLength; import com.ibm.mapper.model.KeyWrap; +import com.ibm.mapper.model.PublicKeyEncryption; import com.ibm.mapper.model.algorithms.AES; +import com.ibm.mapper.model.algorithms.RSA; import com.ibm.mapper.model.functionality.Decrypt; import com.ibm.mapper.model.functionality.Encapsulate; import com.ibm.mapper.model.functionality.Encrypt; @@ -40,15 +44,19 @@ import com.ibm.mapper.model.mode.CCM; import com.ibm.mapper.model.mode.CFB; import com.ibm.mapper.model.mode.CTR; +import com.ibm.mapper.model.mode.EAX; import com.ibm.mapper.model.mode.ECB; import com.ibm.mapper.model.mode.GCM; import com.ibm.mapper.model.mode.GCMSIV; +import com.ibm.mapper.model.mode.KW; +import com.ibm.mapper.model.mode.KWP; import com.ibm.mapper.model.mode.OCB; import com.ibm.mapper.model.mode.OFB; import com.ibm.mapper.model.mode.SIV; import com.ibm.mapper.model.mode.XTS; import com.ibm.mapper.model.padding.ANSIX923; import com.ibm.mapper.model.padding.OAEP; +import com.ibm.mapper.model.padding.PKCS1; import com.ibm.mapper.model.padding.PKCS7; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -65,7 +73,7 @@ public final class PycaCipherContextTranslator implements IContextTranslation) { + if (value instanceof Algorithm) { if (detectionContext.get("kind").map(k -> k.equals("AEAD")).orElse(false)) { return switch (value.asString().toUpperCase().trim()) { case "AESGCM" -> @@ -82,28 +90,52 @@ public final class PycaCipherContextTranslator implements IContextTranslation i); - } else if (value instanceof ValueAction - && detectionContext - .get("kind") - .map(k -> k.equals("padding")) - .orElse(false) // padding case - ) { - return switch (value.asString().toUpperCase().trim()) { - case "PKCS7" -> Optional.of(new PKCS7(detectionLocation)); - case "ANSIX923" -> Optional.of(new ANSIX923(detectionLocation)); - case "OAEP" -> Optional.of(new OAEP(detectionLocation)); - default -> Optional.empty(); + } else if (value instanceof ValueAction) { + if (detectionContext + .get("kind") + .map(k -> k.equals("padding")) + .orElse(false)) { // padding case + return switch (value.asString().toUpperCase().trim()) { + case "PKCS7" -> Optional.of(new PKCS7(detectionLocation)); + case "ANSIX923" -> Optional.of(new ANSIX923(detectionLocation)); + case "OAEP" -> Optional.of(new OAEP(detectionLocation)); + default -> Optional.empty(); + }; + } + + // Handle ValueAction with algorithm name (e.g., "AES", "DES", "DES3") + // Get the algorithm name from the ValueAction + String algorithmName = value.asString(); + return switch (algorithmName.trim().toUpperCase()) { + case "PKCS1_OAEP" -> { + RSA rsaOaep = new RSA(PublicKeyEncryption.class, detectionLocation); + rsaOaep.put(new OAEP(detectionLocation)); + yield Optional.of((INode) rsaOaep); + } + case "PKCS1_V1_5" -> { + RSA rsaPkcs1 = new RSA(PublicKeyEncryption.class, detectionLocation); + rsaPkcs1.put(new PKCS1(detectionLocation)); + yield Optional.of((INode) rsaPkcs1); + } + case "HPKE" -> Optional.of(new EllipticCurveAlgorithm(detectionLocation)); + default -> pycaCipherMapper.parse(algorithmName, detectionLocation).map(i -> i); }; } else if (value instanceof Mode mode) { return switch (mode.asString().toUpperCase().trim()) { - case "CBC" -> Optional.of(new CBC(detectionLocation)); - case "CTR" -> Optional.of(new CTR(detectionLocation)); - case "OFB" -> Optional.of(new OFB(detectionLocation)); - case "CFB" -> Optional.of(new CFB(detectionLocation)); + case "CBC", "MODE_CBC" -> Optional.of(new CBC(detectionLocation)); + case "CTR", "MODE_CTR" -> Optional.of(new CTR(detectionLocation)); + case "OFB", "MODE_OFB" -> Optional.of(new OFB(detectionLocation)); + case "MODE_OCB" -> Optional.of(new OCB(detectionLocation)); + case "CFB", "MODE_CFB" -> Optional.of(new CFB(detectionLocation)); case "CFB8" -> Optional.of(new CFB(8, detectionLocation)); - case "GCM" -> Optional.of(new GCM(detectionLocation)); + case "GCM", "MODE_GCM" -> Optional.of(new GCM(detectionLocation)); case "XTS" -> Optional.of(new XTS(detectionLocation)); - case "ECB" -> Optional.of(new ECB(detectionLocation)); + case "ECB", "MODE_ECB" -> Optional.of(new ECB(detectionLocation)); + case "MODE_EAX" -> Optional.of(new EAX(detectionLocation)); + case "CCM", "MODE_CCM" -> Optional.of(new CCM(detectionLocation)); + case "MODE_SIV" -> Optional.of(new SIV(detectionLocation)); + case "MODE_KW" -> Optional.of(new KW(detectionLocation)); + case "MODE_KWP" -> Optional.of(new KWP(detectionLocation)); default -> Optional.empty(); }; } else if (value instanceof CipherAction cipherAction) { diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaDigestContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaDigestContextTranslator.java index 7c09567e2..978667844 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaDigestContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaDigestContextTranslator.java @@ -19,6 +19,8 @@ */ package com.ibm.plugin.translation.translator.contexts; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.DigestSize; import com.ibm.engine.model.IValue; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.DetectionContext; @@ -41,7 +43,13 @@ public final class PycaDigestContextTranslator implements IContextTranslation value, @Nonnull DetectionContext detectionContext, @Nonnull DetectionLocation detectionLocation) { - if (value instanceof ValueAction || value instanceof com.ibm.engine.model.Algorithm) { + if (value instanceof DigestSize digestSize) { + // Detected truncation parameter (e.g. SHA512.new(truncate=224)): + // produce a DigestSize node; a reorganizer rule will reshape the parent. + return Optional.of( + new com.ibm.mapper.model.DigestSize(digestSize.getValue(), detectionLocation)); + } + if (value instanceof ValueAction || value instanceof Algorithm) { final PycaDigestMapper pycaDigestMapper = new PycaDigestMapper(); return pycaDigestMapper .parse(value.asString(), detectionLocation) diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyAgreementContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyAgreementContextTranslator.java index 614573d88..a8a15b3ca 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyAgreementContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyAgreementContextTranslator.java @@ -22,6 +22,7 @@ import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.DetectionContext; import com.ibm.engine.rule.IBundle; import com.ibm.mapper.IContextTranslation; @@ -45,17 +46,19 @@ public class PycaKeyAgreementContextTranslator implements IContextTranslation value, @Nonnull DetectionContext detectionContext, @Nonnull DetectionLocation detectionLocation) { - if (value instanceof Algorithm algorithm) { - return Optional.of(algorithm) + if (value instanceof ValueAction || value instanceof Algorithm) { + return Optional.of(value.asString().toUpperCase().trim()) .map( algo -> - switch (algo.asString().toUpperCase().trim()) { + switch (algo) { case "ECDH" -> new ECDH(detectionLocation); case "EC" -> new EllipticCurveAlgorithm( KeyAgreement.class, new EllipticCurveAlgorithm( detectionLocation)); + case "X25519" -> new X25519(detectionLocation); + case "X448" -> new X448(detectionLocation); default -> null; }) .map( diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSecretContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyContextTranslator.java similarity index 62% rename from python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSecretContextTranslator.java rename to python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyContextTranslator.java index 91703e972..df9bdde20 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSecretContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyContextTranslator.java @@ -19,21 +19,23 @@ */ package com.ibm.plugin.translation.translator.contexts; +import com.ibm.engine.model.Curve; import com.ibm.engine.model.IValue; import com.ibm.engine.model.KeyAction; import com.ibm.engine.model.context.DetectionContext; import com.ibm.engine.rule.IBundle; import com.ibm.mapper.IContextTranslation; +import com.ibm.mapper.mapper.pyca.PycaCurveMapper; +import com.ibm.mapper.mapper.pyca.PycaKeyBasedAlgorithmMapper; import com.ibm.mapper.model.INode; -import com.ibm.mapper.model.SecretKey; -import com.ibm.mapper.model.algorithms.Fernet; +import com.ibm.mapper.model.Key; import com.ibm.mapper.model.functionality.KeyGeneration; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; import javax.annotation.Nonnull; import org.sonar.plugins.python.api.tree.Tree; -public final class PycaSecretContextTranslator implements IContextTranslation { +public final class PycaKeyContextTranslator implements IContextTranslation { @Override public @Nonnull Optional translate( @Nonnull IBundle bundleIdentifier, @@ -42,18 +44,30 @@ public final class PycaSecretContextTranslator implements IContextTranslation) { // action is always "generate" + final PycaKeyBasedAlgorithmMapper mapper = new PycaKeyBasedAlgorithmMapper(); return detectionContext .get("algorithm") - .map( - str -> - switch (str.toUpperCase().trim()) { - case "FERNET" -> new Fernet(detectionLocation); - default -> null; - }) + .flatMap(str -> mapper.parse(str, detectionLocation)) .map( algo -> { - final SecretKey key = new SecretKey(algo); + final Key key = new Key(algo); + key.put(new KeyGeneration(detectionLocation)); + return key; + }); + } else if (value instanceof Curve curve + && detectionContext + .get("algorithm") + .map(a -> a.equalsIgnoreCase("EC")) + .orElse(false)) { + final PycaCurveMapper mapper = new PycaCurveMapper(); + return mapper.parse(curve.asString(), detectionLocation) + .map( + ec -> { + Key key = new Key(ec); key.put(new KeyGeneration(detectionLocation)); + // currently only GENERATE is + // used as key action is this + // context return key; }); } diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyDerivationContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyDerivationContextTranslator.java index 9dca1a1c6..db285ddc4 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyDerivationContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaKeyDerivationContextTranslator.java @@ -21,8 +21,10 @@ import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; +import com.ibm.engine.model.IterationCount; import com.ibm.engine.model.KeySize; import com.ibm.engine.model.Mode; +import com.ibm.engine.model.SaltSize; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.DetectionContext; import com.ibm.engine.rule.IBundle; @@ -32,11 +34,15 @@ import com.ibm.mapper.model.Cipher; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.NumberOfIterations; +import com.ibm.mapper.model.SaltLength; import com.ibm.mapper.model.algorithms.ANSIX963; import com.ibm.mapper.model.algorithms.CMAC; import com.ibm.mapper.model.algorithms.ConcatenationKDF; import com.ibm.mapper.model.algorithms.HKDF; import com.ibm.mapper.model.algorithms.HMAC; +import com.ibm.mapper.model.algorithms.KDFCounter; +import com.ibm.mapper.model.algorithms.PBKDF1; import com.ibm.mapper.model.algorithms.PBKDF2; import com.ibm.mapper.model.algorithms.Scrypt; import com.ibm.mapper.model.functionality.KeyDerivation; @@ -99,6 +105,17 @@ public class PycaKeyDerivationContextTranslator implements IContextTranslation { + final PycaDigestMapper digestMapper = new PycaDigestMapper(); + yield digestMapper + .parse(algorithm.asString(), detectionLocation) + .map( + kdf -> { + final PBKDF1 pbkdf1 = new PBKDF1(kdf); + pbkdf1.put(new KeyDerivation(detectionLocation)); + return pbkdf1; + }); + } case "pbkdf2" -> { final PycaDigestMapper digestMapper = new PycaDigestMapper(); yield digestMapper @@ -110,6 +127,14 @@ public class PycaKeyDerivationContextTranslator implements IContextTranslation { + // ValueAction already creates the root KDF node for these rules; + // the Algorithm child should contribute only the digest. + final PycaDigestMapper digestMapper = new PycaDigestMapper(); + yield digestMapper + .parse(algorithm.asString(), detectionLocation) + .map(digest -> (INode) digest); + } case "concatkdf" -> { final PycaDigestMapper digestMapper = new PycaDigestMapper(); yield digestMapper @@ -144,12 +169,22 @@ public class PycaKeyDerivationContextTranslator implements IContextTranslation keySize) { return Optional.of(new KeyLength(keySize.getValue(), detectionLocation)); + } else if (value instanceof IterationCount iterationCount) { + return Optional.of( + new NumberOfIterations(iterationCount.getValue(), detectionLocation)); + } else if (value instanceof SaltSize saltSize) { + return Optional.of(new SaltLength(saltSize.getValue(), detectionLocation)); } else if (value instanceof ValueAction action) { return Optional.of(action.asString().toUpperCase().trim()) .map( str -> - switch (action.asString().toUpperCase().trim()) { + switch (str) { + case "PBKDF1" -> new PBKDF1(detectionLocation); + case "PBKDF2" -> new PBKDF2(detectionLocation); + case "HKDF" -> new HKDF(detectionLocation); case "SCRYPT" -> new Scrypt(detectionLocation); + case "SP800_108_COUNTER" -> + new KDFCounter(detectionLocation); default -> null; }) .map( diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaMacContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaMacContextTranslator.java index 26db48048..b3e92618e 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaMacContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaMacContextTranslator.java @@ -19,6 +19,7 @@ */ package com.ibm.plugin.translation.translator.contexts; +import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.DetectionContext; @@ -26,11 +27,11 @@ import com.ibm.mapper.IContextTranslation; import com.ibm.mapper.mapper.pyca.PycaCipherMapper; import com.ibm.mapper.mapper.pyca.PycaDigestMapper; +import com.ibm.mapper.mapper.pyca.PycaMacMapper; import com.ibm.mapper.model.Cipher; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.algorithms.CMAC; import com.ibm.mapper.model.algorithms.HMAC; -import com.ibm.mapper.model.algorithms.Poly1305; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; import javax.annotation.Nonnull; @@ -46,7 +47,7 @@ public final class PycaMacContextTranslator implements IContextTranslation @Nonnull DetectionContext detectionContext, @Nonnull DetectionLocation detectionLocation) { - if (value instanceof com.ibm.engine.model.Algorithm algorithm) { + if (value instanceof Algorithm algorithm) { // hash algorithm Optional possibleKind = detectionContext.get("kind"); if (possibleKind.isPresent()) { @@ -70,13 +71,20 @@ public final class PycaMacContextTranslator implements IContextTranslation .parse(algorithm.asString(), detectionLocation) .map(HMAC::new); } + // the cipher used to derive the Poly1305 key pair; it is attached as a child + // of the Poly1305 node + case "poly1305" -> { + final PycaCipherMapper cipherMapper = new PycaCipherMapper(); + yield cipherMapper + .parse(algorithm.asString(), detectionLocation) + .map(c -> c); + } default -> Optional.empty(); }; } } else if (value instanceof ValueAction action) { - if (action.asString().equalsIgnoreCase("poly1305")) { - return Optional.of(new HMAC(new Poly1305(detectionLocation))); - } + final PycaMacMapper macMapper = new PycaMacMapper(); + return macMapper.parse(action.asString(), detectionLocation).map(n -> n); } return Optional.empty(); } diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPrivateKeyContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPrivateKeyContextTranslator.java index a997d583f..163134775 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPrivateKeyContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPrivateKeyContextTranslator.java @@ -26,35 +26,15 @@ import com.ibm.engine.model.context.DetectionContext; import com.ibm.engine.rule.IBundle; import com.ibm.mapper.IContextTranslation; -import com.ibm.mapper.model.EllipticCurveAlgorithm; +import com.ibm.mapper.mapper.pyca.PycaCurveMapper; +import com.ibm.mapper.mapper.pyca.PycaKeyBasedAlgorithmMapper; import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.KeyAgreement; import com.ibm.mapper.model.KeyLength; import com.ibm.mapper.model.PrivateKey; import com.ibm.mapper.model.PublicKeyEncryption; -import com.ibm.mapper.model.algorithms.DH; -import com.ibm.mapper.model.algorithms.DSA; -import com.ibm.mapper.model.algorithms.Ed25519; -import com.ibm.mapper.model.algorithms.Ed448; -import com.ibm.mapper.model.algorithms.RSA; -import com.ibm.mapper.model.curves.Brainpoolp256r1; -import com.ibm.mapper.model.curves.Brainpoolp384r1; -import com.ibm.mapper.model.curves.Brainpoolp512r1; -import com.ibm.mapper.model.curves.Secp192r1; -import com.ibm.mapper.model.curves.Secp224r1; -import com.ibm.mapper.model.curves.Secp256k1; -import com.ibm.mapper.model.curves.Secp256r1; -import com.ibm.mapper.model.curves.Secp384r1; -import com.ibm.mapper.model.curves.Secp521r1; -import com.ibm.mapper.model.curves.Sect163k1; -import com.ibm.mapper.model.curves.Sect163r2; -import com.ibm.mapper.model.curves.Sect233k1; -import com.ibm.mapper.model.curves.Sect233r1; -import com.ibm.mapper.model.curves.Sect283k1; -import com.ibm.mapper.model.curves.Sect283r1; -import com.ibm.mapper.model.curves.Sect409k1; -import com.ibm.mapper.model.curves.Sect409r1; -import com.ibm.mapper.model.curves.Sect571k1; -import com.ibm.mapper.model.curves.Sect571r1; +import com.ibm.mapper.model.Signature; import com.ibm.mapper.model.functionality.KeyGeneration; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -83,43 +63,20 @@ public final class PycaPrivateKeyContextTranslator implements IContextTranslatio .get("algorithm") .map(a -> a.equalsIgnoreCase("EC")) .orElse(false)) { - return Optional.of(curve.asString()) - .map( - str -> - switch (str.toUpperCase().trim()) { - case "SECP256R1" -> new Secp256r1(detectionLocation); - case "SECP384R1" -> new Secp384r1(detectionLocation); - case "SECP521R1" -> new Secp521r1(detectionLocation); - case "SECP224R1" -> new Secp224r1(detectionLocation); - case "SECP192R1" -> new Secp192r1(detectionLocation); - case "SECP256K1" -> new Secp256k1(detectionLocation); - case "BRAINPOOLP256R1" -> - new Brainpoolp256r1(detectionLocation); - case "BRAINPOOLP384R1" -> - new Brainpoolp384r1(detectionLocation); - case "BRAINPOOLP512R1" -> - new Brainpoolp512r1(detectionLocation); - case "SECT571K1" -> new Sect571k1(detectionLocation); - case "SECT409K1" -> new Sect409k1(detectionLocation); - case "SECT283K1" -> new Sect283k1(detectionLocation); - case "SECT233K1" -> new Sect233k1(detectionLocation); - case "SECT163K1" -> new Sect163k1(detectionLocation); - case "SECT571R1" -> new Sect571r1(detectionLocation); - case "SECT409R1" -> new Sect409r1(detectionLocation); - case "SECT283R1" -> new Sect283r1(detectionLocation); - case "SECT233R1" -> new Sect233r1(detectionLocation); - case "SECT163R2" -> new Sect163r2(detectionLocation); - default -> null; - }) - .map(EllipticCurveAlgorithm::new) + final PycaCurveMapper mapper = new PycaCurveMapper(); + return mapper.parse(curve.asString(), detectionLocation) .map( ec -> { - PrivateKey privateKey = new PrivateKey((PublicKeyEncryption) ec); - privateKey.put( - new KeyGeneration( - detectionLocation)); // currently only GENERATE is - // used as key action is this - // context + // currently only GENERATE is used as key action in this context + PrivateKey privateKey; + if (ec instanceof Signature sig) { + privateKey = new PrivateKey(sig); + } else if (ec instanceof KeyAgreement ka) { + privateKey = new PrivateKey(ka); + } else { + privateKey = new PrivateKey((PublicKeyEncryption) ec); + } + privateKey.put(new KeyGeneration(detectionLocation)); return privateKey; }); } @@ -130,27 +87,14 @@ public final class PycaPrivateKeyContextTranslator implements IContextTranslatio @Nonnull DetectionContext detectionContext, @Nullable Integer keySize, @Nonnull DetectionLocation detectionLocation) { + final PycaKeyBasedAlgorithmMapper mapper = new PycaKeyBasedAlgorithmMapper(); return detectionContext .get("algorithm") + .flatMap(str -> mapper.parse(str, detectionLocation)) + .map(algorithm -> new PrivateKey(new Key(algorithm))) .map( - str -> - switch (str.toUpperCase().trim()) { - case "DH" -> new DH(detectionLocation); - case "RSA" -> new RSA(detectionLocation); - case "DSA" -> new DSA(detectionLocation); - case "EC" -> new EllipticCurveAlgorithm(detectionLocation); - case "ED25519" -> new Ed25519(detectionLocation); - case "ED448" -> new Ed448(detectionLocation); - default -> null; - }) - .map( - algorithm -> { - PrivateKey privateKey = new PrivateKey(algorithm); - privateKey.put( - new KeyGeneration( - detectionLocation)); // currently only GENERATE is - // used as key action is this - // context + privateKey -> { + privateKey.put(new KeyGeneration(detectionLocation)); return privateKey; }) .map( diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPublicKeyContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPublicKeyContextTranslator.java index b876f11d4..63ed4b6d1 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPublicKeyContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaPublicKeyContextTranslator.java @@ -19,16 +19,20 @@ */ package com.ibm.plugin.translation.translator.contexts; +import com.ibm.engine.model.Curve; import com.ibm.engine.model.IValue; import com.ibm.engine.model.KeyAction; import com.ibm.engine.model.context.DetectionContext; import com.ibm.engine.rule.IBundle; import com.ibm.mapper.IContextTranslation; +import com.ibm.mapper.mapper.pyca.PycaCurveMapper; +import com.ibm.mapper.mapper.pyca.PycaKeyBasedAlgorithmMapper; import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.KeyAgreement; import com.ibm.mapper.model.PublicKey; -import com.ibm.mapper.model.algorithms.DH; -import com.ibm.mapper.model.algorithms.DSA; -import com.ibm.mapper.model.algorithms.RSA; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; import com.ibm.mapper.model.functionality.KeyGeneration; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; @@ -45,24 +49,31 @@ public final class PycaPublicKeyContextTranslator implements IContextTranslation @Nonnull DetectionContext detectionContext, @Nonnull DetectionLocation detectionLocation) { if (value instanceof KeyAction) { + final PycaKeyBasedAlgorithmMapper mapper = new PycaKeyBasedAlgorithmMapper(); return detectionContext .get("algorithm") + .flatMap(str -> mapper.parse(str, detectionLocation)) + .map(algorithm -> new PublicKey(new Key(algorithm))) .map( - algorithm -> - switch (algorithm.toUpperCase().trim()) { - case "DH" -> new DH(detectionLocation); - case "RSA" -> new RSA(detectionLocation); - case "DSA" -> new DSA(detectionLocation); - default -> null; - }) + publicKey -> { + publicKey.put(new KeyGeneration(detectionLocation)); + return publicKey; + }); + } else if (value instanceof Curve) { + final PycaCurveMapper mapper = new PycaCurveMapper(); + return mapper.parse(value.asString(), detectionLocation) .map( - algorithm -> { - PublicKey publicKey = new PublicKey(algorithm); - publicKey.put( - new KeyGeneration( - detectionLocation)); // currently only GENERATE is - // used as key action is this - // context + algo -> { + // currently only GENERATE is used as key action in this context + PublicKey publicKey; + if (algo instanceof Signature sig) { + publicKey = new PublicKey(sig); + } else if (algo instanceof KeyAgreement ka) { + publicKey = new PublicKey(ka); + } else { + publicKey = new PublicKey((PublicKeyEncryption) algo); + } + publicKey.put(new KeyGeneration(detectionLocation)); return publicKey; }); } diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaRandomContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaRandomContextTranslator.java new file mode 100644 index 000000000..9e6458896 --- /dev/null +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaRandomContextTranslator.java @@ -0,0 +1,57 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.translation.translator.contexts; + +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DetectionContext; +import com.ibm.engine.rule.IBundle; +import com.ibm.mapper.IContextTranslation; +import com.ibm.mapper.model.Algorithm; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.PseudorandomNumberGenerator; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; +import javax.annotation.Nonnull; +import org.sonar.plugins.python.api.tree.Tree; + +@SuppressWarnings("java:S1301") +public final class PycaRandomContextTranslator implements IContextTranslation { + + @Override + public @Nonnull Optional translate( + @Nonnull IBundle bundleIdentifier, + @Nonnull IValue value, + @Nonnull DetectionContext detectionContext, + @Nonnull DetectionLocation detectionLocation) { + if (value instanceof ValueAction) { + return switch (value.asString().toUpperCase().trim()) { + case "PRNG" -> + Optional.of( + new Algorithm( + "PRNG", + PseudorandomNumberGenerator.class, + detectionLocation)); + default -> Optional.empty(); + }; + } + return Optional.empty(); + } +} diff --git a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSignatureContextTranslator.java b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSignatureContextTranslator.java index 78bb48ba5..4f7072294 100644 --- a/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSignatureContextTranslator.java +++ b/python/src/main/java/com/ibm/plugin/translation/translator/contexts/PycaSignatureContextTranslator.java @@ -29,12 +29,15 @@ import com.ibm.mapper.model.INode; import com.ibm.mapper.model.ProbabilisticSignatureScheme; import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.algorithms.DSS; import com.ibm.mapper.model.algorithms.ECDSA; +import com.ibm.mapper.model.algorithms.EdDSA; import com.ibm.mapper.model.algorithms.MGF1; import com.ibm.mapper.model.algorithms.RSA; import com.ibm.mapper.model.algorithms.RSAssaPSS; import com.ibm.mapper.model.functionality.Sign; import com.ibm.mapper.model.functionality.Verify; +import com.ibm.mapper.model.padding.PKCS1; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; import javax.annotation.Nonnull; @@ -82,7 +85,16 @@ public final class PycaSignatureContextTranslator implements IContextTranslation }; } else { return switch (value.asString().toUpperCase().trim()) { + case "RSA" -> Optional.of(new RSA(Signature.class, detectionLocation)); + case "DSS" -> Optional.of(new DSS(detectionLocation)); + case "ECDSA" -> Optional.of(new ECDSA(detectionLocation)); + case "EDDSA" -> Optional.of(new EdDSA(detectionLocation)); case "MGF1" -> Optional.of(new MGF1(detectionLocation)); + case "RSA-PKCS1V15" -> { + RSA rsaPkcs1 = new RSA(Signature.class, detectionLocation); + rsaPkcs1.put(new PKCS1(detectionLocation)); + yield Optional.of((INode) rsaPkcs1); + } case "RSA-PSS" -> Optional.of(new RSAssaPSS(detectionLocation)); default -> Optional.empty(); }; diff --git a/python/src/test/files/rules/detection/aead/PycaAESGCMTestFile.py b/python/src/test/files/rules/detection/pyca/aead/PycaAESGCMTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/aead/PycaAESGCMTestFile.py rename to python/src/test/files/rules/detection/pyca/aead/PycaAESGCMTestFile.py diff --git a/python/src/test/files/rules/detection/aead/PycaChaCha20Poly1305TestFile.py b/python/src/test/files/rules/detection/pyca/aead/PycaChaCha20Poly1305TestFile.py similarity index 100% rename from python/src/test/files/rules/detection/aead/PycaChaCha20Poly1305TestFile.py rename to python/src/test/files/rules/detection/pyca/aead/PycaChaCha20Poly1305TestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/DSA/PycaDSANumbersTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/DSA/PycaDSANumbersTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/DSA/PycaDSASignTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSASignTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/DSA/PycaDSASignTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSASignTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSADecryptTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/RSA/PycaRSADecryptTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSANumbersTestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/RSA/PycaRSANumbersTestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign1TestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign1TestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign1TestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign1TestFile.py diff --git a/python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign2TestFile.py b/python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign2TestFile.py similarity index 100% rename from python/src/test/files/rules/detection/asymmetric/RSA/PycaRSASign2TestFile.py rename to python/src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign2TestFile.py diff --git a/python/src/test/files/rules/detection/fernet/PycaFernetDecryptTestFile.py b/python/src/test/files/rules/detection/pyca/fernet/PycaFernetDecryptTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/fernet/PycaFernetDecryptTestFile.py rename to python/src/test/files/rules/detection/pyca/fernet/PycaFernetDecryptTestFile.py diff --git a/python/src/test/files/rules/detection/fernet/PycaFernetEncryptTestFile.py b/python/src/test/files/rules/detection/pyca/fernet/PycaFernetEncryptTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/fernet/PycaFernetEncryptTestFile.py rename to python/src/test/files/rules/detection/pyca/fernet/PycaFernetEncryptTestFile.py diff --git a/python/src/test/files/rules/detection/fernet/PycaMultiFernetTestFile.py b/python/src/test/files/rules/detection/pyca/fernet/PycaMultiFernetTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/fernet/PycaMultiFernetTestFile.py rename to python/src/test/files/rules/detection/pyca/fernet/PycaMultiFernetTestFile.py diff --git a/python/src/test/files/rules/detection/hash/PycaHashDirectTest.py b/python/src/test/files/rules/detection/pyca/hash/PycaHashDirectTest.py similarity index 100% rename from python/src/test/files/rules/detection/hash/PycaHashDirectTest.py rename to python/src/test/files/rules/detection/pyca/hash/PycaHashDirectTest.py diff --git a/python/src/test/files/rules/detection/kdf/PycaConcatKDFHMACTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHMACTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/kdf/PycaConcatKDFHMACTestFile.py rename to python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHMACTestFile.py diff --git a/python/src/test/files/rules/detection/kdf/PycaConcatKDFHashTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHashTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/kdf/PycaConcatKDFHashTestFile.py rename to python/src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHashTestFile.py diff --git a/python/src/test/files/rules/detection/kdf/PycaHKDFExpandTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaHKDFExpandTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/kdf/PycaHKDFExpandTestFile.py rename to python/src/test/files/rules/detection/pyca/kdf/PycaHKDFExpandTestFile.py diff --git a/python/src/test/files/rules/detection/kdf/PycaHKDFTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaHKDFTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/kdf/PycaHKDFTestFile.py rename to python/src/test/files/rules/detection/pyca/kdf/PycaHKDFTestFile.py diff --git a/python/src/test/files/rules/detection/kdf/PycaKBKDFCMACTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFCMACTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/kdf/PycaKBKDFCMACTestFile.py rename to python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFCMACTestFile.py diff --git a/python/src/test/files/rules/detection/kdf/PycaKBKDFHMACTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFHMACTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/kdf/PycaKBKDFHMACTestFile.py rename to python/src/test/files/rules/detection/pyca/kdf/PycaKBKDFHMACTestFile.py diff --git a/python/src/test/files/rules/detection/kdf/PycaPBKDF2TestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaPBKDF2TestFile.py similarity index 100% rename from python/src/test/files/rules/detection/kdf/PycaPBKDF2TestFile.py rename to python/src/test/files/rules/detection/pyca/kdf/PycaPBKDF2TestFile.py diff --git a/python/src/test/files/rules/detection/kdf/PycaScryptTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaScryptTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/kdf/PycaScryptTestFile.py rename to python/src/test/files/rules/detection/pyca/kdf/PycaScryptTestFile.py diff --git a/python/src/test/files/rules/detection/kdf/PycaX963KDFTestFile.py b/python/src/test/files/rules/detection/pyca/kdf/PycaX963KDFTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/kdf/PycaX963KDFTestFile.py rename to python/src/test/files/rules/detection/pyca/kdf/PycaX963KDFTestFile.py diff --git a/python/src/test/files/rules/detection/keyagreement/PycaKeyAgreementTestFile.py b/python/src/test/files/rules/detection/pyca/keyagreement/PycaKeyAgreementTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/keyagreement/PycaKeyAgreementTestFile.py rename to python/src/test/files/rules/detection/pyca/keyagreement/PycaKeyAgreementTestFile.py diff --git a/python/src/test/files/rules/detection/mac/PycaCMACTestFile.py b/python/src/test/files/rules/detection/pyca/mac/PycaCMACTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/mac/PycaCMACTestFile.py rename to python/src/test/files/rules/detection/pyca/mac/PycaCMACTestFile.py diff --git a/python/src/test/files/rules/detection/mac/PycaHMACTestFile.py b/python/src/test/files/rules/detection/pyca/mac/PycaHMACTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/mac/PycaHMACTestFile.py rename to python/src/test/files/rules/detection/pyca/mac/PycaHMACTestFile.py diff --git a/python/src/test/files/rules/detection/mac/PycaMacDetectionInCustomFunctionTestFile.py b/python/src/test/files/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/mac/PycaMacDetectionInCustomFunctionTestFile.py rename to python/src/test/files/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTestFile.py diff --git a/python/src/test/files/rules/detection/mac/PycaPoly1305TestFile.py b/python/src/test/files/rules/detection/pyca/mac/PycaPoly1305TestFile.py similarity index 89% rename from python/src/test/files/rules/detection/mac/PycaPoly1305TestFile.py rename to python/src/test/files/rules/detection/pyca/mac/PycaPoly1305TestFile.py index b51c92c91..2fbaec802 100644 --- a/python/src/test/files/rules/detection/mac/PycaPoly1305TestFile.py +++ b/python/src/test/files/rules/detection/pyca/mac/PycaPoly1305TestFile.py @@ -2,7 +2,7 @@ def generate_poly1305(key, data): # Create a Poly1305 context with the given key - poly1305_ctx = Poly1305(key) # Noncompliant {{(Mac) HMAC-Poly1305}} + poly1305_ctx = Poly1305(key) # Noncompliant {{(Mac) Poly1305}} # Update the context with the data poly1305_ctx.update(data) diff --git a/python/src/test/files/rules/detection/padding/PycaPaddingTestFile.py b/python/src/test/files/rules/detection/pyca/padding/PycaPaddingTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/padding/PycaPaddingTestFile.py rename to python/src/test/files/rules/detection/pyca/padding/PycaPaddingTestFile.py diff --git a/python/src/test/files/rules/detection/symmetric/PycaCipher1TestFile.py b/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher1TestFile.py similarity index 100% rename from python/src/test/files/rules/detection/symmetric/PycaCipher1TestFile.py rename to python/src/test/files/rules/detection/pyca/symmetric/PycaCipher1TestFile.py diff --git a/python/src/test/files/rules/detection/symmetric/PycaCipher2TestFile.py b/python/src/test/files/rules/detection/pyca/symmetric/PycaCipher2TestFile.py similarity index 100% rename from python/src/test/files/rules/detection/symmetric/PycaCipher2TestFile.py rename to python/src/test/files/rules/detection/pyca/symmetric/PycaCipher2TestFile.py diff --git a/python/src/test/files/rules/detection/symmetric/PycaStreamCipher1TestFile.py b/python/src/test/files/rules/detection/pyca/symmetric/PycaStreamCipher1TestFile.py similarity index 100% rename from python/src/test/files/rules/detection/symmetric/PycaStreamCipher1TestFile.py rename to python/src/test/files/rules/detection/pyca/symmetric/PycaStreamCipher1TestFile.py diff --git a/python/src/test/files/rules/detection/wrapping/PycaWrappingTestFile.py b/python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/wrapping/PycaWrappingTestFile.py rename to python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingTestFile.py diff --git a/python/src/test/files/rules/detection/wrapping/PycaWrappingWithPaddingTestFile.py b/python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTestFile.py similarity index 100% rename from python/src/test/files/rules/detection/wrapping/PycaWrappingWithPaddingTestFile.py rename to python/src/test/files/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTestFile.py diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/AESTestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/AESTestFile.py new file mode 100644 index 000000000..ed60adc81 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/AESTestFile.py @@ -0,0 +1,5 @@ +from Crypto.Cipher import AES + +key_aes = b"0123456789abcdef" +cipher_aes = AES.new(key_aes, AES.MODE_CBC, iv=b'some init vector') # Noncompliant {{(BlockCipher) AES-CBC}} +cipher_aes.encrypt(b'some message') diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/BlowfishTestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/BlowfishTestFile.py new file mode 100644 index 000000000..acbb3603a --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/BlowfishTestFile.py @@ -0,0 +1,4 @@ +from Crypto.Cipher import Blowfish + +key_blowfish = b"0123456789abcdef" +cipher_blowfish = Blowfish.new(key_blowfish, Blowfish.MODE_CBC) # Noncompliant {{(BlockCipher) Blowfish-CBC}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/CAST5TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/CAST5TestFile.py new file mode 100644 index 000000000..dd654fcea --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/CAST5TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Cipher import CAST + +key_cast = b"0123456789abcdef" +cipher_cast = CAST.new(key_cast, CAST.MODE_CBC) # Noncompliant {{(BlockCipher) CAST5-CBC}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20Poly1305TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20Poly1305TestFile.py new file mode 100644 index 000000000..2f69ea0be --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20Poly1305TestFile.py @@ -0,0 +1,7 @@ +from Crypto.Cipher import ChaCha20_Poly1305 + +key_chacha20_poly1305 = b"0123456789abcdef0123456789abcdef" +nonce_chacha20_poly1305 = b"01234567" +cipher_chacha20_poly1305 = ChaCha20_Poly1305.new( # Noncompliant {{(AuthenticatedEncryption) ChaCha20-Poly1305}} + key=key_chacha20_poly1305, + nonce=nonce_chacha20_poly1305) diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20TestFile.py new file mode 100644 index 000000000..be90c8ffb --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/ChaCha20TestFile.py @@ -0,0 +1,5 @@ +from Crypto.Cipher import ChaCha20 + +key_chacha20 = b"0123456789abcdef0123456789abcdef" +nonce_chacha20 = b"01234567" +cipher_chacha20 = ChaCha20.new(key=key_chacha20, nonce=nonce_chacha20) # Noncompliant {{(StreamCipher) ChaCha20}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/DESTestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/DESTestFile.py new file mode 100644 index 000000000..bb956ab8e --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/DESTestFile.py @@ -0,0 +1,5 @@ +from Crypto.Cipher import DES + +key_des = b"01234567" +cipher_des = DES.new(key_des, DES.MODE_ECB) # Noncompliant {{(BlockCipher) DES-56-ECB}} +cipher_des.decrypt(b'some blob') diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1OAEPTestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1OAEPTestFile.py new file mode 100644 index 000000000..d6b667f2a --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1OAEPTestFile.py @@ -0,0 +1,6 @@ +from Crypto.Cipher import PKCS1_OAEP +from Crypto.PublicKey import RSA +from Crypto.Hash import SHA256 + +key = RSA.import_key(open('public.pem').read()) +cipher = PKCS1_OAEP.new(key, hashAlgo=SHA256) # Noncompliant {{(PublicKeyEncryption) RSA-OAEP}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1v15TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1v15TestFile.py new file mode 100644 index 000000000..adee56079 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/PKCS1v15TestFile.py @@ -0,0 +1,5 @@ +from Crypto.Cipher import PKCS1_v1_5 +from Crypto.PublicKey import RSA + +key = RSA.importKey(open('public.pem').read()) +cipher = PKCS1_v1_5.new(key) # Noncompliant {{(PublicKeyEncryption) RSA}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/RC2TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/RC2TestFile.py new file mode 100644 index 000000000..9ba68e2c4 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/RC2TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Cipher import ARC2 + +key_arc2 = b"0123456789abcdef" +cipher_arc2 = ARC2.new(key_arc2, ARC2.MODE_CBC) # Noncompliant {{(BlockCipher) RC2-CBC}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/RC4TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/RC4TestFile.py new file mode 100644 index 000000000..c1d08fd5f --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/RC4TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Cipher import ARC4 + +key_arc4 = b"0123456789abcdef" +cipher_arc4 = ARC4.new(key_arc4) # Noncompliant {{(StreamCipher) RC4}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/Salsa20TestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/Salsa20TestFile.py new file mode 100644 index 000000000..13d498ade --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/Salsa20TestFile.py @@ -0,0 +1,5 @@ +from Crypto.Cipher import Salsa20 + +key_salsa20 = b"0123456789abcdef0123456789abcdef" +nonce_salsa20 = b"01234567" +cipher_salsa20 = Salsa20.new(key=key_salsa20, nonce=nonce_salsa20) # Noncompliant {{(StreamCipher) Salsa20}} diff --git a/python/src/test/files/rules/detection/pycrypto/cipher/TripleDESTestFile.py b/python/src/test/files/rules/detection/pycrypto/cipher/TripleDESTestFile.py new file mode 100644 index 000000000..45ac67190 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/cipher/TripleDESTestFile.py @@ -0,0 +1,4 @@ +from Crypto.Cipher import DES3 + +key_3des = b"0123456789abcdef01234567" +cipher_3des = DES3.new(key_3des, DES3.MODE_CBC) # Noncompliant {{(BlockCipher) 3DES-CBC}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2bTestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2bTestFile.py new file mode 100644 index 000000000..190bcdf7f --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2bTestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import BLAKE2b + +hash_blake2b = BLAKE2b.new() # Noncompliant {{(MessageDigest) BLAKE2b}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2sTestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2sTestFile.py new file mode 100644 index 000000000..5b5928fea --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/BLAKE2sTestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import BLAKE2s + +hash_blake2s = BLAKE2s.new() # Noncompliant {{(MessageDigest) BLAKE2s}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/MD2TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/MD2TestFile.py new file mode 100644 index 000000000..8c1894f04 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/MD2TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import MD2 + +hash_md2 = MD2.new() # Noncompliant {{(MessageDigest) MD2}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/MD5TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/MD5TestFile.py new file mode 100644 index 000000000..4d830526b --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/MD5TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import MD5 + +hash_md5 = MD5.new() # Noncompliant {{(MessageDigest) MD5}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/RIPEMD160TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/RIPEMD160TestFile.py new file mode 100644 index 000000000..8c5d05634 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/RIPEMD160TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import RIPEMD160 + +hash_ripemd160 = RIPEMD160.new() # Noncompliant {{(MessageDigest) RIPEMD-160}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA1TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA1TestFile.py new file mode 100644 index 000000000..49966efe3 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA1TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA1 + +hash_sha1 = SHA1.new() # Noncompliant {{(MessageDigest) SHA-1}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA224TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA224TestFile.py new file mode 100644 index 000000000..b6cdab156 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA224TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA224 + +hash_sha224 = SHA224.new() # Noncompliant {{(MessageDigest) SHA-224}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA256TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA256TestFile.py new file mode 100644 index 000000000..46a45ccd2 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA256TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA256 + +hash_sha256 = SHA256.new() # Noncompliant {{(MessageDigest) SHA-256}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA384TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA384TestFile.py new file mode 100644 index 000000000..69c9d06d9 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA384TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA384 + +hash_sha384 = SHA384.new() # Noncompliant {{(MessageDigest) SHA-384}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA3_224TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_224TestFile.py new file mode 100644 index 000000000..c539ca916 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_224TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA3_224 + +hash_sha3_224 = SHA3_224.new() # Noncompliant {{(MessageDigest) SHA3-224}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA3_256TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_256TestFile.py new file mode 100644 index 000000000..c6975d0a1 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_256TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA3_256 + +hash_sha3_256 = SHA3_256.new() # Noncompliant {{(MessageDigest) SHA3-256}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA3_384TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_384TestFile.py new file mode 100644 index 000000000..03b29d382 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_384TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA3_384 + +hash_sha3_384 = SHA3_384.new() # Noncompliant {{(MessageDigest) SHA3-384}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA3_512TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_512TestFile.py new file mode 100644 index 000000000..338c5b305 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA3_512TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA3_512 + +hash_sha3_512 = SHA3_512.new() # Noncompliant {{(MessageDigest) SHA3-512}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA512TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA512TestFile.py new file mode 100644 index 000000000..d49f1fe8a --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA512TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA512 + +hash_sha512 = SHA512.new() # Noncompliant {{(MessageDigest) SHA-512}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA512_224TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA512_224TestFile.py new file mode 100644 index 000000000..a6a808325 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA512_224TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA512 + +hash_sha512 = SHA512.new(truncate="224") # Noncompliant {{(MessageDigest) SHA-512/224}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/SHA512_256TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/SHA512_256TestFile.py new file mode 100644 index 000000000..d41a8794e --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/SHA512_256TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import SHA512 + +hash_sha512 = SHA512.new(truncate="256") # Noncompliant {{(MessageDigest) SHA-512/256}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/TupleHash128TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/TupleHash128TestFile.py new file mode 100644 index 000000000..6c9c11419 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/TupleHash128TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import TupleHash128 + +hash_t128 = TupleHash128.new() # Noncompliant {{(ExtendableOutputFunction) TupleHash128}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/TupleHash256TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/TupleHash256TestFile.py new file mode 100644 index 000000000..9613b062c --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/TupleHash256TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import TupleHash256 + +hash_t256 = TupleHash256.new() # Noncompliant {{(ExtendableOutputFunction) TupleHash256}} diff --git a/python/src/test/files/rules/detection/pycrypto/hash/cSHAKE256TestFile.py b/python/src/test/files/rules/detection/pycrypto/hash/cSHAKE256TestFile.py new file mode 100644 index 000000000..dc8db14df --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/hash/cSHAKE256TestFile.py @@ -0,0 +1,3 @@ +from Crypto.Hash import cSHAKE256 + +hash_t128 = cSHAKE256.new() # Noncompliant {{(ExtendableOutputFunction) cSHAKE256}} diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/HKDFTestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/HKDFTestFile.py new file mode 100644 index 000000000..1ce3a8994 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/HKDFTestFile.py @@ -0,0 +1,7 @@ +from Crypto.Protocol.KDF import HKDF +from Crypto.Hash import SHA512 + +def test_hkdf(): + secret = b"secret" + salt = b"salt1234567890ab" + key = HKDF(master=secret, key_len=32, salt=salt, hashmod=SHA512) # Noncompliant {{(KeyDerivationFunction) HKDF-SHA-512}} diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF1TestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF1TestFile.py new file mode 100644 index 000000000..be240b09f --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF1TestFile.py @@ -0,0 +1,7 @@ +from Crypto.Protocol.KDF import PBKDF1 +from Crypto.Hash import SHA256 + +def test_pbkdf1(): + password = b"password" + salt = b"salt1234" + key = PBKDF1(password=password, salt=salt, dkLen=16, hashAlgo=SHA256) # Noncompliant {{(PasswordBasedKeyDerivationFunction) PBKDF1-SHA-256}} diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF2TestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF2TestFile.py new file mode 100644 index 000000000..4387b46f4 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/PBKDF2TestFile.py @@ -0,0 +1,7 @@ +from Crypto.Protocol.KDF import PBKDF2 +from Crypto.Hash import SHA512 + +def test_pbkdf2(): + password = b"password" + salt = b"salt1234" + key = PBKDF2(password=password, salt=salt, dkLen=64, count=1000, hmac_hash_module=SHA512) # Noncompliant {{(PasswordBasedKeyDerivationFunction) PBKDF2-SHA-512}} diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/SP800108CounterTestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/SP800108CounterTestFile.py new file mode 100644 index 000000000..ec649c9b9 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/SP800108CounterTestFile.py @@ -0,0 +1,9 @@ +from Crypto.Protocol.KDF import SP800_108_Counter +from Crypto.Hash import HMAC, SHA256 + +def prf(s, x): + return HMAC.new(s, x, SHA256).digest() # NonCompliant {{(Mac) HMAC-SHA-256}} + +def test_sp800_108_counter(): + secret = b"secret" + key = SP800_108_Counter(master=secret, key_len=16, prf=prf) # Noncompliant {{(KeyDerivationFunction) SP800_108_CounterKDF}} diff --git a/python/src/test/files/rules/detection/pycrypto/kdf/ScryptTestFile.py b/python/src/test/files/rules/detection/pycrypto/kdf/ScryptTestFile.py new file mode 100644 index 000000000..3dacc3111 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/kdf/ScryptTestFile.py @@ -0,0 +1,6 @@ +from Crypto.Protocol.KDF import scrypt + +def test_scrypt(): + password = b"password" + salt = b"salt1234" + key = scrypt(password, salt, key_len=32, N=2**14, r=8, p=1, num_keys=1) # Noncompliant {{(PasswordBasedKeyDerivationFunction) scrypt}} diff --git a/python/src/test/files/rules/detection/pycrypto/keyagreement/ECDHTestFile.py b/python/src/test/files/rules/detection/pycrypto/keyagreement/ECDHTestFile.py new file mode 100644 index 000000000..4ba7beb6b --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/keyagreement/ECDHTestFile.py @@ -0,0 +1,14 @@ +from Crypto.Hash import SHAKE128 +from Crypto.PublicKey import ECC +from Crypto.Protocol.DH import key_agreement + +def kdf(x): + return SHAKE128.new(x).read(32) # Noncompliant {{(ExtendableOutputFunction) SHAKE128}} + +priv_key = ECC.generate(curve='p256') +pub_key = priv_key.public_key() + +session_key = key_agreement( # Noncompliant {{(KeyAgreement) ECDH}} + kdf=kdf, + static_priv=priv_key, + static_pub=pub_key) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/keyagreement/X25519TestFile.py b/python/src/test/files/rules/detection/pycrypto/keyagreement/X25519TestFile.py new file mode 100644 index 000000000..04fbad759 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/keyagreement/X25519TestFile.py @@ -0,0 +1,14 @@ +from Crypto.Hash import SHAKE128 +from Crypto.Protocol.DH import key_agreement, import_x25519_public_key, import_x25519_private_key + +def kdf(x): + return SHAKE128.new(x).read(32) # Noncompliant {{(ExtendableOutputFunction) SHAKE128}} + +pub_key = import_x25519_public_key(b'\xab' * 32) +priv_key = import_x25519_private_key(b'\xcd' * 32) + +session_key = key_agreement( # Noncompliant {{(KeyAgreement) x25519}} + kdf=kdf, + static_priv=priv_key, + static_pub=pub_key) + diff --git a/python/src/test/files/rules/detection/pycrypto/keyagreement/X448TestFile.py b/python/src/test/files/rules/detection/pycrypto/keyagreement/X448TestFile.py new file mode 100644 index 000000000..024eeb19d --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/keyagreement/X448TestFile.py @@ -0,0 +1,13 @@ +from Crypto.Hash import SHAKE128 +from Crypto.Protocol.DH import key_agreement, import_x448_public_key, import_x448_private_key + +def kdf(x): + return SHAKE128.new(x).read(32) # Noncompliant {{(ExtendableOutputFunction) SHAKE128}} + +pub_key = import_x448_public_key(b'\xab' * 56) +priv_key = import_x448_private_key(b'\xcd' * 56) + +session_key = key_agreement( # Noncompliant {{(KeyAgreement) x448}} + kdf=kdf, + static_priv=priv_key, + static_pub=pub_key) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/mac/CMACTestFile.py b/python/src/test/files/rules/detection/pycrypto/mac/CMACTestFile.py new file mode 100644 index 000000000..3d51283ff --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/mac/CMACTestFile.py @@ -0,0 +1,5 @@ +from Crypto.Hash import CMAC +from Crypto.Cipher import AES + +key = b'some key' +cmac = CMAC.new(key, ciphermod=AES) # Noncompliant {{(Mac) CMAC-AES}} diff --git a/python/src/test/files/rules/detection/pycrypto/mac/HMACTestFile.py b/python/src/test/files/rules/detection/pycrypto/mac/HMACTestFile.py new file mode 100644 index 000000000..bc776cb2e --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/mac/HMACTestFile.py @@ -0,0 +1,4 @@ +from Crypto.Hash import HMAC, SHA256 + +secret = b'some secret' +hmac = HMAC.new(secret, digestmod=SHA256) # Noncompliant {{(Mac) HMAC-SHA-256}} diff --git a/python/src/test/files/rules/detection/pycrypto/mac/KMAC128TestFile.py b/python/src/test/files/rules/detection/pycrypto/mac/KMAC128TestFile.py new file mode 100644 index 000000000..fa40ab43a --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/mac/KMAC128TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Hash import KMAC128 + +key = b'Sixteen byte key' +mac = KMAC128.new(key=key) # Noncompliant {{(Mac) KMAC128}} diff --git a/python/src/test/files/rules/detection/pycrypto/mac/KMAC256TestFile.py b/python/src/test/files/rules/detection/pycrypto/mac/KMAC256TestFile.py new file mode 100644 index 000000000..592c3d70d --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/mac/KMAC256TestFile.py @@ -0,0 +1,4 @@ +from Crypto.Hash import KMAC256 + +key = b'Sixteen byte key' +mac = KMAC256.new(key=key) # Noncompliant {{(Mac) KMAC256}} diff --git a/python/src/test/files/rules/detection/pycrypto/mac/Poly1305TestFile.py b/python/src/test/files/rules/detection/pycrypto/mac/Poly1305TestFile.py new file mode 100644 index 000000000..5c610e09c --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/mac/Poly1305TestFile.py @@ -0,0 +1,5 @@ +from Crypto.Hash import Poly1305 +from Crypto.Cipher import AES + +key = b'0123456789abcdef0123456789abcdef' +mac = Poly1305.new(key=key, cipher=AES) # Noncompliant {{(Mac) Poly1305}} diff --git a/python/src/test/files/rules/detection/pycrypto/publickey/DSATestFile.py b/python/src/test/files/rules/detection/pycrypto/publickey/DSATestFile.py new file mode 100644 index 000000000..1d78c629c --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/publickey/DSATestFile.py @@ -0,0 +1,6 @@ +from Crypto.PublicKey import DSA + +def test_dsa(): + key1 = DSA.generate(bits=2048) # Noncompliant {{(PrivateKey) DSA}} + key2 = DSA.construct((2,3), True) # Noncompliant {{(Key) DSA}} + key3 = DSA.import_key("abcdef") # Noncompliant {{(Key) DSA}} diff --git a/python/src/test/files/rules/detection/pycrypto/publickey/ECCTestFile.py b/python/src/test/files/rules/detection/pycrypto/publickey/ECCTestFile.py new file mode 100644 index 000000000..bdffd456f --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/publickey/ECCTestFile.py @@ -0,0 +1,6 @@ +from Crypto.PublicKey import ECC + +def test_ecc(): + key1 = ECC.generate(curve="Ed25519") # Noncompliant {{(PrivateKey) Ed25519}} + key2 = ECC.construct(curve="Curve448", seed=b"A" * 56) # Noncompliant {{(Key) x448}} + key3 = ECC.import_key("abcdef") # Noncompliant {{(Key) EC}} diff --git a/python/src/test/files/rules/detection/pycrypto/publickey/ElGamalTestFile.py b/python/src/test/files/rules/detection/pycrypto/publickey/ElGamalTestFile.py new file mode 100644 index 000000000..af0359230 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/publickey/ElGamalTestFile.py @@ -0,0 +1,5 @@ +from Crypto.PublicKey import ElGamal + +def test_elgamal(): + key1 = ElGamal.generate(bits=2048, randfunc=None) # Noncompliant {{(PrivateKey) ElGamal}} + key2 = ElGamal.construct((2,3,4)) # Noncompliant {{(Key) ElGamal}} diff --git a/python/src/test/files/rules/detection/pycrypto/publickey/RSATestFile.py b/python/src/test/files/rules/detection/pycrypto/publickey/RSATestFile.py new file mode 100644 index 000000000..9699b98ad --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/publickey/RSATestFile.py @@ -0,0 +1,6 @@ +from Crypto.PublicKey import RSA + +def test_rsa(): + key1 = RSA.generate(bits=2048) # Noncompliant {{(PrivateKey) RSA}} + key2 = RSA.construct((2,3), True) # Noncompliant {{(Key) RSA}} + key3 = RSA.import_key("abcdef") # Noncompliant {{(Key) RSA}} diff --git a/python/src/test/files/rules/detection/pycrypto/signature/DSSSignTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/DSSSignTestFile.py new file mode 100644 index 000000000..c49b2b9bd --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/DSSSignTestFile.py @@ -0,0 +1,9 @@ +from Crypto.Hash import SHA256 +from Crypto.PublicKey import DSA +from Crypto.Signature import DSS + +message = b'some message' +key = DSA.import_key(open('privkey.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +signer = DSS.new(key=key, mode='fips-186-3') # Noncompliant {{(Signature) DSA-SHA-256}} +signature = signer.sign(h) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/DSSVerifyTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/DSSVerifyTestFile.py new file mode 100644 index 000000000..a904abae0 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/DSSVerifyTestFile.py @@ -0,0 +1,15 @@ +from Crypto.Hash import SHA256 +from Crypto.PublicKey import DSA +from Crypto.Signature import DSS + +message = b'some message' +key = DSA.import_key(open('pubkey.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +verifier = DSS.new(key=key, mode='fips-186-3') # Noncompliant {{(Signature) DSA-SHA-256}} +signature = b'some signature' + +try: + verifier.verify(h, signature) + print("The message is authentic.") +except ValueError: + print("The message is not authentic.") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/ECDSASignTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/ECDSASignTestFile.py new file mode 100644 index 000000000..35c1c6daf --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/ECDSASignTestFile.py @@ -0,0 +1,9 @@ +from Crypto.Hash import SHA256 +from Crypto.PublicKey import ECC +from Crypto.Signature import DSS + +message = b'some message' +key = ECC.import_key(open('privkey.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +signer = DSS.new(key=key, mode='fips-186-3') # Noncompliant {{(Signature) ECDSA-SHA-256}} +signature = signer.sign(h) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/ECDSAVerifyTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/ECDSAVerifyTestFile.py new file mode 100644 index 000000000..e53953f81 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/ECDSAVerifyTestFile.py @@ -0,0 +1,15 @@ +from Crypto.Hash import SHA256 +from Crypto.PublicKey import ECC +from Crypto.Signature import DSS + +message = b'some message' +key = ECC.import_key(open('pubkey.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +verifier = DSS.new(key=key, mode='fips-186-3') # Noncompliant {{(Signature) ECDSA-SHA-256}} +signature = b'some signature' + +try: + verifier.verify(h, signature) + print("The message is authentic.") +except ValueError: + print("The message is not authentic.") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/EdDSASignTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/EdDSASignTestFile.py new file mode 100644 index 000000000..3eb5f573c --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/EdDSASignTestFile.py @@ -0,0 +1,9 @@ +from Crypto.PublicKey import ECC +from Crypto.Signature import eddsa +from Crypto.Hash import SHA512 + +message = b'some message' +prehashed_message = SHA512.new(message) # Noncompliant {{(MessageDigest) SHA-512}} +key = ECC.import_key(open("private_ed25519.pem").read()) +signer = eddsa.new(key=key, mode='rfc8032') # Noncompliant {{(Signature) EdDSA}} +signature = signer.sign(prehashed_message) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/EdDSAVerifyTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/EdDSAVerifyTestFile.py new file mode 100644 index 000000000..41bcd2734 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/EdDSAVerifyTestFile.py @@ -0,0 +1,14 @@ +from Crypto.PublicKey import ECC +from Crypto.Signature import eddsa +from Crypto.Hash import SHA512 + +signature = b'some signature' +raw_public_bytes = b"\x01" * 32 +public_key = eddsa.import_public_key(raw_public_bytes) +verifier = eddsa.new(key=public_key, mode='rfc8032') # Noncompliant {{(Signature) EdDSA}} + +try: + verifier.verify(h, signature) + print("The message is authentic") +except ValueError: + print("The message is not authentic") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15SignTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15SignTestFile.py new file mode 100644 index 000000000..a70c95c08 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15SignTestFile.py @@ -0,0 +1,9 @@ +from Crypto.Signature import pkcs1_15 +from Crypto.Hash import SHA256 +from Crypto.PublicKey import RSA + +message = b'To be signed' +key = RSA.import_key(open('private_key.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +scheme = pkcs1_15.new(rsa_key=key) # Noncompliant {{(Signature) RSA-PKCS1-1.5-SHA-256}} +signature = scheme.sign(h) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15VerifyTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15VerifyTestFile.py new file mode 100644 index 000000000..bd01ce006 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/PKCS1v15VerifyTestFile.py @@ -0,0 +1,15 @@ +from Crypto.Signature import pkcs1_15 +from Crypto.Hash import SHA256 +from Crypto.PublicKey import RSA + +message = b'To be signed' +key = RSA.import_key(open('public_key.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +scheme = pkcs1_15.new(rsa_key=key) # Noncompliant {{(Signature) RSA-PKCS1-1.5-SHA-256}} +signature = b'some sginature' + +try: + scheme.verify(h, signature) + print("The signature is valid.") +except (ValueError, TypeError): + print("The signature is not valid.") \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/PSSSignTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/PSSSignTestFile.py new file mode 100644 index 000000000..36b5e24c6 --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/PSSSignTestFile.py @@ -0,0 +1,9 @@ +from Crypto.Signature import pss +from Crypto.Hash import SHA256 +from Crypto.PublicKey import RSA + +message = b'To be signed' +key = RSA.import_key(open('private_key.der').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +scheme = pss.new(rsa_key=key) # Noncompliant {{(ProbabilisticSignatureScheme) RSA-PSS}} +signature = scheme.sign(h) \ No newline at end of file diff --git a/python/src/test/files/rules/detection/pycrypto/signature/PSSVerifyTestFile.py b/python/src/test/files/rules/detection/pycrypto/signature/PSSVerifyTestFile.py new file mode 100644 index 000000000..dda6f698e --- /dev/null +++ b/python/src/test/files/rules/detection/pycrypto/signature/PSSVerifyTestFile.py @@ -0,0 +1,16 @@ +from Crypto.Signature import pss +from Crypto.Signature.pss import PSS_SigScheme +from Crypto.Hash import SHA256 +from Crypto.PublicKey import RSA + +message = b'To be signed' +key = RSA.import_key(open('pubkey.der', 'rb').read()) +h = SHA256.new(message) # Noncompliant {{(MessageDigest) SHA-256}} +verifier = pss.new(rsa_key=key) # Noncompliant {{(ProbabilisticSignatureScheme) RSA-PSS}} +signature = b'some sginature' + +try: + verifier.verify(h, signature) + print("The signature is authentic.") +except (ValueError): + print("The signature is not authentic.") \ No newline at end of file diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/NamedAllKeywordParametersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/named_parameters/NamedAllKeywordParametersTest.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/NamedAllKeywordParametersTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/named_parameters/NamedAllKeywordParametersTest.java index 10a25efae..dc4b42d51 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/NamedAllKeywordParametersTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/named_parameters/NamedAllKeywordParametersTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection; +package com.ibm.plugin.rules.detection.named_parameters; import static org.assertj.core.api.Assertions.assertThat; diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/NamedParameterWildcardTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/named_parameters/NamedParameterWildcardTest.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/NamedParameterWildcardTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/named_parameters/NamedParameterWildcardTest.java index e6a49d629..7eccd762b 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/NamedParameterWildcardTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/named_parameters/NamedParameterWildcardTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection; +package com.ibm.plugin.rules.detection.named_parameters; import static com.ibm.engine.detection.MethodMatcher.ANY; import static org.assertj.core.api.Assertions.fail; diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/NamedParametersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/named_parameters/NamedParametersTest.java similarity index 99% rename from python/src/test/java/com/ibm/plugin/rules/detection/NamedParametersTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/named_parameters/NamedParametersTest.java index fe1111d99..63ebf024e 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/NamedParametersTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/named_parameters/NamedParametersTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection; +package com.ibm.plugin.rules.detection.named_parameters; import static org.assertj.core.api.Assertions.assertThat; diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaAESGCMTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAESGCMTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaAESGCMTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAESGCMTest.java index d3409b362..7811c3317 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaAESGCMTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaAESGCMTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.aead; +package com.ibm.plugin.rules.detection.pyca.aead; import static org.assertj.core.api.Assertions.assertThat; @@ -52,7 +52,7 @@ class PycaAESGCMTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/aead/PycaAESGCMTestFile.py", this); + "src/test/files/rules/detection/pyca/aead/PycaAESGCMTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaChaCha20Poly1305Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaChaCha20Poly1305Test.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaChaCha20Poly1305Test.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaChaCha20Poly1305Test.java index 004201c31..88c1b01f4 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/aead/PycaChaCha20Poly1305Test.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/aead/PycaChaCha20Poly1305Test.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.aead; +package com.ibm.plugin.rules.detection.pyca.aead; import static org.assertj.core.api.Assertions.assertThat; @@ -50,7 +50,7 @@ class PycaChaCha20Poly1305Test extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/aead/PycaChaCha20Poly1305TestFile.py", this); + "src/test/files/rules/detection/pyca/aead/PycaChaCha20Poly1305TestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSANumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTest.java similarity index 96% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSANumbersTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTest.java index 2178efc29..beebbd7c6 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSANumbersTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.DSA; +package com.ibm.plugin.rules.detection.pyca.asymmetric.DSA; import static org.assertj.core.api.Assertions.assertThat; @@ -47,7 +47,8 @@ class PycaDSANumbersTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/DSA/PycaDSANumbersTestFile.py", this); + "src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSANumbersTestFile.py", + this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSASignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSASignTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSASignTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSASignTest.java index 295196b08..1c4cfcafe 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DSA/PycaDSASignTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DSA/PycaDSASignTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.DSA; +package com.ibm.plugin.rules.detection.pyca.asymmetric.DSA; import static org.assertj.core.api.Assertions.assertThat; @@ -54,7 +54,7 @@ class PycaDSASignTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/DSA/PycaDSASignTestFile.py", this); + "src/test/files/rules/detection/pyca/asymmetric/DSA/PycaDSASignTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java similarity index 95% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java index 06ce44018..4097dcf75 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.DiffieHellman; +package com.ibm.plugin.rules.detection.pyca.asymmetric.DiffieHellman; import static org.assertj.core.api.Assertions.assertThat; @@ -47,7 +47,7 @@ public final class PycaDiffieHellmanGenerateTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py", + "src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanGenerateTestFile.py", this); } diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java similarity index 95% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java index bdd8bf7dc..26099a4a9 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.DiffieHellman; +package com.ibm.plugin.rules.detection.pyca.asymmetric.DiffieHellman; import static org.assertj.core.api.Assertions.assertThat; @@ -45,7 +45,7 @@ class PycaDiffieHellmanNumbersTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py", + "src/test/files/rules/detection/pyca/asymmetric/DiffieHellman/PycaDiffieHellmanNumbersTestFile.py", this); } diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java similarity index 95% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java index dde89a44a..d34d4fa91 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; import static org.assertj.core.api.Assertions.assertThat; @@ -46,7 +46,7 @@ class PycaEllipticCurveDeriveTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py", + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveDeriveTestFile.py", this); } diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java index ca421610e..c073991dc 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; import static org.assertj.core.api.Assertions.assertThat; @@ -57,7 +57,7 @@ class PycaEllipticCurveKeyExchangeTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py", + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveKeyExchangeTestFile.py", this); } diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java similarity index 90% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java index 2c95fc794..9c1f7d79c 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; import com.ibm.engine.detection.DetectionStore; import com.ibm.mapper.model.INode; @@ -38,7 +38,7 @@ class PycaEllipticCurveNumbersTest extends TestBase { @Test void test() { PythonCheckVerifier.verifyNoIssue( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py", + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveNumbersTestFile.py", this); } diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java index c6156730e..84e4928eb 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2Test.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; import static org.assertj.core.api.Assertions.assertThat; @@ -50,7 +50,7 @@ class PycaEllipticCurveSign2Test extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py", + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSign2TestFile.py", this); } diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java index 42e6c8347..e70a1c3df 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; import static org.assertj.core.api.Assertions.assertThat; @@ -54,7 +54,7 @@ class PycaEllipticCurveSignTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py", + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveSignTestFile.py", this); } diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java similarity index 91% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java index ab38d7bcb..2941263c2 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.EllipticCurve; +package com.ibm.plugin.rules.detection.pyca.asymmetric.EllipticCurve; import com.ibm.engine.detection.DetectionStore; import com.ibm.mapper.model.INode; @@ -41,7 +41,7 @@ class PycaEllipticCurveVerifyTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py", + "src/test/files/rules/detection/pyca/asymmetric/EllipticCurve/PycaEllipticCurveVerifyTestFile.py", this); } diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSADecryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTest.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSADecryptTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTest.java index 416e98ba3..274f3bcd8 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSADecryptTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.RSA; +package com.ibm.plugin.rules.detection.pyca.asymmetric.RSA; import static org.assertj.core.api.Assertions.assertThat; @@ -58,7 +58,8 @@ class PycaRSADecryptTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/RSA/PycaRSADecryptTestFile.py", this); + "src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSADecryptTestFile.py", + this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSANumbersTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSANumbersTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTest.java index c78a44f64..5321621ee 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSANumbersTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.RSA; +package com.ibm.plugin.rules.detection.pyca.asymmetric.RSA; import static org.assertj.core.api.Assertions.assertThat; @@ -47,7 +47,8 @@ class PycaRSANumbersTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/RSA/PycaRSANumbersTestFile.py", this); + "src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSANumbersTestFile.py", + this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign1Test.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign1Test.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign1Test.java index e57bdb78a..25c86fe9c 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign1Test.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign1Test.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.RSA; +package com.ibm.plugin.rules.detection.pyca.asymmetric.RSA; import static org.assertj.core.api.Assertions.assertThat; @@ -56,7 +56,7 @@ class PycaRSASign1Test extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/RSA/PycaRSASign1TestFile.py", this); + "src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign1TestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign2Test.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign2Test.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign2Test.java index bb5181e56..c39328fa6 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/asymmetric/RSA/PycaRSASign2Test.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/asymmetric/RSA/PycaRSASign2Test.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.asymmetric.RSA; +package com.ibm.plugin.rules.detection.pyca.asymmetric.RSA; import static org.assertj.core.api.Assertions.assertThat; @@ -55,7 +55,7 @@ class PycaRSASign2Test extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/asymmetric/RSA/PycaRSASign2TestFile.py", this); + "src/test/files/rules/detection/pyca/asymmetric/RSA/PycaRSASign2TestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetDecryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetDecryptTest.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetDecryptTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetDecryptTest.java index 10119414d..19f7d28cd 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetDecryptTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetDecryptTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.fernet; +package com.ibm.plugin.rules.detection.pyca.fernet; import static org.assertj.core.api.Assertions.assertThat; @@ -58,7 +58,7 @@ class PycaFernetDecryptTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/fernet/PycaFernetDecryptTestFile.py", this); + "src/test/files/rules/detection/pyca/fernet/PycaFernetDecryptTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetEncryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetEncryptTest.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetEncryptTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetEncryptTest.java index 759916b45..5ad5aa112 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaFernetEncryptTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaFernetEncryptTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.fernet; +package com.ibm.plugin.rules.detection.pyca.fernet; import static org.assertj.core.api.Assertions.assertThat; @@ -58,7 +58,7 @@ class PycaFernetEncryptTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/fernet/PycaFernetEncryptTestFile.py", this); + "src/test/files/rules/detection/pyca/fernet/PycaFernetEncryptTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaMultiFernetTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaMultiFernetTest.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaMultiFernetTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaMultiFernetTest.java index 36fe6893b..ac90ea5a5 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/fernet/PycaMultiFernetTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/fernet/PycaMultiFernetTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.fernet; +package com.ibm.plugin.rules.detection.pyca.fernet; import static org.assertj.core.api.Assertions.assertThat; @@ -59,7 +59,7 @@ class PycaMultiFernetTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/fernet/PycaMultiFernetTestFile.py", this); + "src/test/files/rules/detection/pyca/fernet/PycaMultiFernetTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/hash/PycaHashDirectTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHashDirectTest.java similarity index 96% rename from python/src/test/java/com/ibm/plugin/rules/detection/hash/PycaHashDirectTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHashDirectTest.java index 73cef79d4..0e57b8a00 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/hash/PycaHashDirectTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/hash/PycaHashDirectTest.java @@ -1,6 +1,6 @@ /* * Sonar Cryptography Plugin - * Copyright (C) 2024 PQCA + * Copyright (C) 2026 PQCA * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.hash; +package com.ibm.plugin.rules.detection.pyca.hash; import static org.assertj.core.api.Assertions.assertThat; @@ -46,7 +46,7 @@ class PycaHashDirectTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/hash/PycaHashDirectTest.py", this); + "src/test/files/rules/detection/pyca/hash/PycaHashDirectTest.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHMACTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHMACTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHMACTest.java index 4c72b5be8..2e7bd5711 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHMACTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHMACTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.kdf; +package com.ibm.plugin.rules.detection.pyca.kdf; import static org.assertj.core.api.Assertions.assertThat; @@ -50,7 +50,7 @@ class PycaConcatKDFHMACTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaConcatKDFHMACTestFile.py", this); + "src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHMACTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHashTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHashTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHashTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHashTest.java index 68a47dbc9..e10a0c5f2 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaConcatKDFHashTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaConcatKDFHashTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.kdf; +package com.ibm.plugin.rules.detection.pyca.kdf; import static org.assertj.core.api.Assertions.assertThat; @@ -49,7 +49,7 @@ class PycaConcatKDFHashTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaConcatKDFHashTestFile.py", this); + "src/test/files/rules/detection/pyca/kdf/PycaConcatKDFHashTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFExpandTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFExpandTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFExpandTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFExpandTest.java index 0eade96a6..585f7448b 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFExpandTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFExpandTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.kdf; +package com.ibm.plugin.rules.detection.pyca.kdf; import static org.assertj.core.api.Assertions.assertThat; @@ -50,7 +50,7 @@ class PycaHKDFExpandTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaHKDFExpandTestFile.py", this); + "src/test/files/rules/detection/pyca/kdf/PycaHKDFExpandTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFTest.java index e7a4a5f16..1db1ba235 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaHKDFTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaHKDFTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.kdf; +package com.ibm.plugin.rules.detection.pyca.kdf; import static org.assertj.core.api.Assertions.assertThat; @@ -49,7 +49,8 @@ class PycaHKDFTest extends TestBase { @Test void test() { - PythonCheckVerifier.verify("src/test/files/rules/detection/kdf/PycaHKDFTestFile.py", this); + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/kdf/PycaHKDFTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFCMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFCMACTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFCMACTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFCMACTest.java index 0436832c1..16e7df570 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFCMACTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFCMACTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.kdf; +package com.ibm.plugin.rules.detection.pyca.kdf; import static org.assertj.core.api.Assertions.assertThat; @@ -50,7 +50,7 @@ class PycaKBKDFCMACTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaKBKDFCMACTestFile.py", this); + "src/test/files/rules/detection/pyca/kdf/PycaKBKDFCMACTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFHMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFHMACTest.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFHMACTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFHMACTest.java index ef0fad6b9..828cbf21d 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaKBKDFHMACTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaKBKDFHMACTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.kdf; +package com.ibm.plugin.rules.detection.pyca.kdf; import static org.assertj.core.api.Assertions.assertThat; @@ -52,7 +52,7 @@ class PycaKBKDFHMACTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaKBKDFHMACTestFile.py", this); + "src/test/files/rules/detection/pyca/kdf/PycaKBKDFHMACTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaPBKDF2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaPBKDF2Test.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaPBKDF2Test.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaPBKDF2Test.java index bdcc9af1f..a714802e5 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaPBKDF2Test.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaPBKDF2Test.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.kdf; +package com.ibm.plugin.rules.detection.pyca.kdf; import static org.assertj.core.api.Assertions.assertThat; @@ -51,7 +51,7 @@ class PycaPBKDF2Test extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaPBKDF2TestFile.py", this); + "src/test/files/rules/detection/pyca/kdf/PycaPBKDF2TestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaScryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaScryptTest.java similarity index 96% rename from python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaScryptTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaScryptTest.java index b137aabb5..f1a85a3fb 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaScryptTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaScryptTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.kdf; +package com.ibm.plugin.rules.detection.pyca.kdf; import static org.assertj.core.api.Assertions.assertThat; @@ -45,7 +45,7 @@ class PycaScryptTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaScryptTestFile.py", this); + "src/test/files/rules/detection/pyca/kdf/PycaScryptTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaX963KDFTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaX963KDFTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaX963KDFTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaX963KDFTest.java index 786fa46cb..9310d6380 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/kdf/PycaX963KDFTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/kdf/PycaX963KDFTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.kdf; +package com.ibm.plugin.rules.detection.pyca.kdf; import static org.assertj.core.api.Assertions.assertThat; @@ -50,7 +50,7 @@ class PycaX963KDFTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/kdf/PycaX963KDFTestFile.py", this); + "src/test/files/rules/detection/pyca/kdf/PycaX963KDFTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreementTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreementTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreementTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreementTest.java index bb88f0e77..9327a162f 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/keyagreement/PycaKeyAgreementTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/keyagreement/PycaKeyAgreementTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.keyagreement; +package com.ibm.plugin.rules.detection.pyca.keyagreement; import static org.assertj.core.api.Assertions.assertThat; @@ -45,7 +45,8 @@ class PycaKeyAgreementTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/keyagreement/PycaKeyAgreementTestFile.py", this); + "src/test/files/rules/detection/pyca/keyagreement/PycaKeyAgreementTestFile.py", + this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaCMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaCMACTest.java similarity index 95% rename from python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaCMACTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaCMACTest.java index bbc0f3f2b..afeab14a3 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaCMACTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaCMACTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.mac; +package com.ibm.plugin.rules.detection.pyca.mac; import static org.assertj.core.api.Assertions.assertThat; @@ -45,7 +45,8 @@ class PycaCMACTest extends TestBase { @Test void test() { - PythonCheckVerifier.verify("src/test/files/rules/detection/mac/PycaCMACTestFile.py", this); + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/mac/PycaCMACTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaHMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaHMACTest.java similarity index 96% rename from python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaHMACTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaHMACTest.java index 9957f5381..374ee293d 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaHMACTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaHMACTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.mac; +package com.ibm.plugin.rules.detection.pyca.mac; import static org.assertj.core.api.Assertions.assertThat; @@ -47,7 +47,8 @@ class PycaHMACTest extends TestBase { @Test void test() { - PythonCheckVerifier.verify("src/test/files/rules/detection/mac/PycaHMACTestFile.py", this); + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pyca/mac/PycaHMACTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaMacDetectionInCustomFunctionTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaMacDetectionInCustomFunctionTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTest.java index 9a89f28eb..e7806b573 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaMacDetectionInCustomFunctionTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.mac; +package com.ibm.plugin.rules.detection.pyca.mac; import static org.assertj.core.api.Assertions.assertThat; @@ -53,7 +53,7 @@ class PycaMacDetectionInCustomFunctionTest extends TestBase { @Test void testCryptographicOperationInCustomFunction() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/mac/PycaMacDetectionInCustomFunctionTestFile.py", + "src/test/files/rules/detection/pyca/mac/PycaMacDetectionInCustomFunctionTestFile.py", this); } diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaPoly1305Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaPoly1305Test.java similarity index 74% rename from python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaPoly1305Test.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaPoly1305Test.java index fecc5bd37..b6e9d0d96 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/mac/PycaPoly1305Test.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/mac/PycaPoly1305Test.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.mac; +package com.ibm.plugin.rules.detection.pyca.mac; import static org.assertj.core.api.Assertions.assertThat; @@ -27,8 +27,6 @@ import com.ibm.engine.model.context.MacContext; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.Mac; -import com.ibm.mapper.model.MessageDigest; -import com.ibm.mapper.model.functionality.Digest; import com.ibm.mapper.model.functionality.Tag; import com.ibm.plugin.TestBase; import java.util.List; @@ -45,7 +43,7 @@ class PycaPoly1305Test extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/mac/PycaPoly1305TestFile.py", this); + "src/test/files/rules/detection/pyca/mac/PycaPoly1305TestFile.py", this); } @Override @@ -70,25 +68,13 @@ public void asserts( // Mac INode macNode = nodes.get(0); assertThat(macNode.getKind()).isEqualTo(Mac.class); - assertThat(macNode.getChildren()).hasSize(2); - assertThat(macNode.asString()).isEqualTo("HMAC-Poly1305"); + assertThat(macNode.getChildren()).hasSize(1); + assertThat(macNode.asString()).isEqualTo("Poly1305"); // Tag under Mac INode tagNode = macNode.getChildren().get(Tag.class); assertThat(tagNode).isNotNull(); assertThat(tagNode.getChildren()).isEmpty(); assertThat(tagNode.asString()).isEqualTo("TAG"); - - // MessageDigest under Mac - INode messageDigestNode = macNode.getChildren().get(MessageDigest.class); - assertThat(messageDigestNode).isNotNull(); - assertThat(messageDigestNode.getChildren()).hasSize(1); - assertThat(messageDigestNode.asString()).isEqualTo("Poly1305"); - - // Digest under MessageDigest under Mac - INode digestNode = messageDigestNode.getChildren().get(Digest.class); - assertThat(digestNode).isNotNull(); - assertThat(digestNode.getChildren()).isEmpty(); - assertThat(digestNode.asString()).isEqualTo("DIGEST"); } } diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/padding/PycaPaddingTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPaddingTest.java similarity index 97% rename from python/src/test/java/com/ibm/plugin/rules/detection/padding/PycaPaddingTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPaddingTest.java index a2f29030a..7d3a43556 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/padding/PycaPaddingTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/padding/PycaPaddingTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.padding; +package com.ibm.plugin.rules.detection.pyca.padding; import static org.assertj.core.api.Assertions.assertThat; @@ -46,7 +46,7 @@ class PycaPaddingTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/padding/PycaPaddingTestFile.py", this); + "src/test/files/rules/detection/pyca/padding/PycaPaddingTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher1Test.java similarity index 98% rename from python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher1Test.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher1Test.java index 73a1d6ebb..36834055c 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher1Test.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher1Test.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.symmetric; +package com.ibm.plugin.rules.detection.pyca.symmetric; import static org.assertj.core.api.Assertions.assertThat; @@ -50,7 +50,7 @@ class PycaCipher1Test extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/symmetric/PycaCipher1TestFile.py", this); + "src/test/files/rules/detection/pyca/symmetric/PycaCipher1TestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher2Test.java similarity index 96% rename from python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher2Test.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher2Test.java index 74ac657a9..a69bca299 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaCipher2Test.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaCipher2Test.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.symmetric; +package com.ibm.plugin.rules.detection.pyca.symmetric; import static org.assertj.core.api.Assertions.assertThat; @@ -45,7 +45,7 @@ class PycaCipher2Test extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/symmetric/PycaCipher2TestFile.py", this); + "src/test/files/rules/detection/pyca/symmetric/PycaCipher2TestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaStreamCipher1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaStreamCipher1Test.java similarity index 95% rename from python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaStreamCipher1Test.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaStreamCipher1Test.java index 7c5b1f339..98776e4f0 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/symmetric/PycaStreamCipher1Test.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/symmetric/PycaStreamCipher1Test.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.symmetric; +package com.ibm.plugin.rules.detection.pyca.symmetric; import static org.assertj.core.api.Assertions.assertThat; @@ -44,7 +44,7 @@ class PycaStreamCipher1Test extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/symmetric/PycaStreamCipher1TestFile.py", this); + "src/test/files/rules/detection/pyca/symmetric/PycaStreamCipher1TestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingTest.java similarity index 95% rename from python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingTest.java index 003eaaf9c..9c1a34897 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.wrapping; +package com.ibm.plugin.rules.detection.pyca.wrapping; import static org.assertj.core.api.Assertions.assertThat; @@ -44,7 +44,7 @@ class PycaWrappingTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/wrapping/PycaWrappingTestFile.py", this); + "src/test/files/rules/detection/pyca/wrapping/PycaWrappingTestFile.py", this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingWithPaddingTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTest.java similarity index 95% rename from python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingWithPaddingTest.java rename to python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTest.java index 37d0bb788..bfeeec87f 100644 --- a/python/src/test/java/com/ibm/plugin/rules/detection/wrapping/PycaWrappingWithPaddingTest.java +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTest.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ibm.plugin.rules.detection.wrapping; +package com.ibm.plugin.rules.detection.pyca.wrapping; import static org.assertj.core.api.Assertions.assertThat; @@ -44,7 +44,8 @@ class PycaWrappingWithPaddingTest extends TestBase { @Test void test() { PythonCheckVerifier.verify( - "src/test/files/rules/detection/wrapping/PycaWrappingWithPaddingTestFile.py", this); + "src/test/files/rules/detection/pyca/wrapping/PycaWrappingWithPaddingTestFile.py", + this); } @Override diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/AESTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/AESTest.java new file mode 100644 index 000000000..3f2add569 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/AESTest.java @@ -0,0 +1,106 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class AESTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/AESTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("AES"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_CBC"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(4); + assertThat(cipher.asString()).isEqualTo("AES-CBC"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("CBC"); + + INode oid = cipher.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.1"); + + INode blockSize = cipher.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("128"); + + INode encrypt = cipher.getChildren().get(Encrypt.class); + assertThat(encrypt).isNotNull(); + assertThat(encrypt.getChildren()).isEmpty(); + assertThat(encrypt.asString()).isEqualTo("ENCRYPT"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/BlowfishTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/BlowfishTest.java new file mode 100644 index 000000000..22cbae93c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/BlowfishTest.java @@ -0,0 +1,88 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class BlowfishTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/BlowfishTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("Blowfish"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_CBC"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(1); + assertThat(cipher.asString()).isEqualTo("Blowfish-CBC"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("CBC"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/CAST5Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/CAST5Test.java new file mode 100644 index 000000000..5c174d392 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/CAST5Test.java @@ -0,0 +1,94 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class CAST5Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/CAST5TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("CAST5"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_CBC"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(2); + assertThat(cipher.asString()).isEqualTo("CAST5-CBC"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("CBC"); + + INode blockSize = cipher.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("64"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Poly1305Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Poly1305Test.java new file mode 100644 index 000000000..2e82ca849 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Poly1305Test.java @@ -0,0 +1,79 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.AuthenticatedEncryption; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ChaCha20Poly1305Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/ChaCha20Poly1305TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("ChaCha20Poly1305"); + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(AuthenticatedEncryption.class); + assertThat(cipher.getChildren()).hasSize(1); + assertThat(cipher.asString()).isEqualTo("ChaCha20-Poly1305"); + + INode messageDigest = cipher.getChildren().get(MessageDigest.class); + assertThat(messageDigest).isNotNull(); + assertThat(messageDigest.getChildren()).hasSize(1); + assertThat(messageDigest.asString()).isEqualTo("Poly1305"); + + INode digest = messageDigest.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Test.java new file mode 100644 index 000000000..0ce2ec9cd --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/ChaCha20Test.java @@ -0,0 +1,67 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.StreamCipher; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ChaCha20Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/ChaCha20TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("ChaCha20"); + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(StreamCipher.class); + assertThat(cipher.getChildren()).isEmpty(); + assertThat(cipher.asString()).isEqualTo("ChaCha20"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/DESTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/DESTest.java new file mode 100644 index 000000000..cee58d89c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/DESTest.java @@ -0,0 +1,100 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Mode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class DESTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/DESTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("DES"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_ECB"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(4); + assertThat(cipher.asString()).isEqualTo("DES-56-ECB"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("ECB"); + + INode keyLength = cipher.getChildren().get(KeyLength.class); + assertThat(keyLength).isNotNull(); + assertThat(keyLength.getChildren()).isEmpty(); + assertThat(keyLength.asString()).isEqualTo("56"); + + INode blockSize = cipher.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("64"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1OAEPTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1OAEPTest.java new file mode 100644 index 000000000..64e5360eb --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1OAEPTest.java @@ -0,0 +1,117 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PKCS1OAEPTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/PKCS1OAEPTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + // cipher = PKCS1_OAEP.new(key, SHA256) + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("PKCS1_OAEP"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + DetectionStore keyStore = + getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); + if (keyStore != null) { + assertThat(keyStore.getDetectionValues()).hasSize(1); + assertThat(keyStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(keyStore.getDetectionValues().get(0)).isInstanceOf(Algorithm.class); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(cipher.getChildren()).hasSize(3); + assertThat(cipher.asString()).isEqualTo("RSA-OAEP"); + + INode oid = cipher.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.7"); + + INode padding = cipher.getChildren().get(Padding.class); + assertThat(padding).isNotNull(); + assertThat(padding.getChildren()).isEmpty(); + assertThat(padding.asString()).isEqualTo("OAEP"); + + INode key = cipher.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("RSA"); + + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.asString()).isEqualTo("RSA"); + + INode pkeOID = pke.getChildren().get(Oid.class); + assertThat(pkeOID).isNotNull(); + assertThat(pkeOID.asString()).isEqualTo("1.2.840.113549.1.1.1"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1v15Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1v15Test.java new file mode 100644 index 000000000..ae488c6ff --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/PKCS1v15Test.java @@ -0,0 +1,89 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PKCS1v15Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/PKCS1v15TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("PKCS1_v1_5"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(cipher.getChildren()).hasSize(2); + assertThat(cipher.asString()).isEqualTo("RSA"); + + INode oid = cipher.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + INode padding = cipher.getChildren().get(Padding.class); + assertThat(padding).isNotNull(); + assertThat(padding.getChildren()).isEmpty(); + assertThat(padding.asString()).isEqualTo("PKCS1"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC2Test.java new file mode 100644 index 000000000..bd456aa8b --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC2Test.java @@ -0,0 +1,88 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class RC2Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/RC2TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RC2"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_CBC"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(1); + assertThat(cipher.asString()).isEqualTo("RC2-CBC"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("CBC"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC4Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC4Test.java new file mode 100644 index 000000000..c5e0b7336 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/RC4Test.java @@ -0,0 +1,77 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.StreamCipher; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class RC4Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/RC4TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RC4"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(StreamCipher.class); + assertThat(cipher.getChildren()).isEmpty(); + assertThat(cipher.asString()).isEqualTo("RC4"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/Salsa20Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/Salsa20Test.java new file mode 100644 index 000000000..5805dcca0 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/Salsa20Test.java @@ -0,0 +1,83 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.StreamCipher; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class Salsa20Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/Salsa20TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("Salsa20"); + + DetectionStore actionStore = + getStoreOfValueType(CipherAction.class, detectionStore.getChildren()); + if (actionStore != null) { + assertThat(actionStore.getDetectionValues().get(0).asString()) + .satisfiesAnyOf( + s -> assertThat(s).isEqualTo("ENCRYPT"), + s -> assertThat(s).isEqualTo("DECRYPT")); + } + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(StreamCipher.class); + assertThat(cipher.getChildren()).hasSize(1); + assertThat(cipher.asString()).isEqualTo("Salsa20"); + + INode keyLength = cipher.getChildren().get(KeyLength.class); + assertThat(keyLength).isNotNull(); + assertThat(keyLength.getChildren()).isEmpty(); + assertThat(keyLength.asString()).isEqualTo("128"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/TripleDESTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/TripleDESTest.java new file mode 100644 index 000000000..2437eb099 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/cipher/TripleDESTest.java @@ -0,0 +1,84 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.cipher; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mode; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class TripleDESTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/cipher/TripleDESTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("3DES"); + + DetectionStore modeStore = + getStoreOfValueType(com.ibm.engine.model.Mode.class, detectionStore.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo("MODE_CBC"); + + assertThat(nodes).hasSize(1); + INode cipher = nodes.get(0); + assertThat(cipher.getKind()).isEqualTo(BlockCipher.class); + assertThat(cipher.getChildren()).hasSize(2); + assertThat(cipher.asString()).isEqualTo("3DES-CBC"); + + INode mode = cipher.getChildren().get(Mode.class); + assertThat(mode).isNotNull(); + assertThat(mode.getChildren()).isEmpty(); + assertThat(mode.asString()).isEqualTo("CBC"); + + INode blockSize = cipher.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("64"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2bTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2bTest.java new file mode 100644 index 000000000..f1c54fe17 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2bTest.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.SaltLength; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class BLAKE2bTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/BLAKE2bTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("BLAKE2b"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("BLAKE2b"); + + INode saltLength = md.getChildren().get(SaltLength.class); + assertThat(saltLength).isNotNull(); + assertThat(saltLength.getChildren()).isEmpty(); + assertThat(saltLength.asString()).isEqualTo("128"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2sTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2sTest.java new file mode 100644 index 000000000..53a984914 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/BLAKE2sTest.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.SaltLength; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class BLAKE2sTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/BLAKE2sTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("BLAKE2s"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("BLAKE2s"); + + INode saltLength = md.getChildren().get(SaltLength.class); + assertThat(saltLength).isNotNull(); + assertThat(saltLength.getChildren()).isEmpty(); + assertThat(saltLength.asString()).isEqualTo("64"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD2Test.java new file mode 100644 index 000000000..9603d7ff5 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD2Test.java @@ -0,0 +1,86 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class MD2Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/MD2TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("MD2"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(3); + assertThat(md.asString()).isEqualTo("MD2"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("128"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("128"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD5Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD5Test.java new file mode 100644 index 000000000..4841ea4b2 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/MD5Test.java @@ -0,0 +1,86 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class MD5Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/MD5TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("MD5"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(3); + assertThat(md.asString()).isEqualTo("MD5"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("128"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/RIPEMD160Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/RIPEMD160Test.java new file mode 100644 index 000000000..9ae8ab849 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/RIPEMD160Test.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class RIPEMD160Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/RIPEMD160TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RIPEMD160"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("RIPEMD-160"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("160"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA1Test.java new file mode 100644 index 000000000..43f1f8a88 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA1Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA1Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA1TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA1"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-1"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.3.14.3.2.26"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("160"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA224Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA224Test.java new file mode 100644 index 000000000..55dc5bb4c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA224Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA224Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA224TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA224"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-224"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.4"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("224"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA256Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA256Test.java new file mode 100644 index 000000000..7984cf644 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA256Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA256Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA256TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA256"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-256"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA384Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA384Test.java new file mode 100644 index 000000000..8a4698440 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA384Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA384Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA384TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA384"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-384"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1024"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.2"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("384"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_224Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_224Test.java new file mode 100644 index 000000000..028159f49 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_224Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA3_224Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA3_224TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA3_224"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA3-224"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1152"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.7"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("224"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_256Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_256Test.java new file mode 100644 index 000000000..59872895c --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_256Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA3_256Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA3_256TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA3_256"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA3-256"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1088"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.8"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_384Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_384Test.java new file mode 100644 index 000000000..15e7518cb --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_384Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA3_384Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA3_384TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA3_384"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA3-384"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("832"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.9"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("384"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_512Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_512Test.java new file mode 100644 index 000000000..1b757a736 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA3_512Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA3_512Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA3_512TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA3_512"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA3-512"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("576"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.10"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("512"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512Test.java new file mode 100644 index 000000000..9d0b7d92d --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512Test.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA512Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA512TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA512"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-512"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1024"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("512"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512_224Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512_224Test.java new file mode 100644 index 000000000..24be5c49d --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512_224Test.java @@ -0,0 +1,95 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA512_224Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA512_224TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA512"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.asString()).isEqualTo("SHA-512/224"); + assertThat(md.getChildren()).hasSize(5); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("224"); + + INode preHash = md.getChildren().get(MessageDigest.class); + assertThat(preHash).isNotNull(); + assertThat(preHash.asString()).isEqualTo("SHA-512"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1024"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.5"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512_256Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512_256Test.java new file mode 100644 index 000000000..3a83b19bc --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/SHA512_256Test.java @@ -0,0 +1,95 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SHA512_256Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/SHA512_256TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SHA512"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.asString()).isEqualTo("SHA-512/256"); + assertThat(md.getChildren()).hasSize(5); + + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + + INode preHash = md.getChildren().get(MessageDigest.class); + assertThat(preHash).isNotNull(); + assertThat(preHash.asString()).isEqualTo("SHA-512"); + + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1024"); + + INode oid = md.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.2.6"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/TupleHash128Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/TupleHash128Test.java new file mode 100644 index 000000000..9a2b50bcf --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/TupleHash128Test.java @@ -0,0 +1,83 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class TupleHash128Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/TupleHash128TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("TupleHash128"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("TupleHash128"); + + // the parameter set identifier is the security strength, not a digest length + assertThat(md.getChildren().get(DigestSize.class)).isNull(); + INode parameterSetIdentifier = md.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.getChildren()).isEmpty(); + assertThat(parameterSetIdentifier.asString()).isEqualTo("128"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/TupleHash256Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/TupleHash256Test.java new file mode 100644 index 000000000..4d1e0754f --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/TupleHash256Test.java @@ -0,0 +1,83 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class TupleHash256Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/TupleHash256TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("TupleHash256"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("TupleHash256"); + + // the parameter set identifier is the security strength, not a digest length + assertThat(md.getChildren().get(DigestSize.class)).isNull(); + INode parameterSetIdentifier = md.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.getChildren()).isEmpty(); + assertThat(parameterSetIdentifier.asString()).isEqualTo("256"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/cSHAKE256Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/cSHAKE256Test.java new file mode 100644 index 000000000..82a4b7db7 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/hash/cSHAKE256Test.java @@ -0,0 +1,80 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.hash; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class cSHAKE256Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/hash/cSHAKE256TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(DigestContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("cSHAKE256"); + + assertThat(nodes).hasSize(1); + INode md = nodes.get(0); + assertThat(md).isInstanceOf(MessageDigest.class); + assertThat(md.getChildren()).hasSize(2); + assertThat(md.asString()).isEqualTo("cSHAKE256"); + + INode parameterSetIdentifier = md.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.getChildren()).isEmpty(); + assertThat(parameterSetIdentifier.asString()).isEqualTo("256"); + + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/HKDFTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/HKDFTest.java new file mode 100644 index 000000000..d58e52ca7 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/HKDFTest.java @@ -0,0 +1,102 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.algorithms.HKDF; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class HKDFTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/kdf/HKDFTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("HKDF"); + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo("256"); + + DetectionStore algorithmStore = + getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); + assertThat(algorithmStore).isNotNull(); + assertThat(algorithmStore.getDetectionValues().get(0).asString()).isEqualTo("SHA512"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(root).isInstanceOf(HKDF.class); + assertThat(root.getChildren()).hasSize(3); + assertThat(root.asString()).isEqualTo("HKDF-SHA-512"); + + INode md = root.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-512"); + assertThat(md.getChildren().get(DigestSize.class).asString()).isEqualTo("512"); + assertThat(md.getChildren().get(Oid.class).asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + assertThat(md.getChildren().get(Digest.class).asString()).isEqualTo("DIGEST"); + assertThat(md.getChildren().get(BlockSize.class).asString()).isEqualTo("1024"); + + assertThat(root.getChildren().get(KeyLength.class).asString()).isEqualTo("256"); + assertThat(root.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF1Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF1Test.java new file mode 100644 index 000000000..50318602e --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF1Test.java @@ -0,0 +1,117 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; +import com.ibm.mapper.model.algorithms.PBKDF1; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PBKDF1Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/kdf/PBKDF1TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("PBKDF1"); + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues()).hasSize(1); + assertThat(keySizeStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + assertThat(keySizeStore.getDetectionValues().get(0)).isInstanceOf(KeySize.class); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo("128"); + + DetectionStore algorithmStore = + getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); + assertThat(algorithmStore).isNotNull(); + assertThat(algorithmStore.getDetectionValues()).hasSize(1); + assertThat(algorithmStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + assertThat(algorithmStore.getDetectionValues().get(0)).isInstanceOf(Algorithm.class); + assertThat(algorithmStore.getDetectionValues().get(0).asString()).isEqualTo("SHA256"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PasswordBasedKeyDerivationFunction.class); + assertThat(root).isInstanceOf(PBKDF1.class); + assertThat(root.getChildren()).hasSize(3); + assertThat(root.asString()).isEqualTo("PBKDF1-SHA-256"); + + INode md = root.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-256"); + + assertThat(md.getChildren().get(DigestSize.class)).isNotNull(); + assertThat(md.getChildren().get(DigestSize.class).asString()).isEqualTo("256"); + assertThat(md.getChildren().get(Oid.class)).isNotNull(); + assertThat(md.getChildren().get(Oid.class).asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + assertThat(md.getChildren().get(Digest.class)).isNotNull(); + assertThat(md.getChildren().get(Digest.class).asString()).isEqualTo("DIGEST"); + assertThat(md.getChildren().get(BlockSize.class)).isNotNull(); + assertThat(md.getChildren().get(BlockSize.class).asString()).isEqualTo("512"); + + assertThat(root.getChildren().get(KeyLength.class)).isNotNull(); + assertThat(root.getChildren().get(KeyLength.class).asString()).isEqualTo("128"); + assertThat(root.getChildren().get(KeyDerivation.class)).isNotNull(); + assertThat(root.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF2Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF2Test.java new file mode 100644 index 000000000..429a251f7 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/PBKDF2Test.java @@ -0,0 +1,111 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.IterationCount; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.NumberOfIterations; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; +import com.ibm.mapper.model.algorithms.PBKDF2; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PBKDF2Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/kdf/PBKDF2TestFile.py", this); + } + + @Override + @SuppressWarnings("unchecked") + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("PBKDF2"); + + DetectionStore iterStore = + getStoreOfValueType(IterationCount.class, detectionStore.getChildren()); + assertThat(iterStore).isNotNull(); + assertThat(iterStore.getDetectionValues().get(0).asString()).isEqualTo("1000"); + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo("512"); + + DetectionStore algorithmStore = + getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); + assertThat(algorithmStore).isNotNull(); + assertThat(algorithmStore.getDetectionValues().get(0).asString()).isEqualTo("SHA512"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PasswordBasedKeyDerivationFunction.class); + assertThat(root).isInstanceOf(PBKDF2.class); + assertThat(root.getChildren()).hasSize(4); + assertThat(root.asString()).isEqualTo("PBKDF2-SHA-512"); + + INode md = root.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.getChildren()).hasSize(4); + assertThat(md.asString()).isEqualTo("SHA-512"); + assertThat(md.getChildren().get(DigestSize.class).asString()).isEqualTo("512"); + assertThat(md.getChildren().get(Oid.class).asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + assertThat(md.getChildren().get(Digest.class).asString()).isEqualTo("DIGEST"); + assertThat(md.getChildren().get(BlockSize.class).asString()).isEqualTo("1024"); + + assertThat(root.getChildren().get(KeyLength.class).asString()).isEqualTo("512"); + assertThat(root.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + assertThat(root.getChildren().get(NumberOfIterations.class).asString()).isEqualTo("1000"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/SP800108CounterTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/SP800108CounterTest.java new file mode 100644 index 000000000..f30fec7d2 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/SP800108CounterTest.java @@ -0,0 +1,82 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.algorithms.KDFCounter; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class SP800108CounterTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/kdf/SP800108CounterTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 1) { + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("SP800_108_Counter"); + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo("128"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(root).isInstanceOf(KDFCounter.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("SP800_108_CounterKDF"); + + assertThat(root.getChildren().get(KeyLength.class).asString()).isEqualTo("128"); + assertThat(root.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/ScryptTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/ScryptTest.java new file mode 100644 index 000000000..9aaac66ae --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/kdf/ScryptTest.java @@ -0,0 +1,82 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.kdf; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyDerivationFunctionContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; +import com.ibm.mapper.model.algorithms.Scrypt; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ScryptTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/kdf/ScryptTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyDerivationFunctionContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("scrypt"); + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, detectionStore.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo("256"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PasswordBasedKeyDerivationFunction.class); + assertThat(root).isInstanceOf(Scrypt.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("scrypt"); + + assertThat(root.getChildren().get(KeyLength.class).asString()).isEqualTo("256"); + assertThat(root.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/ECDHTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/ECDHTest.java new file mode 100644 index 000000000..98ca80bc8 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/ECDHTest.java @@ -0,0 +1,82 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.keyagreement; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKey; +import com.ibm.mapper.model.algorithms.ECDH; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ECDHTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/keyagreement/ECDHTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 1) { + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyAgreementContext.class); + assertThat(detectionStore.getDetectionValues().get(0)).isInstanceOf(ValueAction.class); + assertThat(detectionStore.getDetectionValues().get(0).asString()).isEqualTo("ECDH"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(KeyAgreement.class); + assertThat(root).isInstanceOf(ECDH.class); + assertThat(root.asString()).isEqualTo("ECDH"); + + assertThat(root.getChildren().get(Oid.class)).isNotNull(); + assertThat(root.getChildren().get(Oid.class).asString()).isEqualTo("1.3.132.1.12"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(KeyGeneration.class).asString()) + .isEqualTo("KEYGENERATION"); + assertThat(root.getChildren().get(PublicKey.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKey.class).asString()).isEqualTo("EC"); + assertThat(root.getChildren().get(PrivateKey.class)).isNotNull(); + assertThat(root.getChildren().get(PrivateKey.class).asString()) + .isEqualTo("EC-secp256r1"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X25519Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X25519Test.java new file mode 100644 index 000000000..0417b028f --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X25519Test.java @@ -0,0 +1,87 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.keyagreement; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.mapper.model.EllipticCurve; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKey; +import com.ibm.mapper.model.algorithms.X25519; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class X25519Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/keyagreement/X25519TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 1) { + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyAgreementContext.class); + assertThat(detectionStore.getDetectionValues().get(0)).isInstanceOf(ValueAction.class); + assertThat(detectionStore.getDetectionValues().get(0).asString()).isEqualTo("ECDH"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(KeyAgreement.class); + assertThat(root).isInstanceOf(X25519.class); + assertThat(root.asString()).isEqualTo("x25519"); + + assertThat(root.getChildren().get(Oid.class)).isNotNull(); + assertThat(root.getChildren().get(Oid.class).asString()).isEqualTo("1.3.101.110"); + assertThat(root.getChildren().get(EllipticCurve.class)).isNotNull(); + assertThat(root.getChildren().get(EllipticCurve.class).asString()) + .isEqualTo("Curve25519"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(KeyGeneration.class).asString()) + .isEqualTo("KEYGENERATION"); + assertThat(root.getChildren().get(PublicKey.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKey.class).asString()) + .isEqualTo("EC-Curve25519"); + assertThat(root.getChildren().get(PrivateKey.class)).isNotNull(); + assertThat(root.getChildren().get(PrivateKey.class).asString()) + .isEqualTo("EC-Curve25519"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X448Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X448Test.java new file mode 100644 index 000000000..ba0be8725 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/keyagreement/X448Test.java @@ -0,0 +1,86 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.keyagreement; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyAgreementContext; +import com.ibm.mapper.model.EllipticCurve; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKey; +import com.ibm.mapper.model.algorithms.X448; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class X448Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/keyagreement/X448TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 1) { + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyAgreementContext.class); + assertThat(detectionStore.getDetectionValues().get(0)).isInstanceOf(ValueAction.class); + assertThat(detectionStore.getDetectionValues().get(0).asString()).isEqualTo("ECDH"); + + assertThat(nodes).hasSize(1); + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(KeyAgreement.class); + assertThat(root).isInstanceOf(X448.class); + assertThat(root.asString()).isEqualTo("x448"); + + assertThat(root.getChildren().get(Oid.class)).isNotNull(); + assertThat(root.getChildren().get(Oid.class).asString()).isEqualTo("1.3.101.111"); + assertThat(root.getChildren().get(EllipticCurve.class)).isNotNull(); + assertThat(root.getChildren().get(EllipticCurve.class).asString()) + .isEqualTo("Curve448"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(KeyGeneration.class).asString()) + .isEqualTo("KEYGENERATION"); + assertThat(root.getChildren().get(PublicKey.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKey.class).asString()).isEqualTo("EC-Curve448"); + assertThat(root.getChildren().get(PrivateKey.class)).isNotNull(); + assertThat(root.getChildren().get(PrivateKey.class).asString()) + .isEqualTo("EC-Curve448"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/CMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/CMACTest.java new file mode 100644 index 000000000..5daf5dbff --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/CMACTest.java @@ -0,0 +1,92 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.Oid; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class CMACTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/mac/CMACTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(Algorithm.class); + assertThat(value.asString()).isEqualTo("AES"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + INode mac = nodes.get(0); + assertThat(mac).isInstanceOf(Mac.class); + assertThat(mac.asString()).isEqualTo("CMAC-AES"); + assertThat(mac.getChildren()).hasSize(2); + + INode cipher = mac.getChildren().get(BlockCipher.class); + assertThat(cipher).isNotNull(); + assertThat(cipher.asString()).isEqualTo("AES"); + + INode blockSize = cipher.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("128"); + + INode oid = cipher.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.1"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/HMACTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/HMACTest.java new file mode 100644 index 000000000..b62ec4557 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/HMACTest.java @@ -0,0 +1,91 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class HMACTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/mac/HMACTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(Algorithm.class); + assertThat(value.asString()).isEqualTo("SHA256"); + + assertThat(nodes).hasSize(1); + INode mac = nodes.get(0); + assertThat(mac).isInstanceOf(Mac.class); + assertThat(mac.asString()).isEqualTo("HMAC-SHA-256"); + assertThat(mac.getChildren()).hasSize(3); + + INode digest = mac.getChildren().get(MessageDigest.class); + assertThat(digest).isNotNull(); + assertThat(digest.asString()).isEqualTo("SHA-256"); + assertThat(digest.getChildren()).hasSize(4); + + assertThat(digest.getChildren().get(Digest.class)).isNotNull(); + assertThat(digest.getChildren().get(Digest.class).asString()).isEqualTo("DIGEST"); + assertThat(digest.getChildren().get(BlockSize.class)).isNotNull(); + assertThat(digest.getChildren().get(BlockSize.class).asString()).isEqualTo("512"); + assertThat(digest.getChildren().get(Oid.class)).isNotNull(); + assertThat(digest.getChildren().get(Oid.class).asString()) + .isEqualTo("2.16.840.1.101.3.4.2.1"); + assertThat(digest.getChildren().get(DigestSize.class)).isNotNull(); + assertThat(digest.getChildren().get(DigestSize.class).asString()).isEqualTo("256"); + + assertThat(mac.getChildren().get(Oid.class)).isNotNull(); + assertThat(mac.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.113549.2.9"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC128Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC128Test.java new file mode 100644 index 000000000..fd08672ab --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC128Test.java @@ -0,0 +1,98 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.ExtendableOutputFunction; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.algorithms.KMAC; +import com.ibm.mapper.model.algorithms.shake.CSHAKE; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class KMAC128Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/mac/KMAC128TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("KMAC128"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + INode mac = nodes.get(0); + assertThat(mac).isInstanceOf(KMAC.class); + assertThat(mac.getKind()).isEqualTo(Mac.class); + assertThat(mac.asString()).isEqualTo("KMAC128"); + assertThat(mac.getChildren()).hasSize(3); + + INode parameterSetIdentifier = mac.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.asString()).isEqualTo("128"); + + // the parameter set identifier is the security strength, not an output length + assertThat(mac.getChildren().get(DigestSize.class)).isNull(); + + INode cshake = mac.getChildren().get(ExtendableOutputFunction.class); + assertThat(cshake).isNotNull(); + assertThat(cshake).isInstanceOf(CSHAKE.class); + assertThat(cshake.asString()).isEqualTo("cSHAKE128"); + + INode tag = mac.getChildren().get(Tag.class); + assertThat(tag).isNotNull(); + assertThat(tag.asString()).isEqualTo("TAG"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC256Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC256Test.java new file mode 100644 index 000000000..6d66676f6 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/KMAC256Test.java @@ -0,0 +1,98 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.ExtendableOutputFunction; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.algorithms.KMAC; +import com.ibm.mapper.model.algorithms.shake.CSHAKE; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class KMAC256Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/mac/KMAC256TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + assertThat(detectionStore.getChildren()).isEmpty(); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("KMAC256"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + INode mac = nodes.get(0); + assertThat(mac).isInstanceOf(KMAC.class); + assertThat(mac.getKind()).isEqualTo(Mac.class); + assertThat(mac.asString()).isEqualTo("KMAC256"); + assertThat(mac.getChildren()).hasSize(3); + + INode parameterSetIdentifier = mac.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.asString()).isEqualTo("256"); + + // the parameter set identifier is the security strength, not an output length + assertThat(mac.getChildren().get(DigestSize.class)).isNull(); + + INode cshake = mac.getChildren().get(ExtendableOutputFunction.class); + assertThat(cshake).isNotNull(); + assertThat(cshake).isInstanceOf(CSHAKE.class); + assertThat(cshake.asString()).isEqualTo("cSHAKE256"); + + INode tag = mac.getChildren().get(Tag.class); + assertThat(tag).isNotNull(); + assertThat(tag.asString()).isEqualTo("TAG"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/Poly1305Test.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/Poly1305Test.java new file mode 100644 index 000000000..5b5af659f --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/mac/Poly1305Test.java @@ -0,0 +1,94 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.mac; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Algorithm; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.functionality.Tag; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class Poly1305Test extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/mac/Poly1305TestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + assertThat(findingId).isZero(); + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(MacContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("Poly1305"); + + // the cipher is detected as a child of the Poly1305 detection + assertThat(detectionStore.getChildren()).hasSize(1); + DetectionStore cipherStore = + detectionStore.getChildren().get(0); + assertThat(cipherStore.getDetectionValues()).hasSize(1); + IValue cipherValue = cipherStore.getDetectionValues().get(0); + assertThat(cipherValue).isInstanceOf(Algorithm.class); + assertThat(cipherValue.asString()).isEqualTo("AES"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + + INode mac = nodes.get(0); + assertThat(mac.getKind()).isEqualTo(Mac.class); + assertThat(mac.asString()).isEqualTo("Poly1305"); + assertThat(mac.getChildren()).hasSize(2); + + INode tag = mac.getChildren().get(Tag.class); + assertThat(tag).isNotNull(); + assertThat(tag.asString()).isEqualTo("TAG"); + + INode cipher = mac.getChildren().get(BlockCipher.class); + assertThat(cipher).isNotNull(); + assertThat(cipher.asString()).isEqualTo("AES"); + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/DSATest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/DSATest.java new file mode 100644 index 000000000..c247d110a --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/DSATest.java @@ -0,0 +1,144 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.rule.RuleSets; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class DSATest extends TestBase { + + public DSATest() { + super(RuleSets.rulesOf(PythonCryptoDSA.class)); + } + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/publickey/DSATestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + switch (findingId) { + case 0 -> { + // DSA.generate(bits=2048) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue kSz = detectionStore.getDetectionValues().get(0); + assertThat(kSz).isInstanceOf(KeySize.class); + assertThat(((KeySize) kSz).asString()).isEqualTo("2048"); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PrivateKey.class); + assertThat(root.getChildren()).hasSize(3); + assertThat(root.asString()).isEqualTo("DSA"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + + INode sig = root.getChildren().get(Signature.class); + assertThat(sig).isNotNull(); + assertThat(sig.getChildren().get(Oid.class).asString()) + .isEqualTo("1.2.840.10040.4.1"); + + INode kgen = root.getChildren().get(KeyGeneration.class); + assertThat(kgen).isNotNull(); + + INode klen = root.getChildren().get(KeyLength.class); + assertThat(klen).isNotNull(); + assertThat(klen.asString()).isEqualTo("2048"); + } + case 1 -> { + // DSA.construct(...) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat( + ((KeyAction) detectionStore.getDetectionValues().get(0)) + .getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("DSA"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat( + root.getChildren() + .get(Signature.class) + .getChildren() + .get(Oid.class) + .asString()) + .isEqualTo("1.2.840.10040.4.1"); + } + case 2 -> { + // DSA.import_key(...) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat( + ((KeyAction) detectionStore.getDetectionValues().get(0)) + .getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("DSA"); + assertThat( + root.getChildren() + .get(Signature.class) + .getChildren() + .get(Oid.class) + .asString()) + .isEqualTo("1.2.840.10040.4.1"); + } + default -> throw new AssertionError("Unexpected findingId: " + findingId); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ECCTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ECCTest.java new file mode 100644 index 000000000..18c970a5e --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ECCTest.java @@ -0,0 +1,134 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.Curve; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.rule.RuleSets; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ECCTest extends TestBase { + + public ECCTest() { + super(RuleSets.rulesOf(PythonCryptoECC.class)); + } + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/publickey/ECCTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + switch (findingId) { + case 0 -> { + // ECC.generate(curve="Ed25519") + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue v = detectionStore.getDetectionValues().get(0); + assertThat(v).isInstanceOf(Curve.class); + assertThat(v.asString()).isEqualTo("Ed25519"); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PrivateKey.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("Ed25519"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + + INode sig = root.getChildren().get(Signature.class); + assertThat(sig).isNotNull(); + assertThat(sig.getChildren().get(Oid.class).asString()).isEqualTo("1.3.101.112"); + } + case 1 -> { + // ECC.construct(curve="Curve448", seed=b"A" * 56) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + IValue v = detectionStore.getDetectionValues().get(0); + assertThat(v).isInstanceOf(Curve.class); + assertThat(v.asString()).isEqualTo("Curve448"); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("x448"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + + INode ka = root.getChildren().get(KeyAgreement.class); + assertThat(ka).isNotNull(); + assertThat(ka.getChildren().get(Oid.class).asString()).isEqualTo("1.3.101.111"); + } + case 2 -> { + // ECC.import_key(...) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat( + ((KeyAction) detectionStore.getDetectionValues().get(0)) + .getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("EC"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat( + root.getChildren() + .get(PublicKeyEncryption.class) + .getChildren() + .get(Oid.class) + .asString()) + .isEqualTo("1.2.840.10045.2.1"); + } + default -> throw new AssertionError("Unexpected findingId: " + findingId); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ElGamalTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ElGamalTest.java new file mode 100644 index 000000000..050dd9292 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/ElGamalTest.java @@ -0,0 +1,100 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ElGamalTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/publickey/ElGamalTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + switch (findingId) { + case 0 -> { + // ElGamal.generate(2048, None) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue v = detectionStore.getDetectionValues().get(0); + assertThat(v).isInstanceOf(KeySize.class); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PrivateKey.class); + assertThat(root.getChildren()).hasSize(3); + assertThat(root.asString()).isEqualTo("ElGamal"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKeyEncryption.class)).isNotNull(); + assertThat(root.getChildren().get(KeyLength.class)).isNotNull(); + } + case 1 -> { + // ElGamal.construct((2,3,4)) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat( + ((KeyAction) detectionStore.getDetectionValues().get(0)) + .getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("ElGamal"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKeyEncryption.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKeyEncryption.class).getChildren()) + .isEmpty(); + } + default -> throw new AssertionError("Unexpected findingId: " + findingId); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/RSATest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/RSATest.java new file mode 100644 index 000000000..27e1cfdcd --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/publickey/RSATest.java @@ -0,0 +1,146 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.publickey; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.PrivateKeyContext; +import com.ibm.engine.rule.RuleSets; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PrivateKey; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class RSATest extends TestBase { + + public RSATest() { + super(RuleSets.rulesOf(PythonCryptoRSA.class)); + } + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/publickey/RSATestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + switch (findingId) { + case 0 -> { + // RSA.generate(bits=2048) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(PrivateKeyContext.class); + IValue kSz = detectionStore.getDetectionValues().get(0); + assertThat(kSz).isInstanceOf(KeySize.class); + assertThat(((KeySize) kSz).asString()).isEqualTo("2048"); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(PrivateKey.class); + assertThat(root.getChildren()).hasSize(3); + assertThat(root.asString()).isEqualTo("RSA"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + + INode pke = root.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren().get(Oid.class).asString()) + .isEqualTo("1.2.840.113549.1.1.1"); + + INode kgen = root.getChildren().get(KeyGeneration.class); + assertThat(kgen).isNotNull(); + + INode klen = root.getChildren().get(KeyLength.class); + assertThat(klen).isNotNull(); + assertThat(klen.asString()).isEqualTo("2048"); + } + case 1 -> { + // RSA.construct(...) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + IValue kAction = detectionStore.getDetectionValues().get(0); + assertThat(kAction).isInstanceOf(KeyAction.class); + assertThat(((KeyAction) kAction).getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("RSA"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat(root.getChildren().get(PublicKeyEncryption.class)).isNotNull(); + assertThat( + root.getChildren() + .get(PublicKeyEncryption.class) + .getChildren() + .get(Oid.class) + .asString()) + .isEqualTo("1.2.840.113549.1.1.1"); + } + case 2 -> { + // RSA.import_key(...) + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat( + ((KeyAction) detectionStore.getDetectionValues().get(0)) + .getAction()) + .isEqualTo(KeyAction.Action.GENERATION); + assertThat(detectionStore.getChildren()).isEmpty(); + + INode root = nodes.get(0); + assertThat(root.getKind()).isEqualTo(Key.class); + assertThat(root.getChildren()).hasSize(2); + assertThat(root.asString()).isEqualTo("RSA"); + assertThat(root.getChildren().get(KeyGeneration.class)).isNotNull(); + assertThat( + root.getChildren() + .get(PublicKeyEncryption.class) + .getChildren() + .get(Oid.class) + .asString()) + .isEqualTo("1.2.840.113549.1.1.1"); + } + default -> throw new AssertionError("Unexpected findingId: " + findingId); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSSignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSSignTest.java new file mode 100644 index 000000000..1f63da7a4 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSSignTest.java @@ -0,0 +1,128 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class DSSSignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/DSSSignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("DSS"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("SIGN"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("DSA-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.3.2"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("DSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode sign = sig.getChildren().get(Sign.class); + assertThat(sign).isNotNull(); + assertThat(sign.getChildren()).isEmpty(); + assertThat(sign.asString()).isEqualTo("SIGN"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSVerifyTest.java new file mode 100644 index 000000000..f953bb88d --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/DSSVerifyTest.java @@ -0,0 +1,128 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class DSSVerifyTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/DSSVerifyTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("DSS"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("VERIFY"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("DSA-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("2.16.840.1.101.3.4.3.2"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("DSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode verify = sig.getChildren().get(Verify.class); + assertThat(verify).isNotNull(); + assertThat(verify.getChildren()).isEmpty(); + assertThat(verify.asString()).isEqualTo("VERIFY"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSASignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSASignTest.java new file mode 100644 index 000000000..44c82d828 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSASignTest.java @@ -0,0 +1,132 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ECDSASignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/ECDSASignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("ECDSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("SIGN"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("ECDSA-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.10045.4.3.2"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("EC"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.10045.2.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode sign = sig.getChildren().get(Sign.class); + assertThat(sign).isNotNull(); + assertThat(sign.getChildren()).isEmpty(); + assertThat(sign.asString()).isEqualTo("SIGN"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSAVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSAVerifyTest.java new file mode 100644 index 000000000..e34b57645 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/ECDSAVerifyTest.java @@ -0,0 +1,132 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class ECDSAVerifyTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/ECDSAVerifyTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("ECDSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("VERIFY"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("ECDSA-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.10045.4.3.2"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("EC"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.10045.2.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode verify = sig.getChildren().get(Verify.class); + assertThat(verify).isNotNull(); + assertThat(verify.getChildren()).isEmpty(); + assertThat(verify.asString()).isEqualTo("VERIFY"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSASignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSASignTest.java new file mode 100644 index 000000000..8f4f6d267 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSASignTest.java @@ -0,0 +1,127 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class EdDSASignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/EdDSASignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("EDDSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("SIGN"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(3); + assertThat(sig.asString()).isEqualTo("EdDSA"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("EC"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.10045.2.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-512"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("512"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1024"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode sign = sig.getChildren().get(Sign.class); + assertThat(sign).isNotNull(); + assertThat(sign.getChildren()).isEmpty(); + assertThat(sign.asString()).isEqualTo("SIGN"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSAVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSAVerifyTest.java new file mode 100644 index 000000000..12582c85d --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/EdDSAVerifyTest.java @@ -0,0 +1,127 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class EdDSAVerifyTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/EdDSAVerifyTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("EDDSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("VERIFY"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(3); + assertThat(sig.asString()).isEqualTo("EdDSA"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("EC"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + assertThat(pke.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.10045.2.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-512"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.3"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("512"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("1024"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode verify = sig.getChildren().get(Verify.class); + assertThat(verify).isNotNull(); + assertThat(verify.getChildren()).isEmpty(); + assertThat(verify.asString()).isEqualTo("VERIFY"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15SignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15SignTest.java new file mode 100644 index 000000000..4fd919bd5 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15SignTest.java @@ -0,0 +1,141 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PKCS1v15SignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/PKCS1v15SignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("SIGN"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("RSA-PKCS1-1.5-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.11"); + + INode padding = sig.getChildren().get(Padding.class); + assertThat(padding).isNotNull(); + assertThat(padding.getChildren()).isEmpty(); + assertThat(padding.asString()).isEqualTo("PKCS1"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("RSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + INode pkeOid = pke.getChildren().get(Oid.class); + assertThat(pkeOid).isNotNull(); + assertThat(pkeOid.getChildren()).isEmpty(); + assertThat(pkeOid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode sign = sig.getChildren().get(Sign.class); + assertThat(sign).isNotNull(); + assertThat(sign.getChildren()).isEmpty(); + assertThat(sign.asString()).isEqualTo("SIGN"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15VerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15VerifyTest.java new file mode 100644 index 000000000..d5486d323 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PKCS1v15VerifyTest.java @@ -0,0 +1,142 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Padding; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PKCS1v15VerifyTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/PKCS1v15VerifyTestFile.py", + this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RSA"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("VERIFY"); + + // translation + assertThat(nodes).hasSize(1); + INode sig = nodes.get(0); + assertThat(sig.getKind()).isEqualTo(Signature.class); + assertThat(sig.getChildren()).hasSize(4); + assertThat(sig.asString()).isEqualTo("RSA-PKCS1-1.5-SHA-256"); + + INode oid = sig.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.11"); + + INode padding = sig.getChildren().get(Padding.class); + assertThat(padding).isNotNull(); + assertThat(padding.getChildren()).isEmpty(); + assertThat(padding.asString()).isEqualTo("PKCS1"); + + INode key = sig.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("RSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + INode pkeOid = pke.getChildren().get(Oid.class); + assertThat(pkeOid).isNotNull(); + assertThat(pkeOid.getChildren()).isEmpty(); + assertThat(pkeOid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + INode md = sig.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode verify = sig.getChildren().get(Verify.class); + assertThat(verify).isNotNull(); + assertThat(verify.getChildren()).isEmpty(); + assertThat(verify.asString()).isEqualTo("VERIFY"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSSignTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSSignTest.java new file mode 100644 index 000000000..f87f57004 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSSignTest.java @@ -0,0 +1,135 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.ProbabilisticSignatureScheme; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PSSSignTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/PSSSignTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RSA-PSS"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("SIGN"); + + // translation + assertThat(nodes).hasSize(1); + INode pss = nodes.get(0); + assertThat(pss.getKind()).isEqualTo(ProbabilisticSignatureScheme.class); + assertThat(pss.getChildren()).hasSize(4); + assertThat(pss.asString()).isEqualTo("RSA-PSS"); + + INode oid = pss.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.10"); + + INode key = pss.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("RSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + INode pkeOid = pke.getChildren().get(Oid.class); + assertThat(pkeOid).isNotNull(); + assertThat(pkeOid.getChildren()).isEmpty(); + assertThat(pkeOid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + INode md = pss.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode sign = pss.getChildren().get(Sign.class); + assertThat(sign).isNotNull(); + assertThat(sign.getChildren()).isEmpty(); + assertThat(sign.asString()).isEqualTo("SIGN"); + } + } +} diff --git a/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSVerifyTest.java b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSVerifyTest.java new file mode 100644 index 000000000..6ada8cfb9 --- /dev/null +++ b/python/src/test/java/com/ibm/plugin/rules/detection/pycrypto/signature/PSSVerifyTest.java @@ -0,0 +1,135 @@ +/* + * Sonar Cryptography Plugin + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ibm.plugin.rules.detection.pycrypto.signature; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.mapper.model.BlockSize; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Key; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.ProbabilisticSignatureScheme; +import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.Digest; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; +import org.sonar.plugins.python.api.PythonCheck; +import org.sonar.plugins.python.api.PythonVisitorContext; +import org.sonar.plugins.python.api.symbols.Symbol; +import org.sonar.plugins.python.api.tree.Tree; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class PSSVerifyTest extends TestBase { + + @Test + void test() { + PythonCheckVerifier.verify( + "src/test/files/rules/detection/pycrypto/signature/PSSVerifyTestFile.py", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull DetectionStore detectionStore, + @Nonnull List nodes) { + if (findingId == 2) { + // detection store + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(SignatureContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isEqualTo("RSA-PSS"); + + DetectionStore sigStore = + getStoreOfValueType(SignatureAction.class, detectionStore.getChildren()); + assertThat(sigStore).isNotNull(); + assertThat(sigStore.getDetectionValues()).hasSize(1); + assertThat(sigStore.getDetectionValueContext()).isInstanceOf(SignatureContext.class); + IValue sigValue = sigStore.getDetectionValues().get(0); + assertThat(sigValue).isInstanceOf(SignatureAction.class); + assertThat(sigValue.asString()).isEqualTo("VERIFY"); + + // translation + assertThat(nodes).hasSize(1); + INode pss = nodes.get(0); + assertThat(pss.getKind()).isEqualTo(ProbabilisticSignatureScheme.class); + assertThat(pss.getChildren()).hasSize(4); + assertThat(pss.asString()).isEqualTo("RSA-PSS"); + + INode oid = pss.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.getChildren()).isEmpty(); + assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.10"); + + INode key = pss.getChildren().get(Key.class); + assertThat(key).isNotNull(); + assertThat(key.asString()).isEqualTo("RSA"); + assertThat(key.getChildren()).hasSize(2); + INode keyGen = key.getChildren().get(KeyGeneration.class); + assertThat(keyGen).isNotNull(); + assertThat(keyGen.getChildren()).isEmpty(); + assertThat(keyGen.asString()).isEqualTo("KEYGENERATION"); + INode pke = key.getChildren().get(PublicKeyEncryption.class); + assertThat(pke).isNotNull(); + INode pkeOid = pke.getChildren().get(Oid.class); + assertThat(pkeOid).isNotNull(); + assertThat(pkeOid.getChildren()).isEmpty(); + assertThat(pkeOid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + INode md = pss.getChildren().get(MessageDigest.class); + assertThat(md).isNotNull(); + assertThat(md.asString()).isEqualTo("SHA-256"); + assertThat(md.getChildren()).hasSize(4); + INode mdOid = md.getChildren().get(Oid.class); + assertThat(mdOid).isNotNull(); + assertThat(mdOid.getChildren()).isEmpty(); + assertThat(mdOid.asString()).isEqualTo("2.16.840.1.101.3.4.2.1"); + INode digestSize = md.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.getChildren()).isEmpty(); + assertThat(digestSize.asString()).isEqualTo("256"); + INode blockSize = md.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.getChildren()).isEmpty(); + assertThat(blockSize.asString()).isEqualTo("512"); + INode digest = md.getChildren().get(Digest.class); + assertThat(digest).isNotNull(); + assertThat(digest.getChildren()).isEmpty(); + assertThat(digest.asString()).isEqualTo("DIGEST"); + + INode verify = pss.getChildren().get(Verify.class); + assertThat(verify).isNotNull(); + assertThat(verify.getChildren()).isEmpty(); + assertThat(verify.asString()).isEqualTo("VERIFY"); + } + } +}