From 2a29f8f99d7db994aeaf26c96aa7634341070fca Mon Sep 17 00:00:00 2001 From: Chennamma Date: Sat, 2 May 2026 11:02:02 +0000 Subject: [PATCH 1/2] feat(csharp): add ML-KEM detection rules for .NET 9 Signed-off-by: Chennamma --- .../rules/detection/CSharpDetectionRules.java | 4 +- .../rules/detection/dotnet/DotNetMLKem.java | 72 +++++++++++++++++++ .../contexts/CSharpKeyContextTranslator.java | 2 + .../detection/dotnet/DotNetMLKemTestFile.cs | 19 +++++ .../detection/dotnet/DotNetMLKemTest.java | 72 +++++++++++++++++++ 5 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKem.java create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetMLKemTestFile.cs create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKemTest.java diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/CSharpDetectionRules.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/CSharpDetectionRules.java index 92576f65..144faf37 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/CSharpDetectionRules.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/CSharpDetectionRules.java @@ -27,6 +27,7 @@ import com.ibm.plugin.rules.detection.dotnet.DotNetECDiffieHellman; import com.ibm.plugin.rules.detection.dotnet.DotNetECDsa; import com.ibm.plugin.rules.detection.dotnet.DotNetHMAC; +import com.ibm.plugin.rules.detection.dotnet.DotNetMLKem; import com.ibm.plugin.rules.detection.dotnet.DotNetRC2; import com.ibm.plugin.rules.detection.dotnet.DotNetRSA; import com.ibm.plugin.rules.detection.dotnet.DotNetRfc2898DeriveBytes; @@ -56,7 +57,8 @@ public static List> rules() { DotNetDSA.rules().stream(), DotNetSHA.rules().stream(), DotNetHMAC.rules().stream(), - DotNetRfc2898DeriveBytes.rules().stream()) + DotNetRfc2898DeriveBytes.rules().stream(), + DotNetMLKem.rules().stream()) .flatMap(i -> i) .toList(); } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKem.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKem.java new file mode 100644 index 00000000..72786761 --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKem.java @@ -0,0 +1,72 @@ +/* + * 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.dotnet; + +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.factory.ValueActionFactory; +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; + +/** + * Detection rules for ML-KEM (FIPS 203) in .NET 9+. + * + *

Detects key generation for all three parameter sets: + * + *

    + *
  • {@code MLKem512.GenerateKey()} + *
  • {@code MLKem768.GenerateKey()} + *
  • {@code MLKem1024.GenerateKey()} + *
+ */ +@SuppressWarnings("java:S1192") +public final class DotNetMLKem { + + private DotNetMLKem() { + // nothing + } + + private static IDetectionRule mlKemRule(String className, String value) { + return new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(className) + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>(value)) + .withoutParameters() + .buildForContext(new KeyContext(Map.of("kind", "MLKEM"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + } + + private static final IDetectionRule MLKEM_512 = mlKemRule("MLKem512", "MLKEM512"); + + private static final IDetectionRule MLKEM_768 = mlKemRule("MLKem768", "MLKEM768"); + + private static final IDetectionRule MLKEM_1024 = + mlKemRule("MLKem1024", "MLKEM1024"); + + @Nonnull + public static List> rules() { + return List.of(MLKEM_512, MLKEM_768, MLKEM_1024); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpKeyContextTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpKeyContextTranslator.java index 6b50ce83..0a016536 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpKeyContextTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpKeyContextTranslator.java @@ -32,6 +32,7 @@ import com.ibm.mapper.model.algorithms.DSA; import com.ibm.mapper.model.algorithms.ECDH; import com.ibm.mapper.model.algorithms.ECDSA; +import com.ibm.mapper.model.algorithms.MLKEM; import com.ibm.mapper.model.algorithms.PBKDF2; import com.ibm.mapper.model.algorithms.RSA; import com.ibm.mapper.utils.DetectionLocation; @@ -57,6 +58,7 @@ public final class CSharpKeyContextTranslator implements IContextTranslation Optional.of(new ECDH(detectionLocation)); case "DSA" -> Optional.of(new DSA(detectionLocation)); case "KDF" -> Optional.of(new PBKDF2(detectionLocation)); + case "MLKEM" -> Optional.of(new MLKEM(detectionLocation)); default -> Optional.empty(); }; } else if (value instanceof KeySize keySize) { diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetMLKemTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetMLKemTestFile.cs new file mode 100644 index 00000000..ab8d2e34 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetMLKemTestFile.cs @@ -0,0 +1,19 @@ +using System.Security.Cryptography; + +public class DotNetMLKemTest +{ + public void TestMLKem512() + { + var key = MLKem512.GenerateKey(); // Noncompliant + } + + public void TestMLKem768() + { + var key = MLKem768.GenerateKey(); // Noncompliant + } + + public void TestMLKem1024() + { + var key = MLKem1024.GenerateKey(); // Noncompliant + } +} \ No newline at end of file diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKemTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKemTest.java new file mode 100644 index 00000000..04a0b19c --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKemTest.java @@ -0,0 +1,72 @@ +/* + * 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.dotnet; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.language.csharp.CSharpCheck; +import com.ibm.engine.language.csharp.CSharpScanContext; +import com.ibm.engine.language.csharp.CSharpSymbol; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyEncapsulationMechanism; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +class DotNetMLKemTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetMLKemTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isIn("MLKEM512", "MLKEM768", "MLKEM1024"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(KeyEncapsulationMechanism.class); + assertThat(node.asString()).startsWith("ML-KEM"); + } +} From 0d96ce3dcfbe712901ebdb076f5a7f66eea2014b Mon Sep 17 00:00:00 2001 From: Chennamma Date: Sat, 2 May 2026 17:49:28 +0000 Subject: [PATCH 2/2] feat(csharp): add ML-DSA detection rules for .NET 9 Signed-off-by: Chennamma --- .../rules/detection/CSharpDetectionRules.java | 4 +- .../rules/detection/dotnet/DotNetMLDsa.java | 71 ++++++++++++++++++ .../contexts/CSharpKeyContextTranslator.java | 2 + .../detection/dotnet/DotNetMLDsaTestFile.cs | 19 +++++ .../detection/dotnet/DotNetMLDsaTest.java | 72 +++++++++++++++++++ 5 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsa.java create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetMLDsaTestFile.cs create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsaTest.java diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/CSharpDetectionRules.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/CSharpDetectionRules.java index 144faf37..fafb0c98 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/CSharpDetectionRules.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/CSharpDetectionRules.java @@ -27,6 +27,7 @@ import com.ibm.plugin.rules.detection.dotnet.DotNetECDiffieHellman; import com.ibm.plugin.rules.detection.dotnet.DotNetECDsa; import com.ibm.plugin.rules.detection.dotnet.DotNetHMAC; +import com.ibm.plugin.rules.detection.dotnet.DotNetMLDsa; import com.ibm.plugin.rules.detection.dotnet.DotNetMLKem; import com.ibm.plugin.rules.detection.dotnet.DotNetRC2; import com.ibm.plugin.rules.detection.dotnet.DotNetRSA; @@ -58,7 +59,8 @@ public static List> rules() { DotNetSHA.rules().stream(), DotNetHMAC.rules().stream(), DotNetRfc2898DeriveBytes.rules().stream(), - DotNetMLKem.rules().stream()) + DotNetMLKem.rules().stream(), + DotNetMLDsa.rules().stream()) .flatMap(i -> i) .toList(); } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsa.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsa.java new file mode 100644 index 00000000..b46315b4 --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsa.java @@ -0,0 +1,71 @@ +/* + * 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.dotnet; + +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.factory.ValueActionFactory; +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; + +/** + * Detection rules for ML-DSA (FIPS 204) in .NET 9+. + * + *

Detects key generation for all three parameter sets: + * + *

    + *
  • {@code MLDsa44.GenerateKey()} + *
  • {@code MLDsa65.GenerateKey()} + *
  • {@code MLDsa87.GenerateKey()} + *
+ */ +@SuppressWarnings("java:S1192") +public final class DotNetMLDsa { + + private DotNetMLDsa() { + // nothing + } + + private static IDetectionRule mlDsaRule(String className, String value) { + return new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(className) + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>(value)) + .withoutParameters() + .buildForContext(new KeyContext(Map.of("kind", "MLDSA"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + } + + private static final IDetectionRule MLDSA_44 = mlDsaRule("MLDsa44", "MLDSA44"); + + private static final IDetectionRule MLDSA_65 = mlDsaRule("MLDsa65", "MLDSA65"); + + private static final IDetectionRule MLDSA_87 = mlDsaRule("MLDsa87", "MLDSA87"); + + @Nonnull + public static List> rules() { + return List.of(MLDSA_44, MLDSA_65, MLDSA_87); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpKeyContextTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpKeyContextTranslator.java index 0a016536..5348c5d3 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpKeyContextTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpKeyContextTranslator.java @@ -32,6 +32,7 @@ import com.ibm.mapper.model.algorithms.DSA; import com.ibm.mapper.model.algorithms.ECDH; import com.ibm.mapper.model.algorithms.ECDSA; +import com.ibm.mapper.model.algorithms.MLDSA; import com.ibm.mapper.model.algorithms.MLKEM; import com.ibm.mapper.model.algorithms.PBKDF2; import com.ibm.mapper.model.algorithms.RSA; @@ -58,6 +59,7 @@ public final class CSharpKeyContextTranslator implements IContextTranslation Optional.of(new ECDH(detectionLocation)); case "DSA" -> Optional.of(new DSA(detectionLocation)); case "KDF" -> Optional.of(new PBKDF2(detectionLocation)); + case "MLDSA" -> Optional.of(new MLDSA(detectionLocation)); case "MLKEM" -> Optional.of(new MLKEM(detectionLocation)); default -> Optional.empty(); }; diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetMLDsaTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetMLDsaTestFile.cs new file mode 100644 index 00000000..60040523 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetMLDsaTestFile.cs @@ -0,0 +1,19 @@ +using System.Security.Cryptography; + +public class DotNetMLDsaTest +{ + public void TestMLDsa44() + { + var key = MLDsa44.GenerateKey(); // Noncompliant + } + + public void TestMLDsa65() + { + var key = MLDsa65.GenerateKey(); // Noncompliant + } + + public void TestMLDsa87() + { + var key = MLDsa87.GenerateKey(); // Noncompliant + } +} \ No newline at end of file diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsaTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsaTest.java new file mode 100644 index 00000000..0e12d6f9 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsaTest.java @@ -0,0 +1,72 @@ +/* + * 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.dotnet; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ibm.engine.detection.DetectionStore; +import com.ibm.engine.language.csharp.CSharpCheck; +import com.ibm.engine.language.csharp.CSharpScanContext; +import com.ibm.engine.language.csharp.CSharpSymbol; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Signature; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +class DotNetMLDsaTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetMLDsaTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + /* + * Detection Store + */ + assertThat(detectionStore.getDetectionValues()).hasSize(1); + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(ValueAction.class); + assertThat(value.asString()).isIn("MLDSA44", "MLDSA65", "MLDSA87"); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(Signature.class); + assertThat(node.asString()).startsWith("ML-DSA"); + } +}