From 4116fb0e76337c1cb064b1600613b75d9d382715 Mon Sep 17 00:00:00 2001 From: Fynn Thierling Date: Wed, 19 Aug 2026 11:54:35 +0200 Subject: [PATCH 01/10] feat(csharp): extend AES family detection with full operation coverage Ports the AES/AesGcm/AesCcm rule expansion from the aes-related-crypto reference branch: CreateEncryptor/CreateDecryptor, EncryptCbc/DecryptCbc, EncryptEcb/DecryptEcb, EncryptCfb/DecryptCfb, Try* variants, GenerateKey/ GenerateIV, and AesGcm/AesCcm Encrypt/Decrypt operations as depending detection rules on the existing constructor/factory rules. Adds ModeFactory(String) to emit a constant Mode value when the mode is encoded in the method name (e.g. EncryptCbc) rather than a parameter, and wires ENCRYPT/DECRYPT/GENERATEKEY/GENERATEIV functionality translation into CSharpCipherContextTranslator. Signed-off-by: Fynn Thierling --- .../rules/detection/dotnet/DotNetAES.java | 600 +++++++++++++++++- .../CSharpCipherContextTranslator.java | 8 + .../dotnet/DotNetAESComprehensiveTestFile.cs | 457 +++++++++++++ .../dotnet/DotNetAESComprehensiveTest.java | 448 +++++++++++++ .../ibm/engine/model/factory/ModeFactory.java | 19 + 5 files changed, 1511 insertions(+), 21 deletions(-) create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetAESComprehensiveTestFile.cs create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java index f750ea4e0..4a9f13df8 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java @@ -23,6 +23,7 @@ import com.ibm.engine.language.csharp.tree.CSharpTree; import com.ibm.engine.model.Size; import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.BlockSizeFactory; import com.ibm.engine.model.factory.KeySizeFactory; import com.ibm.engine.model.factory.ModeFactory; import com.ibm.engine.model.factory.PaddingFactory; @@ -30,19 +31,27 @@ import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.builder.DetectionRuleBuilder; import java.util.List; +import java.util.stream.Stream; import javax.annotation.Nonnull; /** - * Detection rules for AES usage in System.Security.Cryptography. + * Detection rules for the AES family in System.Security.Cryptography. * - *

Detects: + *

Classes covered: * *

+ * + *

Architecture: all methods inherited from {@code SymmetricAlgorithm} (EncryptCbc, DecryptCbc, + * CreateEncryptor, property setters, etc.) are expressed as depending rules attached to + * each primary creation rule. The detection engine tracks the variable and fires these rules on + * every matching method call, regardless of the concrete Aes subclass. */ @SuppressWarnings("java:S1192") public final class DotNetAES { @@ -51,6 +60,10 @@ private DotNetAES() { // nothing } + // ========================================================================= + // Property setter rules (synthetic set_X method invocations) + // ========================================================================= + // aes.Mode = CipherMode.CBC → synthetic set_Mode(CipherMode.CBC) private static final IDetectionRule AES_SET_MODE = new DetectionRuleBuilder() @@ -87,10 +100,523 @@ private DotNetAES() { .inBundle(() -> "DotNet") .withoutDependingDetectionRules(); + // aes.FeedbackSize = 128 → synthetic set_FeedbackSize(128) + private static final IDetectionRule AES_SET_FEEDBACK_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_FeedbackSize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new BlockSizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + private static final List> PROPERTY_SETTER_RULES = - List.of(AES_SET_MODE, AES_SET_KEY_SIZE, AES_SET_PADDING); + List.of(AES_SET_MODE, AES_SET_KEY_SIZE, AES_SET_PADDING, AES_SET_FEEDBACK_SIZE); + + // ========================================================================= + // CreateEncryptor / CreateDecryptor rules + // ========================================================================= + + // aes.CreateEncryptor() + private static final IDetectionRule AES_CREATE_ENCRYPTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateEncryptor") + .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // aes.CreateEncryptor(byte[] key, byte[] iv) + private static final IDetectionRule AES_CREATE_ENCRYPTOR_WITH_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateEncryptor") + .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .withMethodParameter(MethodMatcher.ANY) // key bytes + .withMethodParameter(MethodMatcher.ANY) // iv bytes + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // aes.CreateDecryptor() + private static final IDetectionRule AES_CREATE_DECRYPTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateDecryptor") + .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // aes.CreateDecryptor(byte[] key, byte[] iv) + private static final IDetectionRule AES_CREATE_DECRYPTOR_WITH_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateDecryptor") + .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .withMethodParameter(MethodMatcher.ANY) // key bytes + .withMethodParameter(MethodMatcher.ANY) // iv bytes + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptCbc / DecryptCbc rules + // Mode is constant "CBC" (from method name); padding is detected from last param. + // Two overloads: 3-param and 4-param (with output buffer). + // ========================================================================= + + // EncryptCbc(plaintext, iv, padding) + private static final IDetectionRule AES_ENCRYPT_CBC_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCbc(plaintext, iv, destination, padding) [output-buffer overload] + private static final IDetectionRule AES_ENCRYPT_CBC_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCbc(ciphertext, iv, padding) + private static final IDetectionRule AES_DECRYPT_CBC_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); - // Aes.Create() — abstract factory + // DecryptCbc(ciphertext, iv, destination, padding) + private static final IDetectionRule AES_DECRYPT_CBC_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptEcb / DecryptEcb rules + // Mode is constant "ECB" (from method name); no IV parameter. + // Two overloads: 2-param and 3-param (with output buffer). + // ========================================================================= + + // EncryptEcb(plaintext, padding) + private static final IDetectionRule AES_ENCRYPT_ECB_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptEcb(plaintext, destination, padding) + private static final IDetectionRule AES_ENCRYPT_ECB_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptEcb(ciphertext, padding) + private static final IDetectionRule AES_DECRYPT_ECB_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptEcb(ciphertext, destination, padding) + private static final IDetectionRule AES_DECRYPT_ECB_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptCfb / DecryptCfb rules + // Mode is constant "CFB"; padding detected from 3rd param; feedbackSize ignored. + // Two overloads: 4-param and 5-param (with output buffer). + // ========================================================================= + + // EncryptCfb(plaintext, iv, padding, feedbackSize) + private static final IDetectionRule AES_ENCRYPT_CFB_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCfb(plaintext, iv, destination, padding, feedbackSize) + private static final IDetectionRule AES_ENCRYPT_CFB_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCfb(ciphertext, iv, padding, feedbackSize) + private static final IDetectionRule AES_DECRYPT_CFB_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCfb(ciphertext, iv, destination, padding, feedbackSize) + private static final IDetectionRule AES_DECRYPT_CFB_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // TryEncrypt* / TryDecrypt* rules + // Signatures: + // TryEncryptCbc(plaintext, iv, destination, out bytesWritten, padding) — 5 params + // TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, padding) — 5 params + // TryEncryptEcb(plaintext, destination, padding, out bytesWritten) — 4 params + // TryDecryptEcb(ciphertext, destination, padding, out bytesWritten) — 4 params + // TryEncryptCfb(plaintext, iv, destination, out bytesWritten, padding, fs) — 6 params + // TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, padding, fs) — 6 params + // ========================================================================= + + // TryEncryptCbc(plaintext, iv, destination, out bytesWritten, padding) + private static final IDetectionRule AES_TRY_ENCRYPT_CBC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, padding) + private static final IDetectionRule AES_TRY_DECRYPT_CBC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptEcb(plaintext, destination, padding, out bytesWritten) + private static final IDetectionRule AES_TRY_ENCRYPT_ECB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptEcb(ciphertext, destination, padding, out bytesWritten) + private static final IDetectionRule AES_TRY_DECRYPT_ECB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptCfb(plaintext, iv, destination, out bytesWritten, padding, feedbackSize) + private static final IDetectionRule AES_TRY_ENCRYPT_CFB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, padding, feedbackSize) + private static final IDetectionRule AES_TRY_DECRYPT_CFB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Key / IV generation rules + // ========================================================================= + + // aes.GenerateKey() — generates a new random key (size determined by KeySize property) + private static final IDetectionRule AES_GENERATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("GenerateKey")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // aes.GenerateIV() — generates a new random initialization vector + private static final IDetectionRule AES_GENERATE_IV = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GenerateIV") + .shouldBeDetectedAs(new ValueActionFactory<>("GenerateIV")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Aggregated depending-rule lists + // ========================================================================= + + /** + * All cipher operation rules that fire on a tracked Aes-family variable. Includes + * CreateEncryptor/CreateDecryptor, direct mode-specific Encrypt/Decrypt methods, Try* variants, + * and key/IV generation. + */ + private static final List> CIPHER_OP_RULES = + List.of( + AES_CREATE_ENCRYPTOR, + AES_CREATE_ENCRYPTOR_WITH_KEY, + AES_CREATE_DECRYPTOR, + AES_CREATE_DECRYPTOR_WITH_KEY, + AES_ENCRYPT_CBC_3, + AES_ENCRYPT_CBC_4, + AES_DECRYPT_CBC_3, + AES_DECRYPT_CBC_4, + AES_ENCRYPT_ECB_2, + AES_ENCRYPT_ECB_3, + AES_DECRYPT_ECB_2, + AES_DECRYPT_ECB_3, + AES_ENCRYPT_CFB_4, + AES_ENCRYPT_CFB_5, + AES_DECRYPT_CFB_4, + AES_DECRYPT_CFB_5, + AES_TRY_ENCRYPT_CBC, + AES_TRY_DECRYPT_CBC, + AES_TRY_ENCRYPT_ECB, + AES_TRY_DECRYPT_ECB, + AES_TRY_ENCRYPT_CFB, + AES_TRY_DECRYPT_CFB, + AES_GENERATE_KEY, + AES_GENERATE_IV); + + /** Full set of depending rules for all Aes-derived classes. */ + private static final List> AES_DEPENDING_RULES = + Stream.concat(PROPERTY_SETTER_RULES.stream(), CIPHER_OP_RULES.stream()).toList(); + + // ========================================================================= + // AesGcm / AesCcm AEAD operation rules + // ========================================================================= + + // aesGcm.Encrypt(nonce, plaintext, ciphertext, tag [, aad]) + private static final IDetectionRule AES_GCM_ENCRYPT_OP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Encrypt") + .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // aesGcm.Decrypt(nonce, ciphertext, tag, plaintext [, aad]) + private static final IDetectionRule AES_GCM_DECRYPT_OP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Decrypt") + .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> GCM_OP_RULES = + List.of(AES_GCM_ENCRYPT_OP, AES_GCM_DECRYPT_OP); + + // aesCcm.Encrypt(nonce, plaintext, ciphertext, tag [, aad]) + private static final IDetectionRule AES_CCM_ENCRYPT_OP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Encrypt") + .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // aesCcm.Decrypt(nonce, ciphertext, tag, plaintext [, aad]) + private static final IDetectionRule AES_CCM_DECRYPT_OP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Decrypt") + .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> CCM_OP_RULES = + List.of(AES_CCM_ENCRYPT_OP, AES_CCM_DECRYPT_OP); + + // ========================================================================= + // Primary creation rules + // ========================================================================= + + // Aes.Create() — abstract factory, no parameters private static final IDetectionRule AES_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -100,9 +626,21 @@ private DotNetAES() { .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(PROPERTY_SETTER_RULES); + .withDependingDetectionRules(AES_DEPENDING_RULES); - // new AesManaged() — legacy concrete class + // Aes.Create("AES") — named factory (obsolete in .NET 7, still detectable) + private static final IDetectionRule AES_CREATE_NAMED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Aes") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("AES")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(AES_DEPENDING_RULES); + + // new AesManaged() — pure-managed implementation private static final IDetectionRule AES_MANAGED = new DetectionRuleBuilder() .createDetectionRule() @@ -112,22 +650,36 @@ private DotNetAES() { .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(PROPERTY_SETTER_RULES); + .withDependingDetectionRules(AES_DEPENDING_RULES); - // new AesCng() — CNG-backed implementation - private static final IDetectionRule AES_CNG = + // new AesCng() / new AesCng("keyName") / new AesCng("keyName", provider) / ... + // Matches all AesCng constructors (ephemeral 0-param and persisted 1–3 params). + // Uses withAnyParameters() to avoid double-detection that would occur if a separate + // withoutParameters() rule were added alongside this one. + private static final IDetectionRule AES_CNG_NAMED = new DetectionRuleBuilder() .createDetectionRule() .forObjectTypes("AesCng") .forMethods("") .shouldBeDetectedAs(new ValueActionFactory<>("AES")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(AES_DEPENDING_RULES); + + // new AesCryptoServiceProvider() — legacy CAPI implementation + private static final IDetectionRule AES_CSP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("AesCryptoServiceProvider") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("AES")) .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(PROPERTY_SETTER_RULES); + .withDependingDetectionRules(AES_DEPENDING_RULES); - // new AesGcm(key) — GCM authenticated encryption - // TODO: capture key parameter (byte[] key) to extract key length as a known gap + // new AesGcm(key) — GCM authenticated encryption (byte[] or ReadOnlySpan, 1 param) private static final IDetectionRule AES_GCM = new DetectionRuleBuilder() .createDetectionRule() @@ -137,10 +689,9 @@ private DotNetAES() { .withAnyParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(GCM_OP_RULES); - // new AesCcm(key) — CCM authenticated encryption - // TODO: capture key parameter (byte[] key) to extract key length as a known gap + // new AesCcm(key) — CCM authenticated encryption (byte[] or ReadOnlySpan, 1 param) private static final IDetectionRule AES_CCM = new DetectionRuleBuilder() .createDetectionRule() @@ -150,10 +701,17 @@ private DotNetAES() { .withAnyParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(CCM_OP_RULES); @Nonnull public static List> rules() { - return List.of(AES_CREATE, AES_MANAGED, AES_CNG, AES_GCM, AES_CCM); + return List.of( + AES_CREATE, + AES_CREATE_NAMED, + AES_MANAGED, + AES_CNG_NAMED, + AES_CSP, + AES_GCM, + AES_CCM); } } diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java index 4fa746e89..af4d98563 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java @@ -39,6 +39,10 @@ import com.ibm.mapper.model.algorithms.DESede; import com.ibm.mapper.model.algorithms.RC2; import com.ibm.mapper.model.algorithms.RSA; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.Generate; +import com.ibm.mapper.model.functionality.KeyGeneration; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; import javax.annotation.Nonnull; @@ -68,6 +72,10 @@ public final class CSharpCipherContextTranslator Optional.of(new DESede(detectionLocation)); case "RSA" -> Optional.of(new RSA(detectionLocation)); case "RC2" -> Optional.of(new RC2(detectionLocation)); + case "ENCRYPT" -> Optional.of(new Encrypt(detectionLocation)); + case "DECRYPT" -> Optional.of(new Decrypt(detectionLocation)); + case "GENERATEKEY" -> Optional.of(new KeyGeneration(detectionLocation)); + case "GENERATEIV" -> Optional.of(new Generate(detectionLocation)); default -> Optional.empty(); }; if (result.isPresent()) { diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetAESComprehensiveTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetAESComprehensiveTestFile.cs new file mode 100644 index 000000000..c24f86475 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetAESComprehensiveTestFile.cs @@ -0,0 +1,457 @@ +/* + * Comprehensive test file for System.Security.Cryptography AES detection rules. + * + * Covers all five AES-related classes and their complete API surface: + * - Aes (abstract base) + * - AesManaged, AesCng, AesCryptoServiceProvider (derived from Aes) + * - AesGcm, AesCcm (AEAD, separate class hierarchy, same namespace) + * + * Architecture note: all methods inherited from SymmetricAlgorithm (EncryptCbc, + * CreateEncryptor, etc.) are covered once here. The detection engine tracks the + * variable and fires the same depending rules for every concrete Aes subclass. + */ + +using System.Security.Cryptography; + +public class DotNetAESComprehensiveTest +{ + // ------------------------------------------------------------------------- + // Section 1: Factory methods / constructors + // ------------------------------------------------------------------------- + + public void TestAesCreate() + { + var aes = Aes.Create(); + } + + public void TestAesCreateNamed() + { + var aes = Aes.Create("AES"); + } + + public void TestAesManaged() + { + var aes = new AesManaged(); + } + + public void TestAesCng() + { + var aes = new AesCng(); + } + + public void TestAesCngNamed() + { + var aes = new AesCng("myKey"); + } + + public void TestAesCsp() + { + var aes = new AesCryptoServiceProvider(); + } + + public void TestAesGcm() + { + byte[] key = new byte[32]; + var aesGcm = new AesGcm(key); + } + + public void TestAesCcm() + { + byte[] key = new byte[32]; + var aesCcm = new AesCcm(key); + } + + // ------------------------------------------------------------------------- + // Section 2: Property setters (via assignment → synthetic set_X invocations) + // ------------------------------------------------------------------------- + + public void TestPropertyModeCBC() + { + var aes = Aes.Create(); + aes.Mode = CipherMode.CBC; + } + + public void TestPropertyModeECB() + { + var aes = Aes.Create(); + aes.Mode = CipherMode.ECB; + } + + public void TestPropertyModeCFB() + { + var aes = Aes.Create(); + aes.Mode = CipherMode.CFB; + } + + public void TestPropertyModeOFB() + { + var aes = Aes.Create(); + aes.Mode = CipherMode.OFB; + } + + public void TestPropertyModeCTS() + { + var aes = Aes.Create(); + aes.Mode = CipherMode.CTS; + } + + public void TestPropertyKeySize128() + { + var aes = Aes.Create(); + aes.KeySize = 128; + } + + public void TestPropertyKeySize192() + { + var aes = Aes.Create(); + aes.KeySize = 192; + } + + public void TestPropertyKeySize256() + { + var aes = Aes.Create(); + aes.KeySize = 256; + } + + public void TestPropertyPaddingPKCS7() + { + var aes = Aes.Create(); + aes.Padding = PaddingMode.PKCS7; + } + + public void TestPropertyPaddingNone() + { + var aes = Aes.Create(); + aes.Padding = PaddingMode.None; + } + + public void TestPropertyPaddingZeros() + { + var aes = Aes.Create(); + aes.Padding = PaddingMode.Zeros; + } + + public void TestPropertyPaddingANSIX923() + { + var aes = Aes.Create(); + aes.Padding = PaddingMode.ANSIX923; + } + + public void TestPropertyFeedbackSize() + { + var aes = Aes.Create(); + aes.FeedbackSize = 128; + } + + public void TestPropertyIV() + { + var aes = Aes.Create(); + aes.IV = new byte[16]; + } + + public void TestPropertyKey() + { + var aes = Aes.Create(); + aes.Key = new byte[32]; + } + + // ------------------------------------------------------------------------- + // Section 3: CreateEncryptor / CreateDecryptor + // ------------------------------------------------------------------------- + + public void TestCreateEncryptorNoArgs() + { + var aes = Aes.Create(); + var encryptor = aes.CreateEncryptor(); + } + + public void TestCreateEncryptorWithArgs() + { + var aes = Aes.Create(); + byte[] key = new byte[32]; + byte[] iv = new byte[16]; + var encryptor = aes.CreateEncryptor(key, iv); + } + + public void TestCreateDecryptorNoArgs() + { + var aes = Aes.Create(); + var decryptor = aes.CreateDecryptor(); + } + + public void TestCreateDecryptorWithArgs() + { + var aes = Aes.Create(); + byte[] key = new byte[32]; + byte[] iv = new byte[16]; + var decryptor = aes.CreateDecryptor(key, iv); + } + + // ------------------------------------------------------------------------- + // Section 4: Direct mode-specific encrypt methods + // ------------------------------------------------------------------------- + + public void TestEncryptCbc() + { + var aes = Aes.Create(); + byte[] plaintext = new byte[32]; + byte[] iv = new byte[16]; + byte[] ciphertext = aes.EncryptCbc(plaintext, iv, PaddingMode.PKCS7); + } + + public void TestEncryptEcb() + { + var aes = Aes.Create(); + byte[] plaintext = new byte[32]; + byte[] ciphertext = aes.EncryptEcb(plaintext, PaddingMode.None); + } + + public void TestEncryptCfb() + { + var aes = Aes.Create(); + byte[] plaintext = new byte[32]; + byte[] iv = new byte[16]; + byte[] ciphertext = aes.EncryptCfb(plaintext, iv, PaddingMode.None, 128); + } + + // ------------------------------------------------------------------------- + // Section 5: Direct mode-specific decrypt methods + // ------------------------------------------------------------------------- + + public void TestDecryptCbc() + { + var aes = Aes.Create(); + byte[] ciphertext = new byte[32]; + byte[] iv = new byte[16]; + byte[] plaintext = aes.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); + } + + public void TestDecryptEcb() + { + var aes = Aes.Create(); + byte[] ciphertext = new byte[32]; + byte[] plaintext = aes.DecryptEcb(ciphertext, PaddingMode.None); + } + + public void TestDecryptCfb() + { + var aes = Aes.Create(); + byte[] ciphertext = new byte[32]; + byte[] iv = new byte[16]; + byte[] plaintext = aes.DecryptCfb(ciphertext, iv, PaddingMode.None, 128); + } + + // ------------------------------------------------------------------------- + // Section 6: Try* variants + // ------------------------------------------------------------------------- + + public void TestTryEncryptCbc() + { + var aes = Aes.Create(); + byte[] plaintext = new byte[32]; + byte[] iv = new byte[16]; + byte[] destination = new byte[48]; + int bytesWritten; + aes.TryEncryptCbc(plaintext, iv, destination, out bytesWritten, PaddingMode.PKCS7); + } + + public void TestTryDecryptCbc() + { + var aes = Aes.Create(); + byte[] ciphertext = new byte[32]; + byte[] iv = new byte[16]; + byte[] destination = new byte[32]; + int bytesWritten; + aes.TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, PaddingMode.PKCS7); + } + + public void TestTryEncryptEcb() + { + var aes = Aes.Create(); + byte[] plaintext = new byte[32]; + byte[] destination = new byte[48]; + int bytesWritten; + aes.TryEncryptEcb(plaintext, destination, PaddingMode.None, out bytesWritten); + } + + public void TestTryDecryptEcb() + { + var aes = Aes.Create(); + byte[] ciphertext = new byte[32]; + byte[] destination = new byte[32]; + int bytesWritten; + aes.TryDecryptEcb(ciphertext, destination, PaddingMode.None, out bytesWritten); + } + + public void TestTryEncryptCfb() + { + var aes = Aes.Create(); + byte[] plaintext = new byte[32]; + byte[] iv = new byte[16]; + byte[] destination = new byte[48]; + int bytesWritten; + aes.TryEncryptCfb(plaintext, iv, destination, out bytesWritten, PaddingMode.None, 128); + } + + public void TestTryDecryptCfb() + { + var aes = Aes.Create(); + byte[] ciphertext = new byte[32]; + byte[] iv = new byte[16]; + byte[] destination = new byte[32]; + int bytesWritten; + aes.TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, PaddingMode.None, 128); + } + + // ------------------------------------------------------------------------- + // Section 7: Key/IV generation + // ------------------------------------------------------------------------- + + public void TestGenerateKey() + { + var aes = Aes.Create(); + aes.GenerateKey(); + } + + public void TestGenerateIV() + { + var aes = Aes.Create(); + aes.GenerateIV(); + } + + // ------------------------------------------------------------------------- + // Section 8: AesGcm AEAD operations + // ------------------------------------------------------------------------- + + public void TestAesGcmEncrypt() + { + byte[] key = new byte[32]; + var aesGcm = new AesGcm(key); + byte[] nonce = new byte[12]; + byte[] plaintext = new byte[32]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + byte[] aad = new byte[8]; + aesGcm.Encrypt(nonce, plaintext, ciphertext, tag, aad); + } + + public void TestAesGcmDecrypt() + { + byte[] key = new byte[32]; + var aesGcm = new AesGcm(key); + byte[] nonce = new byte[12]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + byte[] plaintext = new byte[32]; + byte[] aad = new byte[8]; + aesGcm.Decrypt(nonce, ciphertext, tag, plaintext, aad); + } + + // ------------------------------------------------------------------------- + // Section 9: AesCcm AEAD operations + // ------------------------------------------------------------------------- + + public void TestAesCcmEncrypt() + { + byte[] key = new byte[32]; + var aesCcm = new AesCcm(key); + byte[] nonce = new byte[12]; + byte[] plaintext = new byte[32]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + byte[] aad = new byte[8]; + aesCcm.Encrypt(nonce, plaintext, ciphertext, tag, aad); + } + + public void TestAesCcmDecrypt() + { + byte[] key = new byte[32]; + var aesCcm = new AesCcm(key); + byte[] nonce = new byte[12]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + byte[] plaintext = new byte[32]; + byte[] aad = new byte[8]; + aesCcm.Decrypt(nonce, ciphertext, tag, plaintext, aad); + } + + // ------------------------------------------------------------------------- + // Section 10: Combined usage patterns (real-world scenarios) + // Demonstrates that depending rules fire correctly for ALL derived classes. + // ------------------------------------------------------------------------- + + public void TestAesCbcFullFlow() + { + var aes = Aes.Create(); + aes.Mode = CipherMode.CBC; + aes.KeySize = 256; + aes.Padding = PaddingMode.PKCS7; + var encryptor = aes.CreateEncryptor(); + } + + public void TestAesCngEncryptCbc() + { + var aes = new AesCng(); + aes.Mode = CipherMode.CBC; + byte[] plaintext = new byte[32]; + byte[] iv = new byte[16]; + byte[] ciphertext = aes.EncryptCbc(plaintext, iv, PaddingMode.PKCS7); + } + + public void TestAesCspDecryptCbc() + { + var aes = new AesCryptoServiceProvider(); + byte[] ciphertext = new byte[32]; + byte[] iv = new byte[16]; + byte[] plaintext = aes.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); + } + + public void TestAesManagedCfbFeedback() + { + var aes = new AesManaged(); + aes.Mode = CipherMode.CFB; + aes.FeedbackSize = 128; + byte[] plaintext = new byte[32]; + byte[] iv = new byte[16]; + byte[] ciphertext = aes.EncryptCfb(plaintext, iv, PaddingMode.None, 128); + } + + public void TestAesCbcWithEncryptorOverload() + { + var aes = Aes.Create(); + byte[] key = new byte[32]; + byte[] iv = new byte[16]; + var encryptor = aes.CreateEncryptor(key, iv); + } + + public void TestAesEcbEncrypt() + { + var aes = new AesCng(); + byte[] plaintext = new byte[32]; + byte[] ciphertext = aes.EncryptEcb(plaintext, PaddingMode.None); + } + + public void TestAesGcmFullFlow() + { + byte[] key = new byte[32]; + var aesGcm = new AesGcm(key); + byte[] nonce = new byte[12]; + byte[] plaintext = new byte[32]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + byte[] aad = new byte[8]; + aesGcm.Encrypt(nonce, plaintext, ciphertext, tag, aad); + } + + public void TestAesCcmFullFlow() + { + byte[] key = new byte[32]; + var aesCcm = new AesCcm(key); + byte[] nonce = new byte[12]; + byte[] plaintext = new byte[32]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + byte[] aad = new byte[8]; + aesCcm.Encrypt(nonce, plaintext, ciphertext, tag, aad); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java new file mode 100644 index 000000000..f09691b26 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java @@ -0,0 +1,448 @@ +/* + * 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.BlockSize; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.Mode; +import com.ibm.engine.model.Padding; +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.KeyLength; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.Generate; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all AES-related detection rules (DotNetAES.java). + * + *

Covers all five AES-related classes and their complete API surface: + * + *

+ * + *

Finding mapping (one finding per test method in DotNetAESComprehensiveTestFile.cs): + * + *

+ * Section 1 – factory methods / constructors (findings 0–7):
+ *   0  TestAesCreate                → AES
+ *   1  TestAesCreateNamed           → AES
+ *   2  TestAesManaged               → AES
+ *   3  TestAesCng                   → AES
+ *   4  TestAesCngNamed              → AES
+ *   5  TestAesCsp                   → AES
+ *   6  TestAesGcm                   → AES
+ *   7  TestAesCcm                   → AES
+ *
+ * Section 2 – property setters (findings 8–22):
+ *   8  TestPropertyModeCBC          → AES-CBC
+ *   9  TestPropertyModeECB          → AES-ECB
+ *   10 TestPropertyModeCFB          → AES-CFB
+ *   11 TestPropertyModeOFB          → AES-OFB
+ *   12 TestPropertyModeCTS          → AES-CTS
+ *   13 TestPropertyKeySize128       → AES-128
+ *   14 TestPropertyKeySize192       → AES-192
+ *   15 TestPropertyKeySize256       → AES-256
+ *   16 TestPropertyPaddingPKCS7     → AES-PKCS7
+ *   17 TestPropertyPaddingNone      → AES-None
+ *   18 TestPropertyPaddingZeros     → AES-Zeros
+ *   19 TestPropertyPaddingANSIX923  → AES-ANSIX923
+ *   20 TestPropertyFeedbackSize     → AES (BlockSize=128 detected, merged into default)
+ *   21 TestPropertyIV               → AES (no IV rule)
+ *   22 TestPropertyKey              → AES (no Key rule)
+ *
+ * Section 3 – CreateEncryptor/CreateDecryptor (findings 23–26):
+ *   23 TestCreateEncryptorNoArgs     → AES + Encrypt
+ *   24 TestCreateEncryptorWithArgs   → AES + Encrypt
+ *   25 TestCreateDecryptorNoArgs     → AES + Decrypt
+ *   26 TestCreateDecryptorWithArgs   → AES + Decrypt
+ *
+ * Section 4 – direct encrypt (findings 27–29):
+ *   27 TestEncryptCbc               → AES-CBC-PKCS7
+ *   28 TestEncryptEcb               → AES-ECB-None
+ *   29 TestEncryptCfb               → AES-CFB-None
+ *
+ * Section 5 – direct decrypt (findings 30–32):
+ *   30 TestDecryptCbc               → AES-CBC-PKCS7
+ *   31 TestDecryptEcb               → AES-ECB-None
+ *   32 TestDecryptCfb               → AES-CFB-None
+ *
+ * Section 6 – Try* variants (findings 33–38):
+ *   33 TestTryEncryptCbc            → AES-CBC-PKCS7
+ *   34 TestTryDecryptCbc            → AES-CBC-PKCS7
+ *   35 TestTryEncryptEcb            → AES-ECB-None
+ *   36 TestTryDecryptEcb            → AES-ECB-None
+ *   37 TestTryEncryptCfb            → AES-CFB-None
+ *   38 TestTryDecryptCfb            → AES-CFB-None
+ *
+ * Section 7 – key/IV generation (findings 39–40):
+ *   39 TestGenerateKey              → AES + KeyGeneration
+ *   40 TestGenerateIV               → AES + Generate
+ *
+ * Section 8 – AesGcm AEAD ops (findings 41–42):
+ *   41 TestAesGcmEncrypt            → AES + Encrypt
+ *   42 TestAesGcmDecrypt            → AES + Decrypt
+ *
+ * Section 9 – AesCcm AEAD ops (findings 43–44):
+ *   43 TestAesCcmEncrypt            → AES + Encrypt
+ *   44 TestAesCcmDecrypt            → AES + Decrypt
+ *
+ * Section 10 – combined usage patterns (findings 45–52):
+ *   45 TestAesCbcFullFlow           → AES-256-CBC-PKCS7 + Encrypt
+ *   46 TestAesCngEncryptCbc         → AES-CBC-PKCS7
+ *   47 TestAesCspDecryptCbc         → AES-CBC-PKCS7
+ *   48 TestAesManagedCfbFeedback    → AES-CFB-None
+ *   49 TestAesCbcWithEncryptorOverload → AES + Encrypt
+ *   50 TestAesEcbEncrypt            → AES-ECB-None
+ *   51 TestAesGcmFullFlow           → AES + Encrypt
+ *   52 TestAesCcmFullFlow           → AES + Encrypt
+ * 
+ */ +class DotNetAESComprehensiveTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetAESComprehensiveTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + // Every top-level finding must be AES + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("AES"); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: simple constructors — only AES, no children fired + // ----------------------------------------------------------------- + case 0, 1, 2, 3, 4, 5, 6, 7 -> { + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("AES"); + } + + // ----------------------------------------------------------------- + // Section 2a: property Mode setters + // ----------------------------------------------------------------- + case 8 -> assertModeFindings(detectionStore, nodes, "CBC", "AES-CBC"); + case 9 -> assertModeFindings(detectionStore, nodes, "ECB", "AES-ECB"); + case 10 -> assertModeFindings(detectionStore, nodes, "CFB", "AES-CFB"); + case 11 -> assertModeFindings(detectionStore, nodes, "OFB", "AES-OFB"); + case 12 -> assertModeFindings(detectionStore, nodes, "CTS", "AES-CTS"); + + // ----------------------------------------------------------------- + // Section 2b: property KeySize setters + // ----------------------------------------------------------------- + case 13 -> assertKeySizeFindings(detectionStore, nodes, "128", "AES-128"); + case 14 -> assertKeySizeFindings(detectionStore, nodes, "192", "AES-192"); + case 15 -> assertKeySizeFindings(detectionStore, nodes, "256", "AES-256"); + + // ----------------------------------------------------------------- + // Section 2c: property Padding setters + // ----------------------------------------------------------------- + case 16 -> assertPaddingFindings(detectionStore, nodes, "PKCS7", "AES-PKCS7"); + case 17 -> assertPaddingFindings(detectionStore, nodes, "None", "AES-None"); + case 18 -> assertPaddingFindings(detectionStore, nodes, "Zeros", "AES-Zeros"); + case 19 -> assertPaddingFindings(detectionStore, nodes, "ANSIX923", "AES-ANSIX923"); + + // ----------------------------------------------------------------- + // Section 2d: FeedbackSize setter — BlockSize(128) detected but + // matches the default AES block size so node string stays "AES" + // ----------------------------------------------------------------- + case 20 -> { + DetectionStore fbStore = + getStoreOfValueType(BlockSize.class, detectionStore.getChildren()); + assertThat(fbStore).isNotNull(); + assertThat(fbStore.getDetectionValues()).hasSize(1); + assertThat(fbStore.getDetectionValues().get(0).asString()).isEqualTo("128"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("AES"); + } + + // ----------------------------------------------------------------- + // Section 2e: IV and Key setters — no detection rules for these + // ----------------------------------------------------------------- + case 21, 22 -> { + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("AES"); + } + + // ----------------------------------------------------------------- + // Section 3: CreateEncryptor / CreateDecryptor + // ----------------------------------------------------------------- + case 23, 24 -> assertEncryptFindings(detectionStore, nodes, "AES"); + case 25, 26 -> assertDecryptFindings(detectionStore, nodes, "AES"); + + // ----------------------------------------------------------------- + // Section 4: direct mode-specific encrypt + // ----------------------------------------------------------------- + case 27 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "AES-CBC-PKCS7"); + case 28 -> + assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "AES-ECB-None"); + case 29 -> + assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "AES-CFB-None"); + + // ----------------------------------------------------------------- + // Section 5: direct mode-specific decrypt + // ----------------------------------------------------------------- + case 30 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "AES-CBC-PKCS7"); + case 31 -> + assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "AES-ECB-None"); + case 32 -> + assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "AES-CFB-None"); + + // ----------------------------------------------------------------- + // Section 6: Try* variants + // ----------------------------------------------------------------- + case 33, 34 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "AES-CBC-PKCS7"); + case 35, 36 -> + assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "AES-ECB-None"); + case 37, 38 -> + assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "AES-CFB-None"); + + // ----------------------------------------------------------------- + // Section 7: GenerateKey / GenerateIV + // ----------------------------------------------------------------- + case 39 -> { + // aes.GenerateKey() → KeyGeneration functionality node + DetectionStore + genKeyStore = + getStoreOfValueType( + ValueAction.class, detectionStore.getChildren()); + assertThat(genKeyStore).isNotNull(); + assertThat(genKeyStore.getDetectionValues().get(0).asString()) + .isEqualTo("GenerateKey"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).getChildren().get(KeyGeneration.class)).isNotNull(); + } + case 40 -> { + // aes.GenerateIV() → Generate functionality node + DetectionStore + genIvStore = + getStoreOfValueType( + ValueAction.class, detectionStore.getChildren()); + assertThat(genIvStore).isNotNull(); + assertThat(genIvStore.getDetectionValues().get(0).asString()) + .isEqualTo("GenerateIV"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).getChildren().get(Generate.class)).isNotNull(); + } + + // ----------------------------------------------------------------- + // Section 8: AesGcm AEAD operations + // ----------------------------------------------------------------- + case 41 -> assertEncryptFindings(detectionStore, nodes, "AES"); + case 42 -> assertDecryptFindings(detectionStore, nodes, "AES"); + + // ----------------------------------------------------------------- + // Section 9: AesCcm AEAD operations + // ----------------------------------------------------------------- + case 43 -> assertEncryptFindings(detectionStore, nodes, "AES"); + case 44 -> assertDecryptFindings(detectionStore, nodes, "AES"); + + // ----------------------------------------------------------------- + // Section 10: combined usage patterns + // ----------------------------------------------------------------- + case 45 -> { + // TestAesCbcFullFlow: Mode=CBC, KeySize=256, Padding=PKCS7, CreateEncryptor + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(BlockCipher.class); + assertThat(node.asString()).isEqualTo("AES-256-CBC-PKCS7"); + assertThat(node.getChildren().get(KeyLength.class)).isNotNull(); + assertThat(node.getChildren().get(KeyLength.class).asString()).isEqualTo("256"); + assertThat(node.getChildren().get(com.ibm.mapper.model.Mode.class)).isNotNull(); + assertThat(node.getChildren().get(Encrypt.class)).isNotNull(); + } + case 46 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "AES-CBC-PKCS7"); + case 47 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "AES-CBC-PKCS7"); + case 48 -> + assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "AES-CFB-None"); + case 49 -> assertEncryptFindings(detectionStore, nodes, "AES"); + case 50 -> + assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "AES-ECB-None"); + case 51 -> assertEncryptFindings(detectionStore, nodes, "AES"); + case 52 -> assertEncryptFindings(detectionStore, nodes, "AES"); + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertModeFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedMode, + @Nonnull String expectedNodeString) { + + DetectionStore modeStore = + getStoreOfValueType(Mode.class, store.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues()).hasSize(1); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo(expectedMode); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(com.ibm.mapper.model.Mode.class)).isNotNull(); + } + + private void assertKeySizeFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedKeySize, + @Nonnull String expectedNodeString) { + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, store.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues()).hasSize(1); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo(expectedKeySize); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(KeyLength.class)).isNotNull(); + assertThat(nodes.get(0).getChildren().get(KeyLength.class).asString()) + .isEqualTo(expectedKeySize); + } + + private void assertPaddingFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedPadding, + @Nonnull String expectedNodeString) { + + DetectionStore paddingStore = + getStoreOfValueType(Padding.class, store.getChildren()); + assertThat(paddingStore).isNotNull(); + assertThat(paddingStore.getDetectionValues()).hasSize(1); + assertThat(paddingStore.getDetectionValues().get(0).asString()).isEqualTo(expectedPadding); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + } + + private void assertEncryptFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedNodeString) { + + DetectionStore encryptStore = + getStoreOfValueType(ValueAction.class, store.getChildren()); + assertThat(encryptStore).isNotNull(); + assertThat(encryptStore.getDetectionValues()).hasSize(1); + assertThat(encryptStore.getDetectionValues().get(0).asString()).isEqualTo("ENCRYPT"); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(Encrypt.class)).isNotNull(); + } + + private void assertDecryptFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedNodeString) { + + DetectionStore decryptStore = + getStoreOfValueType(ValueAction.class, store.getChildren()); + assertThat(decryptStore).isNotNull(); + assertThat(decryptStore.getDetectionValues()).hasSize(1); + assertThat(decryptStore.getDetectionValues().get(0).asString()).isEqualTo("DECRYPT"); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(Decrypt.class)).isNotNull(); + } + + /** + * Asserts mode+padding findings using the translated node tree. Direct-mode methods + * (EncryptCbc, TryDecryptEcb, etc.) place Mode and Padding in the same child detection store, + * so we validate via the final node string rather than per-store inspection. + */ + private void assertModePaddingFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedMode, + @Nonnull String expectedPadding, + @Nonnull String expectedNodeString) { + + // Verify that a Mode value with the expected string is detected somewhere in children + DetectionStore modeStore = + getStoreOfValueType(Mode.class, store.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues()) + .anySatisfy(v -> assertThat(v.asString()).isEqualTo(expectedMode)); + + // The translated node captures the combined result + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + } +} diff --git a/engine/src/main/java/com/ibm/engine/model/factory/ModeFactory.java b/engine/src/main/java/com/ibm/engine/model/factory/ModeFactory.java index 2517b09cf..f4d889969 100644 --- a/engine/src/main/java/com/ibm/engine/model/factory/ModeFactory.java +++ b/engine/src/main/java/com/ibm/engine/model/factory/ModeFactory.java @@ -24,11 +24,30 @@ import com.ibm.engine.model.Mode; import java.util.Optional; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ModeFactory implements IValueFactory { + @Nullable private final String constant; + + public ModeFactory() { + this.constant = null; + } + + /** + * Creates a factory that always emits {@code Mode(constant)} regardless of the resolved + * parameter value. Useful when the mode is encoded in the method name (e.g. {@code EncryptCbc}) + * rather than in a parameter. + */ + public ModeFactory(@Nonnull String constant) { + this.constant = constant; + } + @Override public Optional> apply(@Nonnull ResolvedValue objectTResolvedValue) { + if (constant != null) { + return Optional.of(new Mode<>(constant, objectTResolvedValue.tree())); + } if (objectTResolvedValue.value() instanceof String s) { return Optional.of(new Mode<>(s, objectTResolvedValue.tree())); } From 717f6abade36865762b3f4d99239984197ed45c4 Mon Sep 17 00:00:00 2001 From: Fynn Thierling Date: Wed, 19 Aug 2026 13:56:49 +0200 Subject: [PATCH 02/10] adds DES and DESSERVICEPROVIDER detection rules and tests Signed-off-by: Fynn Thierling --- .../rules/detection/dotnet/DotNetDES.java | 541 +++++++++++++++++- .../dotnet/DotNetDESComprehensiveTestFile.cs | 339 +++++++++++ .../dotnet/DotNetDESComprehensiveTest.java | 418 ++++++++++++++ 3 files changed, 1291 insertions(+), 7 deletions(-) create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetDESComprehensiveTestFile.cs create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDESComprehensiveTest.java diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDES.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDES.java index f4384f7f7..0001364a4 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDES.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDES.java @@ -19,23 +19,38 @@ */ package com.ibm.plugin.rules.detection.dotnet; +import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.Size; import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.BlockSizeFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.ModeFactory; +import com.ibm.engine.model.factory.PaddingFactory; 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.stream.Stream; import javax.annotation.Nonnull; /** - * Detection rules for DES usage in System.Security.Cryptography. + * Detection rules for the DES family in System.Security.Cryptography. * - *

Detects: + *

Classes covered: * *

    - *
  • {@code DES.Create()} — abstract factory (deprecated in .NET 5+) - *
  • {@code new DESCryptoServiceProvider()} — CAPI-backed (deprecated) + *
  • {@code DES} — abstract base ({@code DES.Create()}, {@code DES.Create(string)}) + *
  • {@code DESCryptoServiceProvider} — legacy CAPI implementation *
+ * + *

Architecture: all methods inherited from {@code SymmetricAlgorithm} (EncryptCbc, DecryptCbc, + * CreateEncryptor, property setters, etc.) are expressed as depending rules attached to + * each primary creation rule. The detection engine tracks the variable and fires these rules on + * every matching method call, regardless of the concrete DES subclass. Unlike {@code Aes}, {@code + * DES} has no CNG-backed subclass and no AEAD variant, so it has fewer primary creation rules than + * {@code DotNetAES}, but identical depending-rule coverage since both derive from {@code + * SymmetricAlgorithm}. */ @SuppressWarnings("java:S1192") public final class DotNetDES { @@ -44,6 +59,505 @@ private DotNetDES() { // nothing } + // ========================================================================= + // Property setter rules (synthetic set_X method invocations) + // ========================================================================= + + // des.Mode = CipherMode.CBC → synthetic set_Mode(CipherMode.CBC) + private static final IDetectionRule DES_SET_MODE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_Mode") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new ModeFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // des.KeySize = 64 → synthetic set_KeySize(64) + private static final IDetectionRule DES_SET_KEY_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_KeySize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // des.Padding = PaddingMode.PKCS7 → synthetic set_Padding(PaddingMode.PKCS7) + private static final IDetectionRule DES_SET_PADDING = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_Padding") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // des.FeedbackSize = 8 → synthetic set_FeedbackSize(8) + private static final IDetectionRule DES_SET_FEEDBACK_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_FeedbackSize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new BlockSizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> PROPERTY_SETTER_RULES = + List.of(DES_SET_MODE, DES_SET_KEY_SIZE, DES_SET_PADDING, DES_SET_FEEDBACK_SIZE); + + // ========================================================================= + // CreateEncryptor / CreateDecryptor rules + // ========================================================================= + + // des.CreateEncryptor() + private static final IDetectionRule DES_CREATE_ENCRYPTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateEncryptor") + .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // des.CreateEncryptor(byte[] key, byte[] iv) + private static final IDetectionRule DES_CREATE_ENCRYPTOR_WITH_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateEncryptor") + .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .withMethodParameter(MethodMatcher.ANY) // key bytes + .withMethodParameter(MethodMatcher.ANY) // iv bytes + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // des.CreateDecryptor() + private static final IDetectionRule DES_CREATE_DECRYPTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateDecryptor") + .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // des.CreateDecryptor(byte[] key, byte[] iv) + private static final IDetectionRule DES_CREATE_DECRYPTOR_WITH_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateDecryptor") + .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .withMethodParameter(MethodMatcher.ANY) // key bytes + .withMethodParameter(MethodMatcher.ANY) // iv bytes + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptCbc / DecryptCbc rules + // Mode is constant "CBC" (from method name); padding is detected from last param. + // Two overloads: 3-param and 4-param (with output buffer). + // ========================================================================= + + // EncryptCbc(plaintext, iv, padding) + private static final IDetectionRule DES_ENCRYPT_CBC_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCbc(plaintext, iv, destination, padding) [output-buffer overload] + private static final IDetectionRule DES_ENCRYPT_CBC_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCbc(ciphertext, iv, padding) + private static final IDetectionRule DES_DECRYPT_CBC_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCbc(ciphertext, iv, destination, padding) + private static final IDetectionRule DES_DECRYPT_CBC_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptEcb / DecryptEcb rules + // Mode is constant "ECB" (from method name); no IV parameter. + // Two overloads: 2-param and 3-param (with output buffer). + // ========================================================================= + + // EncryptEcb(plaintext, padding) + private static final IDetectionRule DES_ENCRYPT_ECB_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptEcb(plaintext, destination, padding) + private static final IDetectionRule DES_ENCRYPT_ECB_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptEcb(ciphertext, padding) + private static final IDetectionRule DES_DECRYPT_ECB_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptEcb(ciphertext, destination, padding) + private static final IDetectionRule DES_DECRYPT_ECB_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptCfb / DecryptCfb rules + // Mode is constant "CFB"; padding detected from 3rd param; feedbackSize ignored. + // Two overloads: 4-param and 5-param (with output buffer). + // ========================================================================= + + // EncryptCfb(plaintext, iv, padding, feedbackSize) + private static final IDetectionRule DES_ENCRYPT_CFB_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCfb(plaintext, iv, destination, padding, feedbackSize) + private static final IDetectionRule DES_ENCRYPT_CFB_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCfb(ciphertext, iv, padding, feedbackSize) + private static final IDetectionRule DES_DECRYPT_CFB_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCfb(ciphertext, iv, destination, padding, feedbackSize) + private static final IDetectionRule DES_DECRYPT_CFB_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // TryEncrypt* / TryDecrypt* rules + // Signatures: + // TryEncryptCbc(plaintext, iv, destination, out bytesWritten, padding) — 5 params + // TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, padding) — 5 params + // TryEncryptEcb(plaintext, destination, padding, out bytesWritten) — 4 params + // TryDecryptEcb(ciphertext, destination, padding, out bytesWritten) — 4 params + // TryEncryptCfb(plaintext, iv, destination, out bytesWritten, padding, fs) — 6 params + // TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, padding, fs) — 6 params + // ========================================================================= + + // TryEncryptCbc(plaintext, iv, destination, out bytesWritten, padding) + private static final IDetectionRule DES_TRY_ENCRYPT_CBC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, padding) + private static final IDetectionRule DES_TRY_DECRYPT_CBC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptEcb(plaintext, destination, padding, out bytesWritten) + private static final IDetectionRule DES_TRY_ENCRYPT_ECB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptEcb(ciphertext, destination, padding, out bytesWritten) + private static final IDetectionRule DES_TRY_DECRYPT_ECB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptCfb(plaintext, iv, destination, out bytesWritten, padding, feedbackSize) + private static final IDetectionRule DES_TRY_ENCRYPT_CFB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, padding, feedbackSize) + private static final IDetectionRule DES_TRY_DECRYPT_CFB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Key / IV generation rules + // ========================================================================= + + // des.GenerateKey() — generates a new random key (size determined by KeySize property) + private static final IDetectionRule DES_GENERATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("GenerateKey")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // des.GenerateIV() — generates a new random initialization vector + private static final IDetectionRule DES_GENERATE_IV = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GenerateIV") + .shouldBeDetectedAs(new ValueActionFactory<>("GenerateIV")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Aggregated depending-rule lists + // ========================================================================= + + /** + * All cipher operation rules that fire on a tracked DES-family variable. Includes + * CreateEncryptor/CreateDecryptor, direct mode-specific Encrypt/Decrypt methods, Try* variants, + * and key/IV generation. + */ + private static final List> CIPHER_OP_RULES = + List.of( + DES_CREATE_ENCRYPTOR, + DES_CREATE_ENCRYPTOR_WITH_KEY, + DES_CREATE_DECRYPTOR, + DES_CREATE_DECRYPTOR_WITH_KEY, + DES_ENCRYPT_CBC_3, + DES_ENCRYPT_CBC_4, + DES_DECRYPT_CBC_3, + DES_DECRYPT_CBC_4, + DES_ENCRYPT_ECB_2, + DES_ENCRYPT_ECB_3, + DES_DECRYPT_ECB_2, + DES_DECRYPT_ECB_3, + DES_ENCRYPT_CFB_4, + DES_ENCRYPT_CFB_5, + DES_DECRYPT_CFB_4, + DES_DECRYPT_CFB_5, + DES_TRY_ENCRYPT_CBC, + DES_TRY_DECRYPT_CBC, + DES_TRY_ENCRYPT_ECB, + DES_TRY_DECRYPT_ECB, + DES_TRY_ENCRYPT_CFB, + DES_TRY_DECRYPT_CFB, + DES_GENERATE_KEY, + DES_GENERATE_IV); + + /** Full set of depending rules for all DES-derived classes. */ + private static final List> DES_DEPENDING_RULES = + Stream.concat(PROPERTY_SETTER_RULES.stream(), CIPHER_OP_RULES.stream()).toList(); + + // ========================================================================= + // Primary creation rules + // ========================================================================= + + // DES.Create() — abstract factory, no parameters private static final IDetectionRule DES_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -53,8 +567,21 @@ private DotNetDES() { .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(DES_DEPENDING_RULES); + + // DES.Create("DES") — named factory (obsolete, still detectable) + private static final IDetectionRule DES_CREATE_NAMED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("DES") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("DES")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(DES_DEPENDING_RULES); + // new DESCryptoServiceProvider() — legacy CAPI implementation private static final IDetectionRule DES_CSP = new DetectionRuleBuilder() .createDetectionRule() @@ -64,10 +591,10 @@ private DotNetDES() { .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(DES_DEPENDING_RULES); @Nonnull public static List> rules() { - return List.of(DES_CREATE, DES_CSP); + return List.of(DES_CREATE, DES_CREATE_NAMED, DES_CSP); } } diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetDESComprehensiveTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetDESComprehensiveTestFile.cs new file mode 100644 index 000000000..4dc4ebdfa --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetDESComprehensiveTestFile.cs @@ -0,0 +1,339 @@ +/* + * Comprehensive test file for System.Security.Cryptography DES detection rules. + * + * Covers both DES-related classes and their complete API surface: + * - DES (abstract base) + * - DESCryptoServiceProvider (derived from DES) + * + * Unlike Aes, DES has no CNG-backed subclass and no AEAD variant, so there are + * fewer constructor scenarios in Section 1, and no AEAD sections. + * + * Architecture note: all methods inherited from SymmetricAlgorithm (EncryptCbc, + * CreateEncryptor, etc.) are covered once here. The detection engine tracks the + * variable and fires the same depending rules for every concrete DES subclass. + */ + +using System.Security.Cryptography; + +public class DotNetDESComprehensiveTest +{ + // ------------------------------------------------------------------------- + // Section 1: Factory methods / constructors + // ------------------------------------------------------------------------- + + public void TestDesCreate() + { + var des = DES.Create(); + } + + public void TestDesCreateNamed() + { + var des = DES.Create("DES"); + } + + public void TestDesCsp() + { + var des = new DESCryptoServiceProvider(); + } + + // ------------------------------------------------------------------------- + // Section 2: Property setters (via assignment → synthetic set_X invocations) + // ------------------------------------------------------------------------- + + public void TestPropertyModeCBC() + { + var des = DES.Create(); + des.Mode = CipherMode.CBC; + } + + public void TestPropertyModeECB() + { + var des = DES.Create(); + des.Mode = CipherMode.ECB; + } + + public void TestPropertyModeCFB() + { + var des = DES.Create(); + des.Mode = CipherMode.CFB; + } + + public void TestPropertyModeOFB() + { + var des = DES.Create(); + des.Mode = CipherMode.OFB; + } + + public void TestPropertyModeCTS() + { + var des = DES.Create(); + des.Mode = CipherMode.CTS; + } + + public void TestPropertyKeySize() + { + var des = DES.Create(); + des.KeySize = 64; + } + + public void TestPropertyPaddingPKCS7() + { + var des = DES.Create(); + des.Padding = PaddingMode.PKCS7; + } + + public void TestPropertyPaddingNone() + { + var des = DES.Create(); + des.Padding = PaddingMode.None; + } + + public void TestPropertyPaddingZeros() + { + var des = DES.Create(); + des.Padding = PaddingMode.Zeros; + } + + public void TestPropertyPaddingANSIX923() + { + var des = DES.Create(); + des.Padding = PaddingMode.ANSIX923; + } + + public void TestPropertyFeedbackSize() + { + var des = DES.Create(); + des.FeedbackSize = 8; + } + + public void TestPropertyIV() + { + var des = DES.Create(); + des.IV = new byte[8]; + } + + public void TestPropertyKey() + { + var des = DES.Create(); + des.Key = new byte[8]; + } + + // ------------------------------------------------------------------------- + // Section 3: CreateEncryptor / CreateDecryptor + // ------------------------------------------------------------------------- + + public void TestCreateEncryptorNoArgs() + { + var des = DES.Create(); + var encryptor = des.CreateEncryptor(); + } + + public void TestCreateEncryptorWithArgs() + { + var des = DES.Create(); + byte[] key = new byte[8]; + byte[] iv = new byte[8]; + var encryptor = des.CreateEncryptor(key, iv); + } + + public void TestCreateDecryptorNoArgs() + { + var des = DES.Create(); + var decryptor = des.CreateDecryptor(); + } + + public void TestCreateDecryptorWithArgs() + { + var des = DES.Create(); + byte[] key = new byte[8]; + byte[] iv = new byte[8]; + var decryptor = des.CreateDecryptor(key, iv); + } + + // ------------------------------------------------------------------------- + // Section 4: Direct mode-specific encrypt methods + // ------------------------------------------------------------------------- + + public void TestEncryptCbc() + { + var des = DES.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = des.EncryptCbc(plaintext, iv, PaddingMode.PKCS7); + } + + public void TestEncryptEcb() + { + var des = DES.Create(); + byte[] plaintext = new byte[16]; + byte[] ciphertext = des.EncryptEcb(plaintext, PaddingMode.None); + } + + public void TestEncryptCfb() + { + var des = DES.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = des.EncryptCfb(plaintext, iv, PaddingMode.None, 8); + } + + // ------------------------------------------------------------------------- + // Section 5: Direct mode-specific decrypt methods + // ------------------------------------------------------------------------- + + public void TestDecryptCbc() + { + var des = DES.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] plaintext = des.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); + } + + public void TestDecryptEcb() + { + var des = DES.Create(); + byte[] ciphertext = new byte[16]; + byte[] plaintext = des.DecryptEcb(ciphertext, PaddingMode.None); + } + + public void TestDecryptCfb() + { + var des = DES.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] plaintext = des.DecryptCfb(ciphertext, iv, PaddingMode.None, 8); + } + + // ------------------------------------------------------------------------- + // Section 6: Try* variants + // ------------------------------------------------------------------------- + + public void TestTryEncryptCbc() + { + var des = DES.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[24]; + int bytesWritten; + des.TryEncryptCbc(plaintext, iv, destination, out bytesWritten, PaddingMode.PKCS7); + } + + public void TestTryDecryptCbc() + { + var des = DES.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[16]; + int bytesWritten; + des.TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, PaddingMode.PKCS7); + } + + public void TestTryEncryptEcb() + { + var des = DES.Create(); + byte[] plaintext = new byte[16]; + byte[] destination = new byte[24]; + int bytesWritten; + des.TryEncryptEcb(plaintext, destination, PaddingMode.None, out bytesWritten); + } + + public void TestTryDecryptEcb() + { + var des = DES.Create(); + byte[] ciphertext = new byte[16]; + byte[] destination = new byte[16]; + int bytesWritten; + des.TryDecryptEcb(ciphertext, destination, PaddingMode.None, out bytesWritten); + } + + public void TestTryEncryptCfb() + { + var des = DES.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[24]; + int bytesWritten; + des.TryEncryptCfb(plaintext, iv, destination, out bytesWritten, PaddingMode.None, 8); + } + + public void TestTryDecryptCfb() + { + var des = DES.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[16]; + int bytesWritten; + des.TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, PaddingMode.None, 8); + } + + // ------------------------------------------------------------------------- + // Section 7: Key/IV generation + // ------------------------------------------------------------------------- + + public void TestGenerateKey() + { + var des = DES.Create(); + des.GenerateKey(); + } + + public void TestGenerateIV() + { + var des = DES.Create(); + des.GenerateIV(); + } + + // ------------------------------------------------------------------------- + // Section 8: Combined usage patterns (real-world scenarios) + // Demonstrates that depending rules fire correctly for both derived classes. + // ------------------------------------------------------------------------- + + public void TestDesCbcFullFlow() + { + var des = DES.Create(); + des.Mode = CipherMode.CBC; + des.Padding = PaddingMode.PKCS7; + var encryptor = des.CreateEncryptor(); + } + + public void TestDesCspEncryptCbc() + { + var des = new DESCryptoServiceProvider(); + des.Mode = CipherMode.CBC; + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = des.EncryptCbc(plaintext, iv, PaddingMode.PKCS7); + } + + public void TestDesCspDecryptCbc() + { + var des = new DESCryptoServiceProvider(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] plaintext = des.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); + } + + public void TestDesCfbFeedback() + { + var des = DES.Create(); + des.Mode = CipherMode.CFB; + des.FeedbackSize = 8; + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = des.EncryptCfb(plaintext, iv, PaddingMode.None, 8); + } + + public void TestDesCbcWithEncryptorOverload() + { + var des = DES.Create(); + byte[] key = new byte[8]; + byte[] iv = new byte[8]; + var encryptor = des.CreateEncryptor(key, iv); + } + + public void TestDesEcbEncrypt() + { + var des = new DESCryptoServiceProvider(); + byte[] plaintext = new byte[16]; + byte[] ciphertext = des.EncryptEcb(plaintext, PaddingMode.None); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDESComprehensiveTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDESComprehensiveTest.java new file mode 100644 index 000000000..23bfb5e05 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDESComprehensiveTest.java @@ -0,0 +1,418 @@ +/* + * 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.BlockSize; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.Mode; +import com.ibm.engine.model.Padding; +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.KeyLength; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.Generate; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all DES-related detection rules (DotNetDES.java). + * + *

Covers both DES-related classes and their complete API surface: + * + *

    + *
  • DES (abstract base) + *
  • DESCryptoServiceProvider (derived from DES) + *
+ * + *

Unlike Aes, DES has no CNG-backed subclass and no AEAD variant, so there is no equivalent to + * Section 8/9 of DotNetAESComprehensiveTest (AesGcm/AesCcm). + * + *

Finding mapping (one finding per test method in DotNetDESComprehensiveTestFile.cs): + * + *

+ * Section 1 – factory methods / constructors (findings 0–2):
+ *   0  TestDesCreate                 → DES-56
+ *   1  TestDesCreateNamed            → DES-56
+ *   2  TestDesCsp                    → DES-56
+ *
+ * Section 2 – property setters (findings 3–15):
+ *   3  TestPropertyModeCBC           → DES-56-CBC
+ *   4  TestPropertyModeECB           → DES-56-ECB
+ *   5  TestPropertyModeCFB           → DES-56-CFB
+ *   6  TestPropertyModeOFB           → DES-56-OFB
+ *   7  TestPropertyModeCTS           → DES-56-CTS
+ *   8  TestPropertyKeySize           → DES-64 (overrides default 56)
+ *   9  TestPropertyPaddingPKCS7      → DES-56 (padding never rendered in asString)
+ *   10 TestPropertyPaddingNone       → DES-56 (padding never rendered in asString)
+ *   11 TestPropertyPaddingZeros      → DES-56 (padding never rendered in asString)
+ *   12 TestPropertyPaddingANSIX923   → DES-56 (padding never rendered in asString)
+ *   13 TestPropertyFeedbackSize      → DES-56 (BlockSize never rendered in asString)
+ *   14 TestPropertyIV                → DES-56 (no IV rule)
+ *   15 TestPropertyKey               → DES-56 (no Key rule)
+ *
+ * Section 3 – CreateEncryptor/CreateDecryptor (findings 16–19):
+ *   16 TestCreateEncryptorNoArgs      → DES-56 + Encrypt
+ *   17 TestCreateEncryptorWithArgs    → DES-56 + Encrypt
+ *   18 TestCreateDecryptorNoArgs      → DES-56 + Decrypt
+ *   19 TestCreateDecryptorWithArgs    → DES-56 + Decrypt
+ *
+ * Section 4 – direct encrypt (findings 20–22):
+ *   20 TestEncryptCbc                → DES-56-CBC
+ *   21 TestEncryptEcb                → DES-56-ECB
+ *   22 TestEncryptCfb                → DES-56-CFB
+ *
+ * Section 5 – direct decrypt (findings 23–25):
+ *   23 TestDecryptCbc                → DES-56-CBC
+ *   24 TestDecryptEcb                → DES-56-ECB
+ *   25 TestDecryptCfb                → DES-56-CFB
+ *
+ * Section 6 – Try* variants (findings 26–31):
+ *   26 TestTryEncryptCbc             → DES-56-CBC
+ *   27 TestTryDecryptCbc             → DES-56-CBC
+ *   28 TestTryEncryptEcb             → DES-56-ECB
+ *   29 TestTryDecryptEcb             → DES-56-ECB
+ *   30 TestTryEncryptCfb             → DES-56-CFB
+ *   31 TestTryDecryptCfb             → DES-56-CFB
+ *
+ * Section 7 – key/IV generation (findings 32–33):
+ *   32 TestGenerateKey               → DES-56 + KeyGeneration
+ *   33 TestGenerateIV                → DES-56 + Generate
+ *
+ * Section 8 – combined usage patterns (findings 34–39):
+ *   34 TestDesCbcFullFlow            → DES-56-CBC + Encrypt
+ *   35 TestDesCspEncryptCbc          → DES-56-CBC
+ *   36 TestDesCspDecryptCbc          → DES-56-CBC
+ *   37 TestDesCfbFeedback            → DES-56-CFB
+ *   38 TestDesCbcWithEncryptorOverload → DES-56 + Encrypt
+ *   39 TestDesEcbEncrypt             → DES-56-ECB
+ * 
+ * + *

NOTE: unlike AES, {@code DES.asString()} uses {@code composeName(true, true, false)} — key + * length IS rendered (and {@code DES}'s no-arg constructor always seeds a default {@code + * KeyLength.ofDefault(56, ...)}), while padding is NEVER rendered. So every node string below + * carries a {@code -56} (or overridden key size) suffix, and Padding never contributes to the + * string even when a Padding value is detected as a child. Verified against actual test-run debug + * output (see {@code Algorithm.composeName} and {@code DES.java}/{@code AES.java} for the + * asymmetry) rather than guessed. + */ +class DotNetDESComprehensiveTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetDESComprehensiveTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + // Every top-level finding must be DES + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("DES"); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: simple constructors — only DES, no children fired + // ----------------------------------------------------------------- + case 0, 1, 2 -> { + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("DES-56"); + } + + // ----------------------------------------------------------------- + // Section 2a: property Mode setters + // ----------------------------------------------------------------- + case 3 -> assertModeFindings(detectionStore, nodes, "CBC", "DES-56-CBC"); + case 4 -> assertModeFindings(detectionStore, nodes, "ECB", "DES-56-ECB"); + case 5 -> assertModeFindings(detectionStore, nodes, "CFB", "DES-56-CFB"); + case 6 -> assertModeFindings(detectionStore, nodes, "OFB", "DES-56-OFB"); + case 7 -> assertModeFindings(detectionStore, nodes, "CTS", "DES-56-CTS"); + + // ----------------------------------------------------------------- + // Section 2b: property KeySize setter — overrides the default 56 + // ----------------------------------------------------------------- + case 8 -> assertKeySizeFindings(detectionStore, nodes, "64", "DES-64"); + + // ----------------------------------------------------------------- + // Section 2c: property Padding setters — Padding is tracked as a + // child node but DES.asString() uses composeName(true, true, false), + // so padding never contributes to the rendered string. + // ----------------------------------------------------------------- + case 9 -> assertPaddingFindings(detectionStore, nodes, "PKCS7", "DES-56"); + case 10 -> assertPaddingFindings(detectionStore, nodes, "None", "DES-56"); + case 11 -> assertPaddingFindings(detectionStore, nodes, "Zeros", "DES-56"); + case 12 -> assertPaddingFindings(detectionStore, nodes, "ANSIX923", "DES-56"); + + // ----------------------------------------------------------------- + // Section 2d: FeedbackSize setter — BlockSize(8) detected but never + // contributes to asString() (composeName has no BlockSize branch) + // ----------------------------------------------------------------- + case 13 -> { + DetectionStore fbStore = + getStoreOfValueType(BlockSize.class, detectionStore.getChildren()); + assertThat(fbStore).isNotNull(); + assertThat(fbStore.getDetectionValues()).hasSize(1); + assertThat(fbStore.getDetectionValues().get(0).asString()).isEqualTo("8"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("DES-56"); + } + + // ----------------------------------------------------------------- + // Section 2e: IV and Key setters — no detection rules for these + // ----------------------------------------------------------------- + case 14, 15 -> { + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("DES-56"); + } + + // ----------------------------------------------------------------- + // Section 3: CreateEncryptor / CreateDecryptor + // ----------------------------------------------------------------- + case 16, 17 -> assertEncryptFindings(detectionStore, nodes, "DES-56"); + case 18, 19 -> assertDecryptFindings(detectionStore, nodes, "DES-56"); + + // ----------------------------------------------------------------- + // Section 4: direct mode-specific encrypt + // ----------------------------------------------------------------- + case 20 -> + assertModePaddingFindings(detectionStore, nodes, "CBC", "PKCS7", "DES-56-CBC"); + case 21 -> + assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "DES-56-ECB"); + case 22 -> + assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "DES-56-CFB"); + + // ----------------------------------------------------------------- + // Section 5: direct mode-specific decrypt + // ----------------------------------------------------------------- + case 23 -> + assertModePaddingFindings(detectionStore, nodes, "CBC", "PKCS7", "DES-56-CBC"); + case 24 -> + assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "DES-56-ECB"); + case 25 -> + assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "DES-56-CFB"); + + // ----------------------------------------------------------------- + // Section 6: Try* variants + // ----------------------------------------------------------------- + case 26, 27 -> + assertModePaddingFindings(detectionStore, nodes, "CBC", "PKCS7", "DES-56-CBC"); + case 28, 29 -> + assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "DES-56-ECB"); + case 30, 31 -> + assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "DES-56-CFB"); + + // ----------------------------------------------------------------- + // Section 7: GenerateKey / GenerateIV + // ----------------------------------------------------------------- + case 32 -> { + // des.GenerateKey() → KeyGeneration functionality node + DetectionStore + genKeyStore = + getStoreOfValueType( + ValueAction.class, detectionStore.getChildren()); + assertThat(genKeyStore).isNotNull(); + assertThat(genKeyStore.getDetectionValues().get(0).asString()) + .isEqualTo("GenerateKey"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).getChildren().get(KeyGeneration.class)).isNotNull(); + } + case 33 -> { + // des.GenerateIV() → Generate functionality node + DetectionStore + genIvStore = + getStoreOfValueType( + ValueAction.class, detectionStore.getChildren()); + assertThat(genIvStore).isNotNull(); + assertThat(genIvStore.getDetectionValues().get(0).asString()) + .isEqualTo("GenerateIV"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).getChildren().get(Generate.class)).isNotNull(); + } + + // ----------------------------------------------------------------- + // Section 8: combined usage patterns + // ----------------------------------------------------------------- + case 34 -> { + // TestDesCbcFullFlow: Mode=CBC, Padding=PKCS7 (unrendered), CreateEncryptor + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(BlockCipher.class); + assertThat(node.asString()).isEqualTo("DES-56-CBC"); + assertThat(node.getChildren().get(com.ibm.mapper.model.Mode.class)).isNotNull(); + assertThat(node.getChildren().get(Encrypt.class)).isNotNull(); + } + case 35 -> + assertModePaddingFindings(detectionStore, nodes, "CBC", "PKCS7", "DES-56-CBC"); + case 36 -> + assertModePaddingFindings(detectionStore, nodes, "CBC", "PKCS7", "DES-56-CBC"); + case 37 -> + assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "DES-56-CFB"); + case 38 -> assertEncryptFindings(detectionStore, nodes, "DES-56"); + case 39 -> + assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "DES-56-ECB"); + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertModeFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedMode, + @Nonnull String expectedNodeString) { + + DetectionStore modeStore = + getStoreOfValueType(Mode.class, store.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues()).hasSize(1); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo(expectedMode); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(com.ibm.mapper.model.Mode.class)).isNotNull(); + } + + private void assertKeySizeFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedKeySize, + @Nonnull String expectedNodeString) { + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, store.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues()).hasSize(1); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo(expectedKeySize); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(KeyLength.class)).isNotNull(); + assertThat(nodes.get(0).getChildren().get(KeyLength.class).asString()) + .isEqualTo(expectedKeySize); + } + + private void assertPaddingFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedPadding, + @Nonnull String expectedNodeString) { + + DetectionStore paddingStore = + getStoreOfValueType(Padding.class, store.getChildren()); + assertThat(paddingStore).isNotNull(); + assertThat(paddingStore.getDetectionValues()).hasSize(1); + assertThat(paddingStore.getDetectionValues().get(0).asString()).isEqualTo(expectedPadding); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + } + + private void assertEncryptFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedNodeString) { + + DetectionStore encryptStore = + getStoreOfValueType(ValueAction.class, store.getChildren()); + assertThat(encryptStore).isNotNull(); + assertThat(encryptStore.getDetectionValues()).hasSize(1); + assertThat(encryptStore.getDetectionValues().get(0).asString()).isEqualTo("ENCRYPT"); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(Encrypt.class)).isNotNull(); + } + + private void assertDecryptFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedNodeString) { + + DetectionStore decryptStore = + getStoreOfValueType(ValueAction.class, store.getChildren()); + assertThat(decryptStore).isNotNull(); + assertThat(decryptStore.getDetectionValues()).hasSize(1); + assertThat(decryptStore.getDetectionValues().get(0).asString()).isEqualTo("DECRYPT"); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(Decrypt.class)).isNotNull(); + } + + /** + * Asserts mode+padding findings using the translated node tree. Direct-mode methods + * (EncryptCbc, TryDecryptEcb, etc.) place Mode and Padding in the same child detection store, + * so we validate via the final node string rather than per-store inspection. + */ + private void assertModePaddingFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedMode, + @Nonnull String expectedPadding, + @Nonnull String expectedNodeString) { + + DetectionStore modeStore = + getStoreOfValueType(Mode.class, store.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues()) + .anySatisfy(v -> assertThat(v.asString()).isEqualTo(expectedMode)); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + } +} From 094d6d2ea71920d91b189e61f41ade31fe51dd1b Mon Sep 17 00:00:00 2001 From: Fynn Thierling Date: Wed, 19 Aug 2026 14:20:45 +0200 Subject: [PATCH 03/10] added DSA related detection rules and tests Signed-off-by: Fynn Thierling --- .../rules/detection/dotnet/DotNetDSA.java | 173 ++++++++++++++++- .../translator/CSharpTranslator.java | 7 +- .../CSharpSignatureContextTranslator.java | 54 ++++++ .../dotnet/DotNetDSAComprehensiveTestFile.cs | 180 ++++++++++++++++++ .../dotnet/DotNetDSAComprehensiveTest.java | 178 +++++++++++++++++ 5 files changed, 582 insertions(+), 10 deletions(-) create mode 100644 csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpSignatureContextTranslator.java create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetDSAComprehensiveTestFile.cs create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSAComprehensiveTest.java diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSA.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSA.java index 30fdd70fc..44c0bb6a1 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSA.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSA.java @@ -19,8 +19,14 @@ */ package com.ibm.plugin.rules.detection.dotnet; +import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.Size; import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; import com.ibm.engine.model.factory.ValueActionFactory; import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.builder.DetectionRuleBuilder; @@ -29,14 +35,23 @@ import javax.annotation.Nonnull; /** - * Detection rules for DSA usage in System.Security.Cryptography. + * Detection rules for the DSA family in System.Security.Cryptography. * - *

Detects: + *

Classes covered: * *

    - *
  • {@code DSA.Create()} — abstract factory - *
  • {@code new DSACryptoServiceProvider()} — CAPI-backed implementation + *
  • {@code DSA} — abstract base ({@code DSA.Create()}, {@code DSA.Create(DSAParameters)}, + * {@code DSA.Create(int)}, {@code DSA.Create(string)}) + *
  • {@code DSACng} — CNG-backed implementation (ephemeral and persisted-key constructors) + *
  • {@code DSACryptoServiceProvider} — legacy CAPI implementation + *
  • {@code DSAOpenSsl} — OpenSSL-backed implementation *
+ * + *

Architecture: all methods inherited from {@code AsymmetricAlgorithm} / {@code DSA} (KeySize + * property, CreateSignature, VerifySignature, SignData, VerifyData, Try* variants, etc.) are + * expressed as depending rules attached to each primary creation rule. The detection + * engine tracks the variable and fires these rules on every matching method call, regardless of the + * concrete DSA subclass. */ @SuppressWarnings("java:S1192") public final class DotNetDSA { @@ -45,30 +60,170 @@ private DotNetDSA() { // nothing } + // ========================================================================= + // Property setter rules (synthetic set_X method invocations) + // ========================================================================= + + // dsa.KeySize = 2048 → synthetic set_KeySize(2048) + private static final IDetectionRule DSA_SET_KEY_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_KeySize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new KeyContext(Map.of("kind", "DSA"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Signing / verification operation rules + // Each covers every overload of the given method name (arities vary only by + // hash-algorithm / signature-format / offset-length parameters, which are not + // individually tracked), mirroring the JCA Signature.sign()/verify() rules. + // ========================================================================= + + // dsa.CreateSignature(hash) / dsa.CreateSignature(hash, format) + private static final IDetectionRule DSA_CREATE_SIGNATURE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateSignature") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // dsa.TryCreateSignature(hash, destination, ...) + private static final IDetectionRule DSA_TRY_CREATE_SIGNATURE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryCreateSignature") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // dsa.SignData(data, hashAlgorithm[, format]) + private static final IDetectionRule DSA_SIGN_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // dsa.TrySignData(data, destination, hashAlgorithm, ...) + private static final IDetectionRule DSA_TRY_SIGN_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TrySignData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // dsa.VerifySignature(hash, signature[, format]) + private static final IDetectionRule DSA_VERIFY_SIGNATURE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifySignature") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // dsa.VerifyData(data, signature, hashAlgorithm[, format]) + private static final IDetectionRule DSA_VERIFY_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Aggregated depending-rule list + // ========================================================================= + + /** Full set of depending rules for all DSA-derived classes. */ + private static final List> DSA_DEPENDING_RULES = + List.of( + DSA_SET_KEY_SIZE, + DSA_CREATE_SIGNATURE, + DSA_TRY_CREATE_SIGNATURE, + DSA_SIGN_DATA, + DSA_TRY_SIGN_DATA, + DSA_VERIFY_SIGNATURE, + DSA_VERIFY_DATA); + + // ========================================================================= + // Primary creation rules + // ========================================================================= + + // DSA.Create() / DSA.Create(DSAParameters) / DSA.Create(int) / DSA.Create(string) private static final IDetectionRule DSA_CREATE = new DetectionRuleBuilder() .createDetectionRule() .forObjectTypes("DSA") .forMethods("Create") .shouldBeDetectedAs(new ValueActionFactory<>("DSA")) - .withoutParameters() + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "DSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(DSA_DEPENDING_RULES); + + // new DSACng() / new DSACng(CngKey) / new DSACng(int) — CNG-backed implementation + private static final IDetectionRule DSA_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("DSACng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("DSA")) + .withAnyParameters() .buildForContext(new KeyContext(Map.of("kind", "DSA"))) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(DSA_DEPENDING_RULES); + // new DSACryptoServiceProvider() / (CspParameters) / (int) / (int, CspParameters) private static final IDetectionRule DSA_CSP = new DetectionRuleBuilder() .createDetectionRule() .forObjectTypes("DSACryptoServiceProvider") .forMethods("") .shouldBeDetectedAs(new ValueActionFactory<>("DSA")) - .withoutParameters() + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "DSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(DSA_DEPENDING_RULES); + + // new DSAOpenSsl() / (DSAParameters) / (int) / (IntPtr) / (SafeEvpPKeyHandle) + private static final IDetectionRule DSA_OPENSSL = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("DSAOpenSsl") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("DSA")) + .withAnyParameters() .buildForContext(new KeyContext(Map.of("kind", "DSA"))) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(DSA_DEPENDING_RULES); @Nonnull public static List> rules() { - return List.of(DSA_CREATE, DSA_CSP); + return List.of(DSA_CREATE, DSA_CNG, DSA_CSP, DSA_OPENSSL); } } diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java index 793796b3f..59839d6e6 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java @@ -42,6 +42,7 @@ import com.ibm.plugin.translation.translator.contexts.CSharpDigestContextTranslator; import com.ibm.plugin.translation.translator.contexts.CSharpKeyContextTranslator; import com.ibm.plugin.translation.translator.contexts.CSharpMacContextTranslator; +import com.ibm.plugin.translation.translator.contexts.CSharpSignatureContextTranslator; import java.util.List; import java.util.Optional; import javax.annotation.Nonnull; @@ -88,8 +89,12 @@ public Optional translate( .translate(bundleIdentifier, value, detectionValueContext, detectionLocation); } + if (detectionValueContext.is(SignatureContext.class)) { + return new CSharpSignatureContextTranslator() + .translate(bundleIdentifier, value, detectionValueContext, detectionLocation); + } + if (detectionValueContext.is(PRNGContext.class) - || detectionValueContext.is(SignatureContext.class) || detectionValueContext.is(ProtocolContext.class)) { return Optional.empty(); } diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpSignatureContextTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpSignatureContextTranslator.java new file mode 100644 index 000000000..5b3f77f91 --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpSignatureContextTranslator.java @@ -0,0 +1,54 @@ +/* + * 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.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.context.IDetectionContext; +import com.ibm.engine.rule.IBundle; +import com.ibm.mapper.IContextTranslation; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; +import javax.annotation.Nonnull; + +/** Translates {@link com.ibm.engine.model.context.SignatureContext} detections for .NET APIs. */ +public final class CSharpSignatureContextTranslator implements IContextTranslation { + + @Override + public @Nonnull Optional translate( + @Nonnull IBundle bundleIdentifier, + @Nonnull IValue value, + @Nonnull IDetectionContext detectionContext, + @Nonnull DetectionLocation detectionLocation) { + + if (value instanceof SignatureAction signatureAction) { + return switch (signatureAction.getAction()) { + case SIGN -> Optional.of(new Sign(detectionLocation)); + case VERIFY -> Optional.of(new Verify(detectionLocation)); + }; + } + + return Optional.empty(); + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetDSAComprehensiveTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetDSAComprehensiveTestFile.cs new file mode 100644 index 000000000..6a7329371 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetDSAComprehensiveTestFile.cs @@ -0,0 +1,180 @@ +/* + * Comprehensive test file for System.Security.Cryptography DSA detection rules. + * + * Covers all four DSA-related classes and their complete operational API surface: + * - DSA (abstract base) + * - DSACng, DSACryptoServiceProvider, DSAOpenSsl (derived from DSA) + * + * Architecture note: all methods inherited from DSA / AsymmetricAlgorithm (KeySize, + * CreateSignature, VerifySignature, SignData, VerifyData, Try* variants, etc.) are + * covered once here. The detection engine tracks the variable and fires the same + * depending rules for every concrete DSA subclass. + */ + +using System.Security.Cryptography; + +public class DotNetDSAComprehensiveTest +{ + // ------------------------------------------------------------------------- + // Section 1: Factory methods / constructors + // ------------------------------------------------------------------------- + + public void TestDsaCreate() + { + var dsa = DSA.Create(); + } + + public void TestDsaCreateWithKeySize() + { + var dsa = DSA.Create(2048); + } + + public void TestDsaCng() + { + var dsa = new DSACng(); + } + + public void TestDsaCngWithKeySize() + { + var dsa = new DSACng(2048); + } + + public void TestDsaCsp() + { + var dsa = new DSACryptoServiceProvider(); + } + + public void TestDsaOpenSsl() + { + var dsa = new DSAOpenSsl(); + } + + // ------------------------------------------------------------------------- + // Section 2: Property setters (via assignment → synthetic set_X invocations) + // ------------------------------------------------------------------------- + + public void TestPropertyKeySize1024() + { + var dsa = DSA.Create(); + dsa.KeySize = 1024; + } + + public void TestPropertyKeySize2048() + { + var dsa = DSA.Create(); + dsa.KeySize = 2048; + } + + public void TestPropertyKeySize3072() + { + var dsa = DSA.Create(); + dsa.KeySize = 3072; + } + + // ------------------------------------------------------------------------- + // Section 3: CreateSignature / TryCreateSignature + // ------------------------------------------------------------------------- + + public void TestCreateSignature() + { + var dsa = DSA.Create(); + byte[] hash = new byte[20]; + byte[] signature = dsa.CreateSignature(hash); + } + + public void TestCreateSignatureWithFormat() + { + var dsa = DSA.Create(); + byte[] hash = new byte[20]; + byte[] signature = dsa.CreateSignature(hash, DSASignatureFormat.IeeeP1363FixedFieldConcatenation); + } + + public void TestTryCreateSignature() + { + var dsa = DSA.Create(); + byte[] hash = new byte[20]; + byte[] destination = new byte[64]; + int bytesWritten; + dsa.TryCreateSignature(hash, destination, out bytesWritten); + } + + // ------------------------------------------------------------------------- + // Section 4: VerifySignature + // ------------------------------------------------------------------------- + + public void TestVerifySignature() + { + var dsa = DSA.Create(); + byte[] hash = new byte[20]; + byte[] signature = new byte[64]; + bool valid = dsa.VerifySignature(hash, signature); + } + + public void TestVerifySignatureWithFormat() + { + var dsa = DSA.Create(); + byte[] hash = new byte[20]; + byte[] signature = new byte[64]; + bool valid = dsa.VerifySignature(hash, signature, DSASignatureFormat.IeeeP1363FixedFieldConcatenation); + } + + // ------------------------------------------------------------------------- + // Section 5: SignData / TrySignData + // ------------------------------------------------------------------------- + + public void TestSignData() + { + var dsa = DSA.Create(); + byte[] data = new byte[64]; + byte[] signature = dsa.SignData(data, HashAlgorithmName.SHA256); + } + + public void TestTrySignData() + { + var dsa = DSA.Create(); + byte[] data = new byte[64]; + byte[] destination = new byte[64]; + int bytesWritten; + dsa.TrySignData(data, destination, HashAlgorithmName.SHA256, out bytesWritten); + } + + // ------------------------------------------------------------------------- + // Section 6: VerifyData + // ------------------------------------------------------------------------- + + public void TestVerifyData() + { + var dsa = DSA.Create(); + byte[] data = new byte[64]; + byte[] signature = new byte[64]; + bool valid = dsa.VerifyData(data, signature, HashAlgorithmName.SHA256); + } + + // ------------------------------------------------------------------------- + // Section 7: Combined usage patterns (real-world scenarios) + // Demonstrates that depending rules fire correctly for ALL derived classes. + // ------------------------------------------------------------------------- + + public void TestDsaCngFullFlow() + { + var dsa = new DSACng(); + dsa.KeySize = 2048; + byte[] data = new byte[64]; + byte[] signature = dsa.SignData(data, HashAlgorithmName.SHA256); + } + + public void TestDsaCspVerifyFlow() + { + var dsa = new DSACryptoServiceProvider(); + byte[] data = new byte[64]; + byte[] signature = new byte[64]; + bool valid = dsa.VerifyData(data, signature, HashAlgorithmName.SHA1); + } + + public void TestDsaOpenSslSignFlow() + { + var dsa = new DSAOpenSsl(); + byte[] hash = new byte[20]; + byte[] signature = dsa.CreateSignature(hash); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSAComprehensiveTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSAComprehensiveTest.java new file mode 100644 index 000000000..1dcc4e588 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSAComprehensiveTest.java @@ -0,0 +1,178 @@ +/* + * 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.context.KeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all DSA-related detection rules (DotNetDSA.java). + * + *

Covers all four DSA-related classes and their complete operational API surface: + * + *

    + *
  • DSA (abstract base) + *
  • DSACng, DSACryptoServiceProvider, DSAOpenSsl (derived from DSA) + *
+ * + *

Finding mapping (one finding per test method in DotNetDSAComprehensiveTestFile.cs): + * + *

+ * Section 1 – factory methods / constructors (findings 0–5):
+ *   0 TestDsaCreate              → DSA
+ *   1 TestDsaCreateWithKeySize   → DSA
+ *   2 TestDsaCng                 → DSA
+ *   3 TestDsaCngWithKeySize      → DSA
+ *   4 TestDsaCsp                 → DSA
+ *   5 TestDsaOpenSsl             → DSA
+ *
+ * Section 2 – property KeySize setters (findings 6–8):
+ *   6 TestPropertyKeySize1024    → DSA-1024
+ *   7 TestPropertyKeySize2048    → DSA-2048
+ *   8 TestPropertyKeySize3072    → DSA-3072
+ *
+ * Section 3 – CreateSignature / TryCreateSignature (findings 9–11):
+ *   9  TestCreateSignature            → DSA + Sign
+ *   10 TestCreateSignatureWithFormat  → DSA + Sign
+ *   11 TestTryCreateSignature         → DSA + Sign
+ *
+ * Section 4 – VerifySignature (findings 12–13):
+ *   12 TestVerifySignature            → DSA + Verify
+ *   13 TestVerifySignatureWithFormat  → DSA + Verify
+ *
+ * Section 5 – SignData / TrySignData (findings 14–15):
+ *   14 TestSignData    → DSA + Sign
+ *   15 TestTrySignData → DSA + Sign
+ *
+ * Section 6 – VerifyData (finding 16):
+ *   16 TestVerifyData → DSA + Verify
+ *
+ * Section 7 – combined usage patterns (findings 17–19):
+ *   17 TestDsaCngFullFlow      → DSA-2048 + Sign
+ *   18 TestDsaCspVerifyFlow    → DSA + Verify
+ *   19 TestDsaOpenSslSignFlow  → DSA + Sign
+ * 
+ */ +class DotNetDSAComprehensiveTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetDSAComprehensiveTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + // Every top-level finding must be DSA + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(Signature.class); + assertThat(node.getChildren().get(Oid.class)).isNotNull(); + assertThat(node.getChildren().get(Oid.class).asString()).isEqualTo("1.2.840.10040.4.1"); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: simple constructors — only DSA, no children fired + // ----------------------------------------------------------------- + case 0, 1, 2, 3, 4, 5 -> assertThat(node.asString()).isEqualTo("DSA"); + + // ----------------------------------------------------------------- + // Section 2: property KeySize setters + // ----------------------------------------------------------------- + case 6 -> assertKeySize(node, "1024"); + case 7 -> assertKeySize(node, "2048"); + case 8 -> assertKeySize(node, "3072"); + + // ----------------------------------------------------------------- + // Section 3: CreateSignature / TryCreateSignature + // ----------------------------------------------------------------- + case 9, 10, 11 -> assertSign(node); + + // ----------------------------------------------------------------- + // Section 4: VerifySignature + // ----------------------------------------------------------------- + case 12, 13 -> assertVerify(node); + + // ----------------------------------------------------------------- + // Section 5: SignData / TrySignData + // ----------------------------------------------------------------- + case 14, 15 -> assertSign(node); + + // ----------------------------------------------------------------- + // Section 6: VerifyData + // ----------------------------------------------------------------- + case 16 -> assertVerify(node); + + // ----------------------------------------------------------------- + // Section 7: combined usage patterns + // ----------------------------------------------------------------- + case 17 -> { + assertThat(node.asString()).isEqualTo("DSA-2048"); + assertThat(node.getChildren().get(KeyLength.class)).isNotNull(); + assertThat(node.getChildren().get(KeyLength.class).asString()).isEqualTo("2048"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + case 18 -> assertVerify(node); + case 19 -> assertSign(node); + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + private void assertKeySize(@Nonnull INode node, @Nonnull String expectedKeySize) { + assertThat(node.asString()).isEqualTo("DSA-" + expectedKeySize); + assertThat(node.getChildren().get(KeyLength.class)).isNotNull(); + assertThat(node.getChildren().get(KeyLength.class).asString()).isEqualTo(expectedKeySize); + } + + private void assertSign(@Nonnull INode node) { + assertThat(node.asString()).isEqualTo("DSA"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + + private void assertVerify(@Nonnull INode node) { + assertThat(node.asString()).isEqualTo("DSA"); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); + } +} From d4fbf000a4fab7a1dcedf43b638cd1c399b062dc Mon Sep 17 00:00:00 2001 From: Fynn Thierling Date: Wed, 19 Aug 2026 15:33:22 +0200 Subject: [PATCH 04/10] refactors encrypt/decrypt Signed-off-by: Fynn Thierling --- .../rules/detection/dotnet/DotNetAES.java | 18 ++++++++++-------- .../rules/detection/dotnet/DotNetDES.java | 10 ++++++---- .../CSharpCipherContextTranslator.java | 9 +++++++-- .../dotnet/DotNetAESComprehensiveTest.java | 5 +++-- .../dotnet/DotNetDESComprehensiveTest.java | 5 +++-- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java index 4a9f13df8..337743adc 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java @@ -21,9 +21,11 @@ import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.Size; import com.ibm.engine.model.context.CipherContext; import com.ibm.engine.model.factory.BlockSizeFactory; +import com.ibm.engine.model.factory.CipherActionFactory; import com.ibm.engine.model.factory.KeySizeFactory; import com.ibm.engine.model.factory.ModeFactory; import com.ibm.engine.model.factory.PaddingFactory; @@ -125,7 +127,7 @@ private DotNetAES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("CreateEncryptor") - .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") @@ -137,7 +139,7 @@ private DotNetAES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("CreateEncryptor") - .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) .withMethodParameter(MethodMatcher.ANY) // key bytes .withMethodParameter(MethodMatcher.ANY) // iv bytes .buildForContext(new CipherContext()) @@ -150,7 +152,7 @@ private DotNetAES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("CreateDecryptor") - .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") @@ -162,7 +164,7 @@ private DotNetAES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("CreateDecryptor") - .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) .withMethodParameter(MethodMatcher.ANY) // key bytes .withMethodParameter(MethodMatcher.ANY) // iv bytes .buildForContext(new CipherContext()) @@ -564,7 +566,7 @@ private DotNetAES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("Encrypt") - .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) .withAnyParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") @@ -576,7 +578,7 @@ private DotNetAES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("Decrypt") - .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) .withAnyParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") @@ -591,7 +593,7 @@ private DotNetAES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("Encrypt") - .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) .withAnyParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") @@ -603,7 +605,7 @@ private DotNetAES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("Decrypt") - .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) .withAnyParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDES.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDES.java index 0001364a4..2c32f1f69 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDES.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDES.java @@ -21,9 +21,11 @@ import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.Size; import com.ibm.engine.model.context.CipherContext; import com.ibm.engine.model.factory.BlockSizeFactory; +import com.ibm.engine.model.factory.CipherActionFactory; import com.ibm.engine.model.factory.KeySizeFactory; import com.ibm.engine.model.factory.ModeFactory; import com.ibm.engine.model.factory.PaddingFactory; @@ -124,7 +126,7 @@ private DotNetDES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("CreateEncryptor") - .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") @@ -136,7 +138,7 @@ private DotNetDES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("CreateEncryptor") - .shouldBeDetectedAs(new ValueActionFactory<>("ENCRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) .withMethodParameter(MethodMatcher.ANY) // key bytes .withMethodParameter(MethodMatcher.ANY) // iv bytes .buildForContext(new CipherContext()) @@ -149,7 +151,7 @@ private DotNetDES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("CreateDecryptor") - .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") @@ -161,7 +163,7 @@ private DotNetDES() { .createDetectionRule() .forObjectTypes(MethodMatcher.ANY) .forMethods("CreateDecryptor") - .shouldBeDetectedAs(new ValueActionFactory<>("DECRYPT")) + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) .withMethodParameter(MethodMatcher.ANY) // key bytes .withMethodParameter(MethodMatcher.ANY) // iv bytes .buildForContext(new CipherContext()) diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java index af4d98563..33da6d5cd 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java @@ -20,6 +20,7 @@ package com.ibm.plugin.translation.translator.contexts; import com.ibm.engine.model.BlockSize; +import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.IValue; import com.ibm.engine.model.KeySize; import com.ibm.engine.model.Mode; @@ -72,8 +73,6 @@ public final class CSharpCipherContextTranslator Optional.of(new DESede(detectionLocation)); case "RSA" -> Optional.of(new RSA(detectionLocation)); case "RC2" -> Optional.of(new RC2(detectionLocation)); - case "ENCRYPT" -> Optional.of(new Encrypt(detectionLocation)); - case "DECRYPT" -> Optional.of(new Decrypt(detectionLocation)); case "GENERATEKEY" -> Optional.of(new KeyGeneration(detectionLocation)); case "GENERATEIV" -> Optional.of(new Generate(detectionLocation)); default -> Optional.empty(); @@ -84,6 +83,12 @@ public final class CSharpCipherContextTranslator // Try operation mode JcaCipherOperationModeMapper modeMapper = new JcaCipherOperationModeMapper(); return modeMapper.parse(valueStr, detectionLocation).map(mode -> mode); + } else if (value instanceof CipherAction cipherAction) { + return switch (cipherAction.getAction()) { + case ENCRYPT -> Optional.of(new Encrypt(detectionLocation)); + case DECRYPT -> Optional.of(new Decrypt(detectionLocation)); + default -> Optional.empty(); + }; } else if (value instanceof BlockSize blockSize) { return Optional.of( new com.ibm.mapper.model.BlockSize(blockSize.getValue(), detectionLocation)); diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java index f09691b26..9f68e2fe0 100644 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java @@ -27,6 +27,7 @@ import com.ibm.engine.language.csharp.CSharpSymbol; import com.ibm.engine.language.csharp.tree.CSharpTree; import com.ibm.engine.model.BlockSize; +import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.IValue; import com.ibm.engine.model.KeySize; import com.ibm.engine.model.Mode; @@ -393,7 +394,7 @@ private void assertEncryptFindings( @Nonnull String expectedNodeString) { DetectionStore encryptStore = - getStoreOfValueType(ValueAction.class, store.getChildren()); + getStoreOfValueType(CipherAction.class, store.getChildren()); assertThat(encryptStore).isNotNull(); assertThat(encryptStore.getDetectionValues()).hasSize(1); assertThat(encryptStore.getDetectionValues().get(0).asString()).isEqualTo("ENCRYPT"); @@ -410,7 +411,7 @@ private void assertDecryptFindings( @Nonnull String expectedNodeString) { DetectionStore decryptStore = - getStoreOfValueType(ValueAction.class, store.getChildren()); + getStoreOfValueType(CipherAction.class, store.getChildren()); assertThat(decryptStore).isNotNull(); assertThat(decryptStore.getDetectionValues()).hasSize(1); assertThat(decryptStore.getDetectionValues().get(0).asString()).isEqualTo("DECRYPT"); diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDESComprehensiveTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDESComprehensiveTest.java index 23bfb5e05..738961952 100644 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDESComprehensiveTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDESComprehensiveTest.java @@ -27,6 +27,7 @@ import com.ibm.engine.language.csharp.CSharpSymbol; import com.ibm.engine.language.csharp.tree.CSharpTree; import com.ibm.engine.model.BlockSize; +import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.IValue; import com.ibm.engine.model.KeySize; import com.ibm.engine.model.Mode; @@ -365,7 +366,7 @@ private void assertEncryptFindings( @Nonnull String expectedNodeString) { DetectionStore encryptStore = - getStoreOfValueType(ValueAction.class, store.getChildren()); + getStoreOfValueType(CipherAction.class, store.getChildren()); assertThat(encryptStore).isNotNull(); assertThat(encryptStore.getDetectionValues()).hasSize(1); assertThat(encryptStore.getDetectionValues().get(0).asString()).isEqualTo("ENCRYPT"); @@ -382,7 +383,7 @@ private void assertDecryptFindings( @Nonnull String expectedNodeString) { DetectionStore decryptStore = - getStoreOfValueType(ValueAction.class, store.getChildren()); + getStoreOfValueType(CipherAction.class, store.getChildren()); assertThat(decryptStore).isNotNull(); assertThat(decryptStore.getDetectionValues()).hasSize(1); assertThat(decryptStore.getDetectionValues().get(0).asString()).isEqualTo("DECRYPT"); From 80936bb692dfed638598e8e6df4e0fc7ae63e905 Mon Sep 17 00:00:00 2001 From: Fynn Thierling Date: Thu, 20 Aug 2026 20:31:04 +0200 Subject: [PATCH 05/10] first try at full coverage of system.security.cryptography. this needs extensive testing to be verified and assumed correct Signed-off-by: Fynn Thierling --- .../rules/detection/CSharpDetectionRules.java | 26 +- .../dotnet/DotNetAlgorithmFactory.java | 721 ++++++++++++++++++ .../dotnet/DotNetChaCha20Poly1305.java | 136 ++++ .../dotnet/DotNetECDiffieHellman.java | 191 ++++- .../rules/detection/dotnet/DotNetECDsa.java | 175 ++++- .../rules/detection/dotnet/DotNetHMAC.java | 73 +- .../rules/detection/dotnet/DotNetKMAC.java | 118 +++ .../detection/dotnet/DotNetKeyDerivation.java | 250 ++++++ .../dotnet/DotNetLegacyFormatters.java | 450 +++++++++++ .../rules/detection/dotnet/DotNetMLDsa.java | 555 ++++++++++++++ .../rules/detection/dotnet/DotNetMLKem.java | 327 ++++++++ .../detection/dotnet/DotNetProtectedData.java | 292 +++++++ .../rules/detection/dotnet/DotNetRC2.java | 566 +++++++++++++- .../rules/detection/dotnet/DotNetRSA.java | 249 +++++- .../dotnet/DotNetRandomNumberGenerator.java | 321 ++++++++ .../rules/detection/dotnet/DotNetSHA.java | 305 +++++++- .../rules/detection/dotnet/DotNetSHA3.java | 157 ++++ .../rules/detection/dotnet/DotNetSlhDsa.java | 370 +++++++++ .../detection/dotnet/DotNetTripleDES.java | 566 +++++++++++++- .../dotnet/DotNetX25519DiffieHellman.java | 195 +++++ .../translator/CSharpTranslator.java | 9 +- .../CSharpCipherContextTranslator.java | 44 ++ .../CSharpDigestContextTranslator.java | 31 + .../contexts/CSharpKeyContextTranslator.java | 200 +++++ .../contexts/CSharpMacContextTranslator.java | 50 ++ .../contexts/CSharpPRNGContextTranslator.java | 81 ++ .../dotnet/DotNetAlgorithmFactoryTestFile.cs | 126 +++ .../dotnet/DotNetChaCha20Poly1305TestFile.cs | 92 +++ .../dotnet/DotNetECDiffieHellmanTestFile.cs | 123 ++- .../detection/dotnet/DotNetECDsaTestFile.cs | 153 +++- .../detection/dotnet/DotNetHMACTestFile.cs | 5 + .../detection/dotnet/DotNetKMACTestFile.cs | 7 + .../dotnet/DotNetKeyDerivationTestFile.cs | 83 ++ .../dotnet/DotNetLegacyFormattersTestFile.cs | 138 ++++ .../detection/dotnet/DotNetMLDsaTestFile.cs | 266 +++++++ .../detection/dotnet/DotNetMLKemTestFile.cs | 138 ++++ .../dotnet/DotNetProtectedDataTestFile.cs | 84 ++ .../dotnet/DotNetRC2ComprehensiveTestFile.cs | 353 +++++++++ .../detection/dotnet/DotNetRSATestFile.cs | 182 ++++- .../DotNetRandomNumberGeneratorTestFile.cs | 120 +++ .../detection/dotnet/DotNetSHA3TestFile.cs | 8 + .../detection/dotnet/DotNetSHATestFile.cs | 20 + .../detection/dotnet/DotNetSlhDsaTestFile.cs | 152 ++++ .../DotNetTripleDESComprehensiveTestFile.cs | 359 +++++++++ .../DotNetX25519DiffieHellmanTestFile.cs | 85 +++ .../dotnet/DotNetAlgorithmFactoryTest.java | 235 ++++++ .../dotnet/DotNetChaCha20Poly1305Test.java | 118 +++ .../dotnet/DotNetECDiffieHellmanTest.java | 121 ++- .../detection/dotnet/DotNetECDsaTest.java | 139 +++- .../detection/dotnet/DotNetHMACTest.java | 70 ++ .../detection/dotnet/DotNetKMACTest.java | 106 +++ .../dotnet/DotNetKeyDerivationTest.java | 156 ++++ .../dotnet/DotNetLegacyFormattersTest.java | 264 +++++++ .../detection/dotnet/DotNetMLDsaTest.java | 274 +++++++ .../detection/dotnet/DotNetMLKemTest.java | 193 +++++ .../dotnet/DotNetProtectedDataTest.java | 155 ++++ .../dotnet/DotNetRC2ComprehensiveTest.java | 417 ++++++++++ .../rules/detection/dotnet/DotNetRSATest.java | 185 ++++- .../DotNetRandomNumberGeneratorTest.java | 144 ++++ .../detection/dotnet/DotNetSHA3Test.java | 127 +++ .../rules/detection/dotnet/DotNetSHATest.java | 85 +++ .../detection/dotnet/DotNetSlhDsaTest.java | 192 +++++ .../DotNetTripleDESComprehensiveTest.java | 438 +++++++++++ .../dotnet/DotNetX25519DiffieHellmanTest.java | 152 ++++ mapper/ciphersuites.json | 2 +- .../mapper/ssl/json/JsonCipherSuites.java | 33 + .../mapper/model/algorithms/SPHINCSPlus.java | 25 +- 67 files changed, 12495 insertions(+), 88 deletions(-) create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAlgorithmFactory.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKMAC.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivation.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetLegacyFormatters.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsa.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKem.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetProtectedData.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGenerator.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA3.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSlhDsa.java create mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellman.java create mode 100644 csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpPRNGContextTranslator.java create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetAlgorithmFactoryTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetChaCha20Poly1305TestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetKMACTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetKeyDerivationTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetLegacyFormattersTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetMLDsaTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetMLKemTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetProtectedDataTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetRC2ComprehensiveTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetRandomNumberGeneratorTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetSHA3TestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetSlhDsaTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetTripleDESComprehensiveTestFile.cs create mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetX25519DiffieHellmanTestFile.cs create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAlgorithmFactoryTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305Test.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKMACTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivationTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetLegacyFormattersTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsaTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKemTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetProtectedDataTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRC2ComprehensiveTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGeneratorTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA3Test.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSlhDsaTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetTripleDESComprehensiveTest.java create mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellmanTest.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 92576f65a..5f915a643 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 @@ -22,16 +22,28 @@ import com.ibm.engine.language.csharp.tree.CSharpTree; import com.ibm.engine.rule.IDetectionRule; import com.ibm.plugin.rules.detection.dotnet.DotNetAES; +import com.ibm.plugin.rules.detection.dotnet.DotNetAlgorithmFactory; +import com.ibm.plugin.rules.detection.dotnet.DotNetChaCha20Poly1305; import com.ibm.plugin.rules.detection.dotnet.DotNetDES; import com.ibm.plugin.rules.detection.dotnet.DotNetDSA; 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.DotNetKMAC; +import com.ibm.plugin.rules.detection.dotnet.DotNetKeyDerivation; +import com.ibm.plugin.rules.detection.dotnet.DotNetLegacyFormatters; +import com.ibm.plugin.rules.detection.dotnet.DotNetMLDsa; +import com.ibm.plugin.rules.detection.dotnet.DotNetMLKem; +import com.ibm.plugin.rules.detection.dotnet.DotNetProtectedData; import com.ibm.plugin.rules.detection.dotnet.DotNetRC2; import com.ibm.plugin.rules.detection.dotnet.DotNetRSA; +import com.ibm.plugin.rules.detection.dotnet.DotNetRandomNumberGenerator; import com.ibm.plugin.rules.detection.dotnet.DotNetRfc2898DeriveBytes; import com.ibm.plugin.rules.detection.dotnet.DotNetSHA; +import com.ibm.plugin.rules.detection.dotnet.DotNetSHA3; +import com.ibm.plugin.rules.detection.dotnet.DotNetSlhDsa; import com.ibm.plugin.rules.detection.dotnet.DotNetTripleDES; +import com.ibm.plugin.rules.detection.dotnet.DotNetX25519DiffieHellman; import java.util.List; import java.util.stream.Stream; import javax.annotation.Nonnull; @@ -47,16 +59,28 @@ private CSharpDetectionRules() { public static List> rules() { return Stream.of( DotNetAES.rules().stream(), + DotNetChaCha20Poly1305.rules().stream(), DotNetDES.rules().stream(), DotNetTripleDES.rules().stream(), DotNetRC2.rules().stream(), DotNetRSA.rules().stream(), DotNetECDsa.rules().stream(), DotNetECDiffieHellman.rules().stream(), + DotNetX25519DiffieHellman.rules().stream(), + DotNetMLKem.rules().stream(), + DotNetMLDsa.rules().stream(), + DotNetSlhDsa.rules().stream(), DotNetDSA.rules().stream(), + DotNetLegacyFormatters.rules().stream(), DotNetSHA.rules().stream(), + DotNetSHA3.rules().stream(), DotNetHMAC.rules().stream(), - DotNetRfc2898DeriveBytes.rules().stream()) + DotNetKMAC.rules().stream(), + DotNetRfc2898DeriveBytes.rules().stream(), + DotNetKeyDerivation.rules().stream(), + DotNetRandomNumberGenerator.rules().stream(), + DotNetProtectedData.rules().stream(), + DotNetAlgorithmFactory.rules().stream()) .flatMap(i -> i) .toList(); } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAlgorithmFactory.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAlgorithmFactory.java new file mode 100644 index 000000000..0c8d5b99c --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAlgorithmFactory.java @@ -0,0 +1,721 @@ +/* + * 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.detection.MethodMatcher; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.MacContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.BlockSizeFactory; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.ModeFactory; +import com.ibm.engine.model.factory.PaddingFactory; +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.stream.Stream; +import javax.annotation.Nonnull; + +/** + * Detection rules for the generic, string-based factory methods declared directly on the + * abstract base classes of {@code System.Security.Cryptography} — as opposed to the + * per-algorithm {@code Create()}/{@code Create(string)} overloads already covered on concrete + * algorithm classes ({@code RC2.Create(string)} in {@link DotNetRC2}, {@code + * RandomNumberGenerator.Create(string)} in {@link DotNetRandomNumberGenerator}, etc.), where the + * class name itself already fixes the algorithm identity and the string parameter only selects an + * implementation/provider. + * + *

Here, the base class carries no algorithm identity of its own — the algorithm is + * chosen entirely at runtime by the string argument: + * + *

    + *
  • {@code SymmetricAlgorithm.Create(string algName)} — e.g. {@code + * SymmetricAlgorithm.Create("RC2")} + *
  • {@code HashAlgorithm.Create(string hashName)} — e.g. {@code HashAlgorithm.Create("SHA256")} + *
  • {@code KeyedHashAlgorithm.Create(string algName)} — e.g. {@code + * KeyedHashAlgorithm.Create("HMACSHA256")} + *
  • {@code HMAC.Create(string algorithmName)} — e.g. {@code HMAC.Create("HMACSHA256")} + *
  • {@code AsymmetricAlgorithm.Create(string algName)} — e.g. {@code + * AsymmetricAlgorithm.Create("RSA")} + *
+ * + *

All five overloads are marked {@code Obsolete} (diagnostic {@code SYSLIB0045}) starting with + * .NET 7, in favor of the parameterless, strongly-typed {@code Create()} factory on each concrete + * algorithm type. They remain valid, detectable legacy source across every earlier and current + * .NET/.NET Framework version — the same "obsolete but in scope" precedent already established for + * {@code PasswordDeriveBytes} (see {@link DotNetKeyDerivation}) and {@code + * RandomNumberGenerator.Create(string)}/{@code RNGCryptoServiceProvider} (see {@link + * DotNetRandomNumberGenerator}). + * + *

Architecture — reusing the engine's existing string-value-to-algorithm resolution mechanism + * ({@code AlgorithmFactory}): {@code AlgorithmFactory} ({@code + * com.ibm.engine.model.factory.AlgorithmFactory}) is a generic, language-agnostic engine class + * (parametrized over {@code }, operating purely on {@code ResolvedValue}) already used + * identically by the Java module for the structurally identical problem — a call on an abstract + * base class whose method resolves an algorithm from a runtime string, e.g. {@code + * MessageDigest.getInstance(String)} ({@code JcaMessageDigestGetInstance}) or {@code + * Cipher.getInstance(String)} ({@code JcaCipherGetInstance}). No engine change was required: the + * same {@code .withMethodParameter(type).shouldBeDetectedAs(new AlgorithmFactory<>())} idiom used + * there is reused here verbatim. + * + *

Two things confirm this works for C# without any engine modification: + * + *

    + *
  • {@code CSharpTreeConverter} already converts {@code ClassName.Method(args)} call shapes + * (its own javadoc cites {@code Aes.Create()}, {@code RSA.Create(2048)} as the pattern it + * handles) — {@code SymmetricAlgorithm.Create("RC2")} is exactly this same shape, already + * exercised for a literal argument by {@code RSA.Create(2048)} (an {@code int} literal + * captured via {@code KeySizeFactory} in {@link DotNetRSA}) and for a string argument (with + * no value capture) by {@code RandomNumberGenerator.Create(string)} in {@link + * DotNetRandomNumberGenerator}. + *
  • {@code CSharpDetectionEngine.resolveValues} resolves a {@code CSharpLiteralTree} (which is + * exactly what a string-literal argument becomes, per {@code + * CSharpTreeConverter#convertLiteral}) to its raw string value regardless of what class the + * enclosing call is invoked on — value resolution is driven purely by the argument + * expression, not by the receiver. There is no code path in the C# engine that special-cases + * "abstract base class" vs. "concrete class" as a call receiver: {@code + * getInvokedObjectTypeString} just compares the receiver text against whatever string {@code + * forObjectTypes(...)} was given. + *
+ * + *

Depending rules — deliberately asymmetric across the five factories: + * + *

    + *
  • {@code SymmetricAlgorithm.Create(string)} does attach a depending-rule set ({@code + * SYMMETRIC_ALGORITHM_DEPENDING_RULES} below) for post-creation cipher operations ({@code + * CreateEncryptor}/{@code CreateDecryptor}, {@code EncryptCbc}/{@code Ecb}/{@code Cfb}, + * {@code DecryptCbc}/{@code Ecb}/{@code Cfb}, the {@code Try*} variants, {@code + * GenerateKey}/{@code GenerateIV}, and the {@code Mode}/{@code KeySize}/{@code Padding}/ + * {@code FeedbackSize} property setters). Every one of these rules is declared with {@code + * forObjectTypes(MethodMatcher.ANY)} — i.e. it fires on method name alone, never on a + * concrete receiver class — so it applies unconditionally no matter which concrete algorithm + * (AES/DES/RC2/TripleDES) the runtime string actually resolves to at a given call site; there + * is no "arbitrary pick" being made. This is a literal, line-for-line duplicate of the + * identical {@code PROPERTY_SETTER_RULES}/{@code CIPHER_OP_RULES} pair already defined in + * {@link DotNetAES}/{@link DotNetRC2}/{@link DotNetTripleDES}/{@link DotNetDES} (those fields + * are private to their own files, and this module's established convention is to duplicate + * such per-algorithm depending-rule lists per file rather than centrally share them — see + * e.g. {@code DotNetAES}'s own class javadoc), so this follows the same pattern rather than + * inventing a new shared abstraction. + *
  • {@code HashAlgorithm.Create(string)}, {@code KeyedHashAlgorithm.Create(string)} and {@code + * HMAC.Create(string)} intentionally attach no depending rules, consistent with + * {@link DotNetSHA}/{@link DotNetHMAC}: a hash/MAC algorithm's cryptographically relevant + * information is already fully captured by the algorithm-identity value alone, and operations + * such as {@code ComputeHash}/{@code TransformBlock} add nothing to the CBOM model (unlike + * Encrypt vs. Decrypt for ciphers). Attaching operation rules here would be inconsistent with + * that established, deliberate precedent. + *
  • {@code AsymmetricAlgorithm.Create(string)} intentionally attaches no depending + * rules either, but for a different, structural reason: verified against the official API + * reference + * (learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.asymmetricalgorithm), + * the {@code AsymmetricAlgorithm} base class itself declares no {@code Sign}/{@code Verify}/ + * {@code SignData}/{@code VerifyData}/{@code SignHash}/{@code VerifyHash} methods at all — + * its "Methods" table only lists key import/export members ({@code ExportPkcs8PrivateKey}, + * {@code ImportFromPem}, etc.) plus {@code Clear}/{@code Dispose}. Signing/verification is + * declared only on the concrete subclasses ({@code RSA}, {@code DSA}, {@code ECDsa} — see + * {@link DotNetRSA}/{@link DotNetDSA}/{@link DotNetECDsa} for their own {@code + * SignatureActionFactory}-based depending rules), which a rule keyed on the abstract base + * class has no way to reach. Unlike the {@code SymmetricAlgorithm} case above, there is no + * generic, algorithm-independent operation set to duplicate here. + *
+ * + *

String tables — verified against the official API reference (learn.microsoft.com), not + * guessed: + * + *

    + *
  • {@code HashAlgorithm.Create(string)}, {@code KeyedHashAlgorithm.Create(string)} and {@code + * HMAC.Create(string)} each document an explicit table of accepted {@code hashName}/{@code + * algName}/{@code algorithmName} values (including fully-qualified {@code + * System.Security.Cryptography.*} forms) — reproduced exactly in {@code + * CSharpDigestContextTranslator} and {@code CSharpMacContextTranslator}. + *
  • {@code AsymmetricAlgorithm.Create(string)} documents an explicit table covering {@code + * RSA}, {@code DSA}, {@code ECDsa}/{@code ECDsaCng}, and {@code ECDH}/{@code + * ECDiffieHellman}/{@code ECDiffieHellmanCng} — reproduced in {@code + * CSharpKeyContextTranslator}. + *
  • {@code SymmetricAlgorithm.Create(string)} is the one exception: unlike its four siblings, + * its API reference page does not publish an explicit value table (the underlying + * .NET Core implementation only special-cases a handful of names before falling through to + * reflection-based {@code CryptoConfig} lookup). {@code CSharpCipherContextTranslator}'s new + * branch therefore reuses the same well-known names already accepted elsewhere in that + * translator for this codebase's .NET support (AES, DES, 3DES/TripleDES, RC2) rather than + * inventing an unverifiable table. + *
+ */ +public final class DotNetAlgorithmFactory { + + private DotNetAlgorithmFactory() { + // nothing + } + + // ========================================================================= + // SymmetricAlgorithm.Create(string) depending rules + // + // A SymmetricAlgorithm.Create("...") call site is tracked exactly like any other + // SymmetricAlgorithm-derived variable (Aes/RC2/TripleDES/DES): every method inherited from + // SymmetricAlgorithm itself (CreateEncryptor/CreateDecryptor, EncryptCbc/Ecb/Cfb, + // DecryptCbc/Ecb/Cfb, the Try* variants, GenerateKey/GenerateIV, and the Mode/KeySize/ + // Padding/FeedbackSize property setters) is algorithm-independent — it only depends on the + // method name, never on a concrete class name (forObjectTypes(MethodMatcher.ANY)) — so it + // applies unconditionally regardless of which concrete algorithm the runtime string resolves + // to. This is a deliberate, literal duplicate of the identical rule set already defined in + // DotNetAES/DotNetRC2/DotNetTripleDES/DotNetDES (see their PROPERTY_SETTER_RULES/ + // CIPHER_OP_RULES): those fields are private to their own files, and every existing + // per-algorithm depending-rule list in this module is duplicated per file rather than + // centrally shared, so this follows the same established convention instead of inventing a + // new shared abstraction. + // ========================================================================= + + // alg.Mode = CipherMode.CBC → synthetic set_Mode(CipherMode.CBC) + private static final IDetectionRule SYMMETRIC_ALGORITHM_SET_MODE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_Mode") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new ModeFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // alg.KeySize = 256 → synthetic set_KeySize(256) + private static final IDetectionRule SYMMETRIC_ALGORITHM_SET_KEY_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_KeySize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // alg.Padding = PaddingMode.PKCS7 → synthetic set_Padding(PaddingMode.PKCS7) + private static final IDetectionRule SYMMETRIC_ALGORITHM_SET_PADDING = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_Padding") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // alg.FeedbackSize = 128 → synthetic set_FeedbackSize(128) + private static final IDetectionRule SYMMETRIC_ALGORITHM_SET_FEEDBACK_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_FeedbackSize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new BlockSizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> + SYMMETRIC_ALGORITHM_PROPERTY_SETTER_RULES = + List.of( + SYMMETRIC_ALGORITHM_SET_MODE, + SYMMETRIC_ALGORITHM_SET_KEY_SIZE, + SYMMETRIC_ALGORITHM_SET_PADDING, + SYMMETRIC_ALGORITHM_SET_FEEDBACK_SIZE); + + // alg.CreateEncryptor() + private static final IDetectionRule SYMMETRIC_ALGORITHM_CREATE_ENCRYPTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateEncryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // alg.CreateEncryptor(byte[] key, byte[] iv) + private static final IDetectionRule SYMMETRIC_ALGORITHM_CREATE_ENCRYPTOR_WITH_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateEncryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withMethodParameter(MethodMatcher.ANY) // key bytes + .withMethodParameter(MethodMatcher.ANY) // iv bytes + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // alg.CreateDecryptor() + private static final IDetectionRule SYMMETRIC_ALGORITHM_CREATE_DECRYPTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateDecryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // alg.CreateDecryptor(byte[] key, byte[] iv) + private static final IDetectionRule SYMMETRIC_ALGORITHM_CREATE_DECRYPTOR_WITH_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateDecryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withMethodParameter(MethodMatcher.ANY) // key bytes + .withMethodParameter(MethodMatcher.ANY) // iv bytes + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCbc(plaintext, iv, padding) + private static final IDetectionRule SYMMETRIC_ALGORITHM_ENCRYPT_CBC_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCbc(plaintext, iv, destination, padding) [output-buffer overload] + private static final IDetectionRule SYMMETRIC_ALGORITHM_ENCRYPT_CBC_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCbc(ciphertext, iv, padding) + private static final IDetectionRule SYMMETRIC_ALGORITHM_DECRYPT_CBC_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCbc(ciphertext, iv, destination, padding) + private static final IDetectionRule SYMMETRIC_ALGORITHM_DECRYPT_CBC_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptEcb(plaintext, padding) + private static final IDetectionRule SYMMETRIC_ALGORITHM_ENCRYPT_ECB_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptEcb(plaintext, destination, padding) + private static final IDetectionRule SYMMETRIC_ALGORITHM_ENCRYPT_ECB_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptEcb(ciphertext, padding) + private static final IDetectionRule SYMMETRIC_ALGORITHM_DECRYPT_ECB_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptEcb(ciphertext, destination, padding) + private static final IDetectionRule SYMMETRIC_ALGORITHM_DECRYPT_ECB_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCfb(plaintext, iv, padding, feedbackSize) + private static final IDetectionRule SYMMETRIC_ALGORITHM_ENCRYPT_CFB_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCfb(plaintext, iv, destination, padding, feedbackSize) + private static final IDetectionRule SYMMETRIC_ALGORITHM_ENCRYPT_CFB_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCfb(ciphertext, iv, padding, feedbackSize) + private static final IDetectionRule SYMMETRIC_ALGORITHM_DECRYPT_CFB_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCfb(ciphertext, iv, destination, padding, feedbackSize) + private static final IDetectionRule SYMMETRIC_ALGORITHM_DECRYPT_CFB_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptCbc(plaintext, iv, destination, out bytesWritten, padding) + private static final IDetectionRule SYMMETRIC_ALGORITHM_TRY_ENCRYPT_CBC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, padding) + private static final IDetectionRule SYMMETRIC_ALGORITHM_TRY_DECRYPT_CBC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptEcb(plaintext, destination, padding, out bytesWritten) + private static final IDetectionRule SYMMETRIC_ALGORITHM_TRY_ENCRYPT_ECB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptEcb(ciphertext, destination, padding, out bytesWritten) + private static final IDetectionRule SYMMETRIC_ALGORITHM_TRY_DECRYPT_ECB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptCfb(plaintext, iv, destination, out bytesWritten, padding, feedbackSize) + private static final IDetectionRule SYMMETRIC_ALGORITHM_TRY_ENCRYPT_CFB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, padding, feedbackSize) + private static final IDetectionRule SYMMETRIC_ALGORITHM_TRY_DECRYPT_CFB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // alg.GenerateKey() — generates a new random key (size determined by KeySize property) + private static final IDetectionRule SYMMETRIC_ALGORITHM_GENERATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("GenerateKey")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // alg.GenerateIV() — generates a new random initialization vector + private static final IDetectionRule SYMMETRIC_ALGORITHM_GENERATE_IV = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GenerateIV") + .shouldBeDetectedAs(new ValueActionFactory<>("GenerateIV")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + /** + * All cipher operation rules that fire on a tracked SymmetricAlgorithm-family variable. + * Includes CreateEncryptor/CreateDecryptor, direct mode-specific Encrypt/Decrypt methods, Try* + * variants, and key/IV generation. Literal duplicate of the identical list in + * DotNetAES/DotNetRC2/DotNetTripleDES/DotNetDES (see class javadoc above). + */ + private static final List> SYMMETRIC_ALGORITHM_CIPHER_OP_RULES = + List.of( + SYMMETRIC_ALGORITHM_CREATE_ENCRYPTOR, + SYMMETRIC_ALGORITHM_CREATE_ENCRYPTOR_WITH_KEY, + SYMMETRIC_ALGORITHM_CREATE_DECRYPTOR, + SYMMETRIC_ALGORITHM_CREATE_DECRYPTOR_WITH_KEY, + SYMMETRIC_ALGORITHM_ENCRYPT_CBC_3, + SYMMETRIC_ALGORITHM_ENCRYPT_CBC_4, + SYMMETRIC_ALGORITHM_DECRYPT_CBC_3, + SYMMETRIC_ALGORITHM_DECRYPT_CBC_4, + SYMMETRIC_ALGORITHM_ENCRYPT_ECB_2, + SYMMETRIC_ALGORITHM_ENCRYPT_ECB_3, + SYMMETRIC_ALGORITHM_DECRYPT_ECB_2, + SYMMETRIC_ALGORITHM_DECRYPT_ECB_3, + SYMMETRIC_ALGORITHM_ENCRYPT_CFB_4, + SYMMETRIC_ALGORITHM_ENCRYPT_CFB_5, + SYMMETRIC_ALGORITHM_DECRYPT_CFB_4, + SYMMETRIC_ALGORITHM_DECRYPT_CFB_5, + SYMMETRIC_ALGORITHM_TRY_ENCRYPT_CBC, + SYMMETRIC_ALGORITHM_TRY_DECRYPT_CBC, + SYMMETRIC_ALGORITHM_TRY_ENCRYPT_ECB, + SYMMETRIC_ALGORITHM_TRY_DECRYPT_ECB, + SYMMETRIC_ALGORITHM_TRY_ENCRYPT_CFB, + SYMMETRIC_ALGORITHM_TRY_DECRYPT_CFB, + SYMMETRIC_ALGORITHM_GENERATE_KEY, + SYMMETRIC_ALGORITHM_GENERATE_IV); + + /** Full set of depending rules for the SymmetricAlgorithm.Create(string) factory. */ + private static final List> SYMMETRIC_ALGORITHM_DEPENDING_RULES = + Stream.concat( + SYMMETRIC_ALGORITHM_PROPERTY_SETTER_RULES.stream(), + SYMMETRIC_ALGORITHM_CIPHER_OP_RULES.stream()) + .toList(); + + // SymmetricAlgorithm.Create(string algName) — e.g. SymmetricAlgorithm.Create("RC2") + private static final IDetectionRule SYMMETRIC_ALGORITHM_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SymmetricAlgorithm") + .forMethods("Create") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(SYMMETRIC_ALGORITHM_DEPENDING_RULES); + + // HashAlgorithm.Create(string hashName) — e.g. HashAlgorithm.Create("SHA256") + private static final IDetectionRule HASH_ALGORITHM_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("HashAlgorithm") + .forMethods("Create") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // KeyedHashAlgorithm.Create(string algName) — e.g. KeyedHashAlgorithm.Create("HMACSHA256") + private static final IDetectionRule KEYED_HASH_ALGORITHM_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("KeyedHashAlgorithm") + .forMethods("Create") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new MacContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // HMAC.Create(string algorithmName) — e.g. HMAC.Create("HMACSHA256") + private static final IDetectionRule HMAC_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("HMAC") + .forMethods("Create") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new MacContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // AsymmetricAlgorithm.Create(string algName) — e.g. AsymmetricAlgorithm.Create("RSA") + // No fixed "kind" property: CSharpKeyContextTranslator's new Algorithm-typed branch resolves + // the concrete algorithm (RSA/DSA/ECDSA/ECDH) purely from the captured string value. + private static final IDetectionRule ASYMMETRIC_ALGORITHM_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("AsymmetricAlgorithm") + .forMethods("Create") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new KeyContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + @Nonnull + public static List> rules() { + return List.of( + SYMMETRIC_ALGORITHM_CREATE, + HASH_ALGORITHM_CREATE, + KEYED_HASH_ALGORITHM_CREATE, + HMAC_CREATE, + ASYMMETRIC_ALGORITHM_CREATE); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305.java new file mode 100644 index 000000000..756afc73c --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305.java @@ -0,0 +1,136 @@ +/* + * 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.detection.MethodMatcher; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.CipherActionFactory; +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 javax.annotation.Nonnull; + +/** + * Detection rules for {@code ChaCha20Poly1305} in System.Security.Cryptography. + * + *

{@code ChaCha20Poly1305} is a sealed AEAD cipher class (available since .NET 6, platform gated + * by {@code IsSupported}), structurally analogous to {@code AesGcm}/{@code AesCcm} in {@link + * DotNetAES}: it is constructed from a key and exposes {@code Encrypt}/{@code Decrypt} methods + * taking nonce, plaintext/ciphertext, tag and an optional associated-data buffer. There is no class + * hierarchy to cover (the class is {@code sealed}), and no inherited {@code SymmetricAlgorithm} + * surface (property setters, {@code CreateEncryptor}, mode-specific Encrypt/Decrypt, etc.) applies + * here. + * + *

Constructors covered: + * + *

    + *
  • {@code ChaCha20Poly1305(byte[] key)} + *
  • {@code ChaCha20Poly1305(ReadOnlySpan key)} + *
+ * + * Both take exactly one parameter, so a single rule using {@code withAnyParameters()} covers both + * overloads (parameter types are not resolvable by the engine — see {@code + * CSharpLanguageTranslation}). + * + *

The static {@code IsSupported} property is a platform-availability check, not + * detection-relevant cryptographic information, and is intentionally not modeled (mirrors how + * KMAC/SHA-3 platform-support properties are ignored elsewhere in this module). + * + *

Operations covered as depending rules (fired only on a tracked {@code ChaCha20Poly1305} + * variable), mirroring the {@code AesGcm}/{@code AesCcm} pattern in {@link DotNetAES}: + * + *

    + *
  • {@code Encrypt(nonce, plaintext, ciphertext, tag [, associatedData])} — both the {@code + * byte[]} and {@code ReadOnlySpan} overloads always declare all five parameters (the + * last one defaults to {@code null}/{@code default}), but callers may omit the trailing + * associated-data argument at the call site, so {@code withAnyParameters()} is used to match + * both the 4- and 5-argument call shapes, exactly like {@code AesGcm.Encrypt}. + *
  • {@code Decrypt(nonce, ciphertext, tag, plaintext [, associatedData])} — same reasoning. + *
+ * + *

Known gap: nonce length (fixed at 12 bytes), tag length (fixed at 16 bytes) and the + * associated-data content are not captured as separate values. As with {@code AesGcm}/{@code + * AesCcm}, these arguments are almost always local {@code byte[]} variables (e.g. {@code new + * byte[12]}) rather than literals passed directly into {@code Encrypt}/{@code Decrypt}, and the + * engine cannot resolve values across variable assignments (see {@code CSharpSymbol}). // TODO: + * ChaCha20Poly1305 nonce/tag length capture is not possible with the current engine and is left as + * a known gap, consistent with the same limitation for AesGcm/AesCcm. + */ +public final class DotNetChaCha20Poly1305 { + + private DotNetChaCha20Poly1305() { + // nothing + } + + // ========================================================================= + // ChaCha20Poly1305 AEAD operation rules + // ========================================================================= + + // chaCha20Poly1305.Encrypt(nonce, plaintext, ciphertext, tag [, associatedData]) + private static final IDetectionRule CHACHA20POLY1305_ENCRYPT_OP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Encrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // chaCha20Poly1305.Decrypt(nonce, ciphertext, tag, plaintext [, associatedData]) + private static final IDetectionRule CHACHA20POLY1305_DECRYPT_OP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Decrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> CHACHA20POLY1305_OP_RULES = + List.of(CHACHA20POLY1305_ENCRYPT_OP, CHACHA20POLY1305_DECRYPT_OP); + + // ========================================================================= + // Primary creation rule + // ========================================================================= + + // new ChaCha20Poly1305(key) — AEAD (byte[] or ReadOnlySpan, 1 param) + private static final IDetectionRule CHACHA20_POLY1305 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("ChaCha20Poly1305") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("CHACHA20-POLY1305")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(CHACHA20POLY1305_OP_RULES); + + @Nonnull + public static List> rules() { + return List.of(CHACHA20_POLY1305); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDiffieHellman.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDiffieHellman.java index 79ea96196..ab7f54036 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDiffieHellman.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDiffieHellman.java @@ -19,8 +19,11 @@ */ package com.ibm.plugin.rules.detection.dotnet; +import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.Size; import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.factory.KeySizeFactory; import com.ibm.engine.model.factory.ValueActionFactory; import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.builder.DetectionRuleBuilder; @@ -29,15 +32,66 @@ import javax.annotation.Nonnull; /** - * Detection rules for ECDH usage in System.Security.Cryptography. + * Detection rules for ECDH (Elliptic Curve Diffie-Hellman key agreement) usage in + * System.Security.Cryptography. * - *

Detects: + *

Classes covered: * *

    - *
  • {@code ECDiffieHellman.Create()} — abstract factory - *
  • {@code ECDiffieHellman.Create(curve)} — factory with ECCurve - *
  • {@code new ECDiffieHellmanCng()} — CNG-backed implementation + *
  • {@code ECDiffieHellman} — abstract base ({@code ECDiffieHellman.Create()}, {@code + * ECDiffieHellman.Create(ECCurve)}, {@code ECDiffieHellman.Create(ECParameters)}, {@code + * ECDiffieHellman.Create(string)}) + *
  • {@code ECDiffieHellmanCng} — CNG-backed implementation, Windows-only + *
  • {@code ECDiffieHellmanOpenSsl} — OpenSSL-backed implementation, non-Windows only *
+ * + *

Architecture: all members inherited from {@code ECDiffieHellman} / {@code AsymmetricAlgorithm} + * (the {@code KeySize} property, {@code DeriveKeyMaterial}, {@code DeriveKeyFromHash}, {@code + * DeriveKeyFromHmac}, {@code DeriveKeyTls}, {@code DeriveRawSecretAgreement}) are expressed as + * depending rules attached to each primary creation rule, mirroring {@code + * DotNetECDsa.java} / {@code DotNetAES.java}. Method overloads that only differ by array-vs-{@code + * Span}, {@code CngKey}-vs-{@code ECDiffieHellmanPublicKey} receiver type, optional prepend/append + * byte arrays, or output-buffer parameters are intentionally collapsed into a single {@code + * withAnyParameters()} rule per method name: the ANTLR4-based C# engine cannot resolve parameter + * types (see {@code CSharpLanguageTranslation}), so distinguishing overloads by parameter type is + * not possible. + * + *

None of the key-derivation operations ({@code DeriveKeyMaterial}, {@code DeriveKeyFromHash}, + * {@code DeriveKeyFromHmac}, {@code DeriveKeyTls}, {@code DeriveRawSecretAgreement}) fit any of the + * typed {@code CipherAction.Action} values ({@code WRAP}, {@code HASH}, {@code ENCRYPT}, {@code + * DECRYPT}, {@code PADDING}, {@code MAC}, {@code NONE}) — none of them mean "derive a key". + * Following the existing convention of generic string captures for operations without a typed + * action (e.g. {@code GenerateKey}/{@code GenerateIV} in {@code DotNetAES.java}), each derive + * operation is captured with {@code ValueActionFactory<>()} under its own {@code + * KeyContext} "kind", and translated in {@code CSharpKeyContextTranslator} to the generic {@code + * KeyDerivation} functionality node (or, for {@code DeriveRawSecretAgreement} — which returns the + * raw shared secret with no KDF post-processing applied — to the generic {@code Generate} + * functionality node, mirroring how {@code GenerateIV} is translated). This is a modeling decision + * with real discretion (no dedicated "key agreement derive" functionality node exists yet in the + * mapper model); it is called out here and in the task report rather than decided silently. + * + *

Known gap — {@code PublicKey} property: reading {@code ecdh.PublicKey} (e.g. to send to + * a peer, or to call {@code alice.PublicKey.ToByteArray()}) cannot be detected with the current + * engine. This was verified by tracing the engine code, not assumed: + * + *

    + *
  • A bare property read with no chained call (e.g. {@code var pub = alice.PublicKey;}) never + * reaches the tree converter at all — {@code CSharpTreeConverter.convertPrimaryExpression} + * only emits a node when the {@code primary_expression} contains a {@code method_invocation}; + * a lone member access produces nothing. + *
  • A chained call such as {@code alice.PublicKey.ToByteArray()} *does* produce a {@code + * CSharpMethodInvocationTree} node, but {@code resolveObjectTypeName} resolves its receiver + * ({@code getObjectTypeName()}) to the generic member name {@code "PublicKey"} (the member + * access immediately preceding the call), not the tracked variable name {@code "alice"}. In + * {@code CSharpDetectionEngine.isInvocationOnVariable}, the match is {@code + * invocation.getObjectTypeName().equals(sym.getName())} — so this always fails for the + * tracked {@code alice} symbol, and the depending-rule scan skips the statement before rule + * matching is even attempted (see {@code processStatement}). + *
+ * + *

No rule is added for {@code PublicKey}; forcing one would either never fire (dead code) or + * fire on an unrelated broad pattern (any {@code x.PublicKey.Method()} in the file, disconnected + * from the tracked ECDH variable). */ @SuppressWarnings("java:S1192") public final class DotNetECDiffieHellman { @@ -46,6 +100,109 @@ private DotNetECDiffieHellman() { // nothing } + // ========================================================================= + // Property setter rules (synthetic set_X method invocations) + // ========================================================================= + + // ecdh.KeySize = 384 → synthetic set_KeySize(384) + private static final IDetectionRule ECDH_SET_KEY_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_KeySize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new KeyContext(Map.of("kind", "ECDH"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Key-derivation operation rules + // See class javadoc for why ValueActionFactory (not a typed CipherAction) is used, and why + // each method gets its own KeyContext "kind" (dispatched in CSharpKeyContextTranslator). + // ========================================================================= + + // ecdh.DeriveKeyMaterial(otherPartyPublicKey) + private static final IDetectionRule ECDH_DERIVE_KEY_MATERIAL = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DeriveKeyMaterial") + .shouldBeDetectedAs(new ValueActionFactory<>("DeriveKeyMaterial")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "ECDH_DERIVE_KEY_MATERIAL"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ecdh.DeriveKeyFromHash(otherPartyPublicKey, hashAlgorithm[, secretPrepend, secretAppend]) + private static final IDetectionRule ECDH_DERIVE_KEY_FROM_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DeriveKeyFromHash") + .shouldBeDetectedAs(new ValueActionFactory<>("DeriveKeyFromHash")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "ECDH_DERIVE_KEY_FROM_HASH"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ecdh.DeriveKeyFromHmac(otherPartyPublicKey, hashAlgorithm, hmacKey[, prepend, append]) + private static final IDetectionRule ECDH_DERIVE_KEY_FROM_HMAC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DeriveKeyFromHmac") + .shouldBeDetectedAs(new ValueActionFactory<>("DeriveKeyFromHmac")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "ECDH_DERIVE_KEY_FROM_HMAC"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ecdh.DeriveKeyTls(otherPartyPublicKey, prfLabel, prfSeed) — TLS 1.1 PRF-based derivation. + // Per the ECDiffieHellman API reference there is only one such method (no "1_2" variant). + private static final IDetectionRule ECDH_DERIVE_KEY_TLS = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DeriveKeyTls") + .shouldBeDetectedAs(new ValueActionFactory<>("DeriveKeyTls")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "ECDH_DERIVE_KEY_TLS"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ecdh.DeriveRawSecretAgreement(otherPartyPublicKey) — raw shared secret, no KDF applied. + private static final IDetectionRule ECDH_DERIVE_RAW_SECRET_AGREEMENT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DeriveRawSecretAgreement") + .shouldBeDetectedAs(new ValueActionFactory<>("DeriveRawSecretAgreement")) + .withAnyParameters() + .buildForContext( + new KeyContext(Map.of("kind", "ECDH_DERIVE_RAW_SECRET_AGREEMENT"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Aggregated depending-rule list + // ========================================================================= + + /** Full set of depending rules for all ECDiffieHellman-derived classes. */ + private static final List> ECDH_DEPENDING_RULES = + List.of( + ECDH_SET_KEY_SIZE, + ECDH_DERIVE_KEY_MATERIAL, + ECDH_DERIVE_KEY_FROM_HASH, + ECDH_DERIVE_KEY_FROM_HMAC, + ECDH_DERIVE_KEY_TLS, + ECDH_DERIVE_RAW_SECRET_AGREEMENT); + + // ========================================================================= + // Primary creation rules + // ========================================================================= + + // ECDiffieHellman.Create() / Create(ECCurve) / Create(ECParameters) / Create(string) private static final IDetectionRule ECDH_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -55,21 +212,37 @@ private DotNetECDiffieHellman() { .withAnyParameters() .buildForContext(new KeyContext(Map.of("kind", "ECDH"))) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(ECDH_DEPENDING_RULES); + // new ECDiffieHellmanCng() / (CngKey) / (ECCurve) / (int) — CNG-backed implementation. + // Uses withAnyParameters() to cover all constructor overloads in a single rule and avoid + // double-detection (see AES_CNG_NAMED in DotNetAES.java / ECDSA_OPENSSL in DotNetECDsa.java). private static final IDetectionRule ECDH_CNG = new DetectionRuleBuilder() .createDetectionRule() .forObjectTypes("ECDiffieHellmanCng") .forMethods("") .shouldBeDetectedAs(new ValueActionFactory<>("ECDH")) - .withoutParameters() + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "ECDH"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(ECDH_DEPENDING_RULES); + + // new ECDiffieHellmanOpenSsl() / (ECCurve) / (int) / (IntPtr) / (SafeEvpPKeyHandle) — + // OpenSSL-backed implementation. Uses withAnyParameters() for the same reason as ECDH_CNG. + private static final IDetectionRule ECDH_OPENSSL = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("ECDiffieHellmanOpenSsl") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("ECDH")) + .withAnyParameters() .buildForContext(new KeyContext(Map.of("kind", "ECDH"))) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(ECDH_DEPENDING_RULES); @Nonnull public static List> rules() { - return List.of(ECDH_CREATE, ECDH_CNG); + return List.of(ECDH_CREATE, ECDH_CNG, ECDH_OPENSSL); } } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDsa.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDsa.java index 5c9fb5f54..c0d68c902 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDsa.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDsa.java @@ -19,8 +19,14 @@ */ package com.ibm.plugin.rules.detection.dotnet; +import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.Size; import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; import com.ibm.engine.model.factory.ValueActionFactory; import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.builder.DetectionRuleBuilder; @@ -31,13 +37,29 @@ /** * Detection rules for ECDSA usage in System.Security.Cryptography. * - *

Detects: + *

Classes covered: * *

    - *
  • {@code ECDsa.Create()} — factory without curve - *
  • {@code ECDsa.Create(curve)} — factory with ECCurve parameter - *
  • {@code new ECDsaCng()} — CNG-backed implementation + *
  • {@code ECDsa} — abstract base ({@code ECDsa.Create()}, {@code ECDsa.Create(ECCurve)}, + * {@code ECDsa.Create(ECParameters)}, {@code ECDsa.Create(string)}) + *
  • {@code ECDsaCng} — CNG-backed implementation, Windows-only ({@code ECDsaCng()}, {@code + * ECDsaCng(CngKey)}, {@code ECDsaCng(ECCurve)}, {@code ECDsaCng(int)}) + *
  • {@code ECDsaOpenSsl} — OpenSSL-backed implementation, non-Windows only *
+ * + *

Architecture: all members inherited from {@code ECDsa} / {@code AsymmetricAlgorithm} (the + * {@code KeySize} property, {@code SignData}/{@code VerifyData}, {@code SignHash}/{@code + * VerifyHash}, and their {@code Try*} variants) are expressed as depending rules attached + * to each primary creation rule. The detection engine tracks the variable and fires these rules on + * every matching method call, regardless of the concrete ECDsa subclass. Method overloads that only + * differ by array-vs-{@code Span}, offset/length, {@code DSASignatureFormat}, or output-buffer + * parameters are intentionally collapsed into a single {@code withAnyParameters()} rule per method + * name: the ANTLR4-based C# engine cannot resolve parameter types (see {@code + * CSharpLanguageTranslation}), so distinguishing overloads by parameter type is not possible, and + * none of the extra parameters carry additional cryptographic information worth extracting. Unlike + * RSA, ECDSA has no encrypt/decrypt operations — it is signature-only. Per the {@code ECDsa} API + * reference, there are no {@code TryVerifyData}/{@code TryVerifyHash} methods (verification returns + * a bool directly, so there is no output buffer to size), mirroring RSA. */ @SuppressWarnings("java:S1192") public final class DotNetECDsa { @@ -46,6 +68,125 @@ private DotNetECDsa() { // nothing } + // ========================================================================= + // Property setter rules (synthetic set_X method invocations) + // ========================================================================= + + // ecdsa.KeySize = 256 → synthetic set_KeySize(256) + private static final IDetectionRule ECDSA_SET_KEY_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_KeySize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new KeyContext(Map.of("kind", "ECDSA"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Signing / verification operation rules + // Each rule covers every overload of the given method name (arities vary only + // by hash-algorithm / DSASignatureFormat / offset-length / output-buffer + // parameters, which are not individually tracked), mirroring the RSA/DSA + // SignData()/VerifyData() rules. + // ========================================================================= + + // ecdsa.SignData(data, hashAlgorithm[, format]) [+ offset/length or Stream overloads] + private static final IDetectionRule ECDSA_SIGN_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ecdsa.TrySignData(data, destination, hashAlgorithm[, format], out bytesWritten) + private static final IDetectionRule ECDSA_TRY_SIGN_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TrySignData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ecdsa.SignHash(hash[, format]) — legacy byte[] overload (ECDsaOpenSsl) and modern + // Span/DSASignatureFormat overloads (ECDsa base) are all covered. + private static final IDetectionRule ECDSA_SIGN_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ecdsa.TrySignHash(hash, destination[, format], out bytesWritten) + private static final IDetectionRule ECDSA_TRY_SIGN_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TrySignHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ecdsa.VerifyData(data, signature, hashAlgorithm[, format]) [+ offset/length or Stream + // overloads] + private static final IDetectionRule ECDSA_VERIFY_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ecdsa.VerifyHash(hash, signature[, format]) — legacy byte[] overload (ECDsaOpenSsl) and + // modern ReadOnlySpan/DSASignatureFormat overloads (ECDsa base) are all covered. + private static final IDetectionRule ECDSA_VERIFY_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Aggregated depending-rule list + // ========================================================================= + + /** Full set of depending rules for all ECDsa-derived classes. */ + private static final List> ECDSA_DEPENDING_RULES = + List.of( + ECDSA_SET_KEY_SIZE, + ECDSA_SIGN_DATA, + ECDSA_TRY_SIGN_DATA, + ECDSA_SIGN_HASH, + ECDSA_TRY_SIGN_HASH, + ECDSA_VERIFY_DATA, + ECDSA_VERIFY_HASH); + + // ========================================================================= + // Primary creation rules + // ========================================================================= + + // ECDsa.Create() / ECDsa.Create(ECCurve) / ECDsa.Create(ECParameters) / ECDsa.Create(string) private static final IDetectionRule ECDSA_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -55,21 +196,39 @@ private DotNetECDsa() { .withAnyParameters() .buildForContext(new KeyContext(Map.of("kind", "ECDSA"))) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(ECDSA_DEPENDING_RULES); + // new ECDsaCng() / (CngKey) / (ECCurve) / (int) — CNG-backed implementation. + // Uses withAnyParameters() to cover all constructor overloads in a single rule and avoid + // double-detection (see AES_CNG_NAMED in DotNetAES.java / ECDH_CNG in + // DotNetECDiffieHellman.java). private static final IDetectionRule ECDSA_CNG = new DetectionRuleBuilder() .createDetectionRule() .forObjectTypes("ECDsaCng") .forMethods("") .shouldBeDetectedAs(new ValueActionFactory<>("ECDSA")) - .withoutParameters() + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "ECDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(ECDSA_DEPENDING_RULES); + + // new ECDsaOpenSsl() / (ECCurve) / (int) / (IntPtr) / (SafeEvpPKeyHandle) — OpenSSL-backed + // implementation. Uses withAnyParameters() to avoid double-detection that would occur if + // separate rules were added per overload (see AES_CNG_NAMED in DotNetAES.java). + private static final IDetectionRule ECDSA_OPENSSL = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("ECDsaOpenSsl") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("ECDSA")) + .withAnyParameters() .buildForContext(new KeyContext(Map.of("kind", "ECDSA"))) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(ECDSA_DEPENDING_RULES); @Nonnull public static List> rules() { - return List.of(ECDSA_CREATE, ECDSA_CNG); + return List.of(ECDSA_CREATE, ECDSA_CNG, ECDSA_OPENSSL); } } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetHMAC.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetHMAC.java index 073cfdf08..d1619ec27 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetHMAC.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetHMAC.java @@ -28,11 +28,56 @@ import javax.annotation.Nonnull; /** - * Detection rules for HMAC algorithms in System.Security.Cryptography. + * Detection rules for the HMAC family, and for {@code MACTripleDES}, in {@code + * System.Security.Cryptography}. * - *

Detects constructor calls for all concrete HMAC implementations. The detected value encodes - * the full class name (e.g., {@code "HMACSHA256"}) so the translator can resolve the inner hash. + *

Classes covered: + * + *

    + *
  • {@code HMACMD5}, {@code HMACSHA1}, {@code HMACSHA256}, {@code HMACSHA384}, {@code + * HMACSHA512} — standard HMAC implementations, available across all .NET versions. + *
  • {@code HMACRIPEMD160} — like {@code RIPEMD160}/{@code RIPEMD160Managed} (see {@link + * DotNetSHA}), this class only ever existed in .NET Framework (documented up to + * netframework-4.8.1 only; no netcoreapp/net5+ monikers). It is detected here for legacy .NET + * Framework source code. + *
  • {@code HMACSHA3_256}, {@code HMACSHA3_384}, {@code HMACSHA3_512} — introduced in .NET 8, + * same platform-dependent status ({@code IsSupported}, intentionally not modeled — consistent + * with how other {@code IsSupported} properties are ignored across this rule set) as their + * {@code SHA3_*} digest counterparts in {@link DotNetSHA3}. + *
  • {@code MACTripleDES} — not an {@code HMAC} subclass. Per the official API reference, + * it derives directly from {@code KeyedHashAlgorithm} (the same base class {@code HMAC} + * itself derives from) and computes a TripleDES/CBC-keyed MAC, not a hash-based one. It only + * ever existed in .NET Framework (documented up to netframework-4.8.1 only), same legacy + * status as {@code HMACRIPEMD160}. It is included in this file because it is part of the .NET + * "keyed hash"/MAC surface that this batch covers, not because it shares an implementation + * with HMAC. + *
+ * + *

The detected value encodes the full class name (e.g., {@code "HMACSHA256"}) so the translator + * ({@code CSharpMacContextTranslator}) can resolve the inner hash algorithm. {@code MACTripleDES} + * is translated separately, by reusing the existing {@code DESede} algorithm model reinterpreted + * "as" a {@code Mac} kind — the same idiom already used elsewhere in this codebase to reinterpret + * {@code DESede} as a {@code KeyWrap} (see {@code JcaCipherMapper}/{@code BcWrapperMapper}, and the + * {@code DESede(Class, DESede)} "as-kind" constructor). Note that {@code MACTripleDES} is a plain + * TripleDES-keyed CBC-MAC construction, which is a distinct (and cryptographically weaker) + * construction from the NIST {@code CMAC}/OMAC1 standard also present in this codebase's model — it + * is therefore intentionally not modeled as {@code CMAC}. + * + *

Known gap — the {@code Key} property setter: none of these classes' {@code Key} + * property (inherited from {@code HMAC}/{@code KeyedHashAlgorithm}) is modeled as a depending rule + * here. A grep across every {@code DotNet*.java} rule file in this module found no existing + * precedent for capturing a raw {@code byte[]} property value: unlike {@code Aes.KeySize} (an + * {@code int} literal, capturable via {@code KeySizeFactory}, see {@link DotNetAES}), {@code + * hmac.Key} is virtually always assigned from a variable in realistic code (e.g. {@code hmac.Key = + * keyBytes;}), and per the engine's documented limitations ({@code CSharpSymbol}/{@code + * CSharpTreeConverter}), only literals and bare identifiers are read — the byte contents/length of + * a variable cannot be resolved this way. Recording only that "a key was set" (with no value) would + * require inventing a new marker/translation with no established counterpart elsewhere in this + * codebase, so this is left as a documented, explicit gap (TODO) rather than a guessed modeling + * decision. */ +// TODO: the `Key` property setter (`hmac.Key = ...`) is not modeled as a depending rule — see +// class javadoc "Known gap" section above for the rationale. @SuppressWarnings("java:S1192") public final class DotNetHMAC { @@ -52,6 +97,21 @@ private static IDetectionRule hmacRule(String className) { .withDependingDetectionRules(List.of()); } + // new MACTripleDES() / new MACTripleDES(byte[] key) / new MACTripleDES(string algName, byte[] + // key) + // Not an HMAC subclass (see class javadoc); modeled as a distinct MAC value, resolved to + // DESede-as-Mac by CSharpMacContextTranslator. + private static final IDetectionRule MAC_TRIPLE_DES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MACTripleDES") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("MACTRIPLEDES")) + .withAnyParameters() + .buildForContext(new MacContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + @Nonnull public static List> rules() { return List.of( @@ -59,6 +119,11 @@ public static List> rules() { hmacRule("HMACSHA256"), hmacRule("HMACSHA384"), hmacRule("HMACSHA512"), - hmacRule("HMACMD5")); + hmacRule("HMACMD5"), + hmacRule("HMACRIPEMD160"), + hmacRule("HMACSHA3_256"), + hmacRule("HMACSHA3_384"), + hmacRule("HMACSHA3_512"), + MAC_TRIPLE_DES); } } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKMAC.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKMAC.java new file mode 100644 index 000000000..0a6a9f6f3 --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKMAC.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.dotnet; + +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.context.MacContext; +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 javax.annotation.Nonnull; + +/** + * Detection rules for the KMAC family in {@code System.Security.Cryptography}. + * + *

Classes covered (a recent addition to .NET, specified by NIST SP 800-185; documented for the + * net-9.0/net-10.0/net-11.0 monikers — verified against the official API reference, see {@code + * IsSupported}, a platform-availability check intentionally not modeled here, consistent with how + * other {@code IsSupported} properties are ignored across this rule set): + * + *

    + *
  • {@code Kmac128} — fixed-output KMAC128 MAC + *
  • {@code Kmac256} — fixed-output KMAC256 MAC + *
  • {@code KmacXof128} — extendable-output (XOF) variant of KMAC128 + *
  • {@code KmacXof256} — extendable-output (XOF) variant of KMAC256 + *
+ * + *

All four classes are sealed, implement {@code IDisposable}, and share the identical + * constructor shape: {@code Kmac128(byte[] key, byte[]? customizationString = default)} (plus a + * {@code ReadOnlySpan} overload with the same parameter roles). Both the {@code key} and + * {@code customizationString} arguments are matched with {@code .withAnyParameters()}: per the + * engine's documented limitations ({@code CSharpLanguageTranslation} — no parameter-type checking; + * {@code CSharpTreeConverter} — only bare literals/identifiers are read), a {@code byte[]} key + * argument is virtually always a variable reference in realistic code and its contents/length + * cannot be resolved this way. This mirrors the exact precedent set for the {@code HMAC*} family in + * {@link DotNetHMAC} (see that class's "Known gap" javadoc for the equivalent {@code Key} property + * case). + * + *

Mapper reuse: all four classes translate to the existing {@code + * com.ibm.mapper.model.algorithms.KMAC} model class (already used by the BouncyCastle {@code KMAC} + * mapper — see {@code BcMacMapper}/{@code BcDigestMapper}), via its {@code KMAC(int + * parameterSetIdentifier, DetectionLocation)} constructor: {@code KMAC(128, ...)} for {@code + * Kmac128}/{@code KmacXof128}, {@code KMAC(256, ...)} for {@code Kmac256}/{@code KmacXof256}. No + * new mapper model class was introduced for this batch. + * + *

Known modeling gap — fixed vs. XOF variant collapse: the existing {@code KMAC} mapper + * model has no concept distinguishing the fixed-output KMAC128/256 construction (domain-separated + * internally with {@code right_encode(L)}, for a specific requested output length {@code L}) from + * the true extendable-output KMACXOF128/256 construction (domain-separated with {@code + * right_encode(0)}, per NIST SP 800-185) — its constructor always wraps a plain {@code CSHAKE} + * child regardless of which .NET class triggered detection. Consequently {@code Kmac128} and {@code + * KmacXof128} both translate to an identical node ({@code asString() == "KMAC128"}), and likewise + * for the 256-bit pair. The raw detected value string ({@code "KMAC128"} vs. {@code "KMACXOF128"}, + * i.e. {@link com.ibm.engine.model.ValueAction#asString()} before translation) still distinguishes + * the four .NET classes at the detection-store level, so this is a translation-layer precision gap, + * not a detection gap — it is documented here rather than fixed by inventing a new mapper model + * class. See {@code CSharpMacContextTranslator} for the translation switch. + * + *

Why the output-length parameter is not modeled as a depending rule: like {@code + * Shake128}/{@code Shake256} in {@link DotNetSHA3}, all four KMAC classes expose {@code + * GetHashAndReset(int outputLength)}/{@code GetCurrentHash(int outputLength)}/{@code HashData(..., + * int outputLength, ...)} overloads (in addition to span-based overloads) whose length argument is + * an arbitrary per-call output-size request, not additional information about the algorithm itself + * — the "128"/"256" in the class name is the security-strength parameter, already captured by the + * creation rule. No depending rules are attached for these methods, or for {@code + * AppendData}/{@code Verify}/{@code VerifyCurrentHash}/{@code VerifyHashAndReset}/{@code Clone}, + * consistent with the same convention already established for {@code Shake128}/{@code Shake256} and + * the {@code HMAC*} family (see {@link DotNetHMAC}), where no depending rules are attached for the + * equivalent hash-computation methods either. + */ +@SuppressWarnings("java:S1192") +public final class DotNetKMAC { + + private DotNetKMAC() { + // nothing + } + + // new Kmac128(key) / new Kmac128(key, customizationString) — and the KmacXof128/256, Kmac256 + // siblings. Both constructor overloads (byte[], byte[]) and (ReadOnlySpan, + // ReadOnlySpan) are covered by withAnyParameters(). + private static IDetectionRule kmacRule(String className) { + return new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(className) + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>(className.toUpperCase())) + .withAnyParameters() + .buildForContext(new MacContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + } + + @Nonnull + public static List> rules() { + return List.of( + kmacRule("Kmac128"), + kmacRule("Kmac256"), + kmacRule("KmacXof128"), + kmacRule("KmacXof256")); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivation.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivation.java new file mode 100644 index 000000000..f8eccb88f --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivation.java @@ -0,0 +1,250 @@ +/* + * 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.detection.MethodMatcher; +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 the KDF (key derivation function) family in System.Security.Cryptography, + * excluding {@code Rfc2898DeriveBytes} (covered separately in {@link DotNetRfc2898DeriveBytes}). + * + *

Classes covered: + * + *

    + *
  • {@code HKDF} — RFC 5869 HMAC-based Extract-and-Expand KDF. Static-only class (no + * constructor / no instance): {@code HKDF.Extract(...)}, {@code HKDF.Expand(...)}, {@code + * HKDF.DeriveKey(...)} (each with a {@code byte[]} and a {@code Span}-based overload). + *
  • {@code SP800108HmacCounterKdf} — NIST SP 800-108 HMAC counter-mode KDF (KBKDF). Both + * constructor-based ({@code new SP800108HmacCounterKdf(key, hashAlgorithm)} + instance-method + * {@code DeriveKey}/{@code DeriveBytes}) and a fully static one-shot overload ({@code + * SP800108HmacCounterKdf.DeriveBytes(key, hashAlgorithm, label, context, length)} — no + * instance required). + *
  • {@code PasswordDeriveBytes} — legacy PBKDF1 extension (per the official API reference: + * "This class uses an extension of the PBKDF1 algorithm"). Constructor-based, with instance + * methods {@code GetBytes(int)} (marked {@code Obsolete} since .NET Core, still detectable + * source) and {@code CryptDeriveKey(string, string, int, byte[])}. + *
+ * + *

Modeling decision — why HKDF/SP800108's static overloads are top-level rules, not depending + * rules: the Batch 3 pattern established in {@link DotNetECDiffieHellman} (a creation rule for + * the object, with derive-operations as depending rules attached to it, translated to the generic + * {@code KeyDerivation} functionality node) assumes there is an object instance to track between + * "creation" and "operation". {@code HKDF} has no such instance — every method is {@code static}, + * and a single static call (e.g. {@code HKDF.DeriveKey(...)}) is both the "creation" and the + * "operation" at once, with nothing to chain afterwards. The same is true for {@code + * SP800108HmacCounterKdf.DeriveBytes(...)}, which is static and self-contained (unlike its sibling + * instance methods). For these static, self-contained calls this file follows the {@code + * Rfc2898DeriveBytes} idiom instead: the call is captured directly as a top-level {@code + * ValueActionFactory} mapping straight to the KDF algorithm model node ({@code HKDF} / {@code + * KDFCounter}), exactly like {@code new Rfc2898DeriveBytes(...)} maps directly to {@code PBKDF2} + * with no intermediate {@code KeyDerivation} functionality wrapper — because the class itself *is* + * the KDF, unlike {@code ECDiffieHellman} which is a general-purpose key-agreement primitive that + * only sometimes derives a key. + * + *

Where an actual object instance *is* tracked ({@code SP800108HmacCounterKdf}'s constructor + + * instance {@code DeriveKey}, and {@code PasswordDeriveBytes}'s constructor + instance {@code + * GetBytes}/{@code CryptDeriveKey}), the Batch 3 pattern is reused as instructed: each derive + * operation is a depending rule capturing {@code ValueActionFactory<>()} under its own + * {@code KeyContext} "kind", dispatched in {@code CSharpKeyContextTranslator} to the generic {@code + * KeyDerivation} functionality node — mirroring {@code AES_GENERATE_KEY}/{@code AES_GENERATE_IV} in + * {@link DotNetAES} (an operation captured as a child of an already-identified algorithm) more + * closely than the ECDH case, since here the parent node is already a concrete KDF algorithm, not + * an ambiguous key-agreement primitive. + * + *

As with every other file in this rule set, the ANTLR4-based C# engine cannot resolve parameter + * types (see {@code CSharpLanguageTranslation}), so all overloads that only differ by {@code + * byte[]} vs. {@code ReadOnlySpan}/{@code Span}, {@code string} vs. {@code + * ReadOnlySpan}, or the presence of an output-buffer parameter are collapsed into a single + * {@code withAnyParameters()} rule per method name. The {@code HashAlgorithmName} parameter that + * several of these methods take (e.g. {@code HashAlgorithmName.SHA256}) is not decoded into a + * digest child node — consistent with {@code DotNetECDsa}/{@code DotNetECDiffieHellman}, which + * leave the same parameter opaque on {@code SignData}/{@code DeriveKeyFromHash}, since only + * literals and bare identifiers are readable by the engine (see {@code CSharpTreeConverter}), not + * static member-access expressions like {@code HashAlgorithmName.SHA256}. + * + *

Known gap — {@code SP800108HmacCounterKdf.DeriveBytes} (instance overload): only the + * instance {@code DeriveKey} overloads are modeled as depending rules; the instance {@code + * DeriveBytes} overloads (which take the label/context/hash-algorithm again, redundantly with the + * constructor) exist per the API reference but are not separately covered here — {@code DeriveKey} + * already exercises the same depending-rule path and this avoids an unnecessary near-duplicate rule + * for a method that behaves identically for detection purposes (an opaque {@code + * withAnyParameters()} call site). Not a gap in coverage of the class's cryptographic identity or + * of the "a key was derived" signal — only a granularity choice, called out here rather than + * decided silently. + */ +@SuppressWarnings("java:S1192") +public final class DotNetKeyDerivation { + + private DotNetKeyDerivation() { + // nothing + } + + // ========================================================================= + // HKDF — static-only class, no instance. Each static method is its own + // top-level rule (see class javadoc "Modeling decision"). + // ========================================================================= + + // HKDF.Extract(hashAlgorithmName, ikm, salt) / Extract(hashAlgorithmName, ikm, salt, prk) + private static final IDetectionRule HKDF_EXTRACT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("HKDF") + .forMethods("Extract") + .shouldBeDetectedAs(new ValueActionFactory<>("HKDF")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_HKDF"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // HKDF.Expand(hashAlgorithmName, prk, outputLength, info) / Expand(..., output, info) + private static final IDetectionRule HKDF_EXPAND = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("HKDF") + .forMethods("Expand") + .shouldBeDetectedAs(new ValueActionFactory<>("HKDF")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_HKDF"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // HKDF.DeriveKey(hashAlgorithmName, ikm, outputLength, salt, info) — full Extract+Expand + private static final IDetectionRule HKDF_DERIVE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("HKDF") + .forMethods("DeriveKey") + .shouldBeDetectedAs(new ValueActionFactory<>("HKDF")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_HKDF"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // SP800108HmacCounterKdf — instance derive-operation depending rules, reusing the Batch 3 + // KeyDerivation pattern (see class javadoc). + // ========================================================================= + + // kdf.DeriveKey(label, context, keyLengthBytes) — instance method, all overloads + // (byte[]/ReadOnlySpan/ReadOnlySpan/string label+context, Int32/Span output) + private static final IDetectionRule SP800108_DERIVE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DeriveKey") + .shouldBeDetectedAs(new ValueActionFactory<>("DeriveKey")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_SP800108_DERIVE_KEY"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> SP800108_DEPENDING_RULES = + List.of(SP800108_DERIVE_KEY); + + // new SP800108HmacCounterKdf(key, hashAlgorithmName) — byte[] or ReadOnlySpan key + private static final IDetectionRule SP800108_CTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SP800108HmacCounterKdf") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SP800108")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_SP800108"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(SP800108_DEPENDING_RULES); + + // SP800108HmacCounterKdf.DeriveBytes(key, hashAlgorithmName, label, context, keyLengthBytes) — + // fully static one-shot overload, no instance required (see class javadoc "Modeling decision"). + private static final IDetectionRule SP800108_STATIC_DERIVE_BYTES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SP800108HmacCounterKdf") + .forMethods("DeriveBytes") + .shouldBeDetectedAs(new ValueActionFactory<>("SP800108")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_SP800108"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // PasswordDeriveBytes — legacy PBKDF1 extension. Constructor-based, with instance + // derive-operation depending rules reusing the Batch 3 KeyDerivation pattern. + // ========================================================================= + + // pdb.GetBytes(cb) — Obsolete since .NET Core, still valid/detectable legacy source + private static final IDetectionRule PDB_GET_BYTES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GetBytes") + .shouldBeDetectedAs(new ValueActionFactory<>("GetBytes")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_PDB_GET_BYTES"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // pdb.CryptDeriveKey(algName, algHashName, keySize, rgbIV) + private static final IDetectionRule PDB_CRYPT_DERIVE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CryptDeriveKey") + .shouldBeDetectedAs(new ValueActionFactory<>("CryptDeriveKey")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_PDB_CRYPT_DERIVE_KEY"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> PDB_DEPENDING_RULES = + List.of(PDB_GET_BYTES, PDB_CRYPT_DERIVE_KEY); + + // new PasswordDeriveBytes(password, salt[, hashName, iterations][, cspParams]) — 8 + // constructor overloads (password as string or byte[]; optional hashName/iterations; + // optional CspParameters), all collapsed via withAnyParameters(). + private static final IDetectionRule PASSWORD_DERIVE_BYTES_CTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("PasswordDeriveBytes") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF1")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_PASSWORD_DERIVE_BYTES"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(PDB_DEPENDING_RULES); + + @Nonnull + public static List> rules() { + return List.of( + HKDF_EXTRACT, + HKDF_EXPAND, + HKDF_DERIVE_KEY, + SP800108_CTOR, + SP800108_STATIC_DERIVE_BYTES, + PASSWORD_DERIVE_BYTES_CTOR); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetLegacyFormatters.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetLegacyFormatters.java new file mode 100644 index 000000000..398702487 --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetLegacyFormatters.java @@ -0,0 +1,450 @@ +/* + * 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.detection.MethodMatcher; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.PaddingFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; +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 the legacy Formatter/Deformatter and mask-generation classes in {@code + * System.Security.Cryptography}. + * + *

Classes covered (all nine, all still present through net-11.0 per the official API reference — + * none were removed in modern .NET): + * + *

    + *
  • {@code DSASignatureFormatter} / {@code DSASignatureDeformatter} — create/verify a DSA + * signature from a caller-supplied hash + *
  • {@code RSAPKCS1SignatureFormatter} / {@code RSAPKCS1SignatureDeformatter} — create/verify + * an RSA PKCS #1 v1.5 signature from a caller-supplied hash + *
  • {@code RSAOAEPKeyExchangeFormatter} / {@code RSAOAEPKeyExchangeDeformatter} — + * encrypt/decrypt a symmetric key using RSA-OAEP ("key exchange") + *
  • {@code RSAPKCS1KeyExchangeFormatter} / {@code RSAPKCS1KeyExchangeDeformatter} — + * encrypt/decrypt a symmetric key using RSA PKCS #1 v1.5 ("key exchange") + *
  • {@code PKCS1MaskGenerationMethod} — the PKCS #1 (MGF1) mask generation function used + * internally by OAEP implementations + *
+ * + *

Architecture: each Formatter/Deformatter constructor is modeled exactly like the primary + * creation rules in {@link DotNetDSA} / {@link DotNetRSA} — a top-level {@link ValueActionFactory} + * of {@code "DSA"} or {@code "RSA"} under {@link KeyContext}{@code (kind=DSA/RSA)}, which is + * already translated by the existing {@code CSharpKeyContextTranslator} switch (no translator + * changes needed for those two). The class's two constructors ({@code ()} and {@code + * (AsymmetricAlgorithm)}) are collapsed into a single {@code withAnyParameters()} rule, mirroring + * {@code DSA_CNG}/{@code RSA_CNG} in the referenced files. + * + *

{@code CreateSignature}/{@code VerifySignature} are modeled as depending rules using + * {@link SignatureActionFactory} ({@code SIGN}/{@code VERIFY}) under {@link SignatureContext}, with + * {@code withAnyParameters()} — deliberately not attempting to also capture the PKCS #1 v1.5 + * padding implied by the RSA classes' names, mirroring {@link DotNetRSA}'s own {@code + * RSA_SIGN_DATA}/{@code RSA_VERIFY_DATA} rules, which likewise never capture the {@code + * RSASignaturePadding} parameter of the modern {@code RSA.SignData}/{@code VerifyData} API. There + * is also no existing precedent anywhere in this codebase (Java or C#) for attaching a {@code + * Padding} value under a {@code SignatureContext} detection, so doing so here would require + * inventing new translation code for a single, low-priority call site. + * + *

{@code SetHashAlgorithm(string)} (on all four Signature Formatter/Deformatter classes) + * takes a literal hash-algorithm name (e.g. {@code "SHA1"}, {@code "SHA256"}) — unlike most + * parameters elsewhere in this module, this one is directly readable by the ANTLR4-based engine + * (see {@code CSharpTreeConverter}, which resolves string literals). It is captured with the + * existing {@link AlgorithmFactory} under {@link DigestContext}, reusing the {@code Algorithm} + * branch already present in {@code CSharpDigestContextTranslator} (no translator changes needed). + * + *

{@code CreateKeyExchange}/{@code DecryptKeyExchange} (on the four KeyExchange + * Formatter/Deformatter classes) are modeled as {@link CipherActionFactory} ({@code + * CipherAction.Action.ENCRYPT}/{@code DECRYPT}) under {@link CipherContext}, with a constant {@link + * PaddingFactory} ({@code "OAEP"} or {@code "PKCS1Padding"}) attached to the first parameter — + * exactly the same "constant value keyed off which rule/method matched" pattern {@link DotNetAES} + * uses for {@code ModeFactory<>("CBC")} on {@code EncryptCbc}/{@code DecryptCbc}. Both padding + * strings are already understood by the existing {@code JcaPaddingMapper} reused via {@code + * CSharpCipherContextTranslator}'s {@code Padding} branch ({@code "OAEP"} routes to {@code + * JcaOAEPPaddingMapper}, {@code "PKCS1Padding"} matches its {@code PKCS1} case exactly), so no + * translator changes were needed for this either. Unlike the signature padding case above, this is + * not blocked by the "no parameter value resolution" engine limitation at all: the padding scheme + * here is a compile-time fact of which concrete class was instantiated (its class name), not a + * runtime argument that would need to be resolved. + * + *

Open design question — {@code CipherAction.Action.WRAP} vs. {@code ENCRYPT}/{@code DECRYPT} + * for key exchange: the engine's {@code CipherAction.Action} enum already defines {@code WRAP}, + * used by {@code JcaCipherWrap} (Java, {@code Cipher.WRAP_MODE}) and {@code PycaWrapping} (Python). + * However, its translation is not consistent between those two: {@code + * JavaCipherContextTranslator} maps {@code WRAP} to the generic {@code Encapsulate} functionality, + * while {@code PycaCipherContextTranslator} maps it to a {@code KeyWrap}-typed algorithm variant + * instead of an operation node at all. {@code CSharpCipherContextTranslator} does not handle {@code + * WRAP} at present (falls through to {@code default -> Optional.empty()}). Given this pre-existing + * inconsistency, this file deliberately uses the already-wired {@code ENCRYPT}/{@code DECRYPT} + * actions instead — which also matches the fact that both {@code RSAOAEPKeyExchangeFormatter} and + * {@code RSAPKCS1KeyExchangeFormatter}'s reference-source implementations literally delegate to + * {@code RSA.Encrypt}/{@code RSA.Decrypt} internally, the exact same operation {@link DotNetRSA}'s + * own {@code RSA_ENCRYPT}/{@code RSA_DECRYPT} rules already model with {@code ENCRYPT}/{@code + * DECRYPT}. Should a unified cross-language "key wrap" CBOM concept ever be settled on, these four + * rules (the {@code CREATE_KEY_EXCHANGE_*}/{@code DECRYPT_KEY_EXCHANGE_*} constants below) are the + * ones to revisit. + * + *

{@code PKCS1MaskGenerationMethod} reuses the existing MGF1 concept already present in + * the mapper model ({@code mapper/model/algorithms/MGF1.java}, implementing {@code + * MaskGenerationFunction} — already used by {@code JcaMGFMapper}/{@code JcaOAEPPaddingMapper} for + * JCA's OAEP padding translation), rather than inventing a new one. Its sole constructor is + * captured as {@code ValueActionFactory<>("MGF1")} under {@code KeyContext(kind=MGF1)} — reusing + * {@link KeyContext} as the generic "identify which standalone algorithm was just constructed" + * context already used in this module for RSA/DSA/ECDSA/ECDH/X25519/ML-KEM/ML-DSA/SLH-DSA/KDF + * variants (see {@code CSharpKeyContextTranslator}), not because MGF1 is a "key" algorithm in the + * strict sense. This required one small additive case in {@code + * CSharpKeyContextTranslator}'s existing kind switch: {@code case "MGF1" -> Optional.of(new + * MGF1(detectionLocation))}. Its {@code HashName} property (compiles to a synthetic {@code + * set_HashName(string)} call, see {@code CSharpTreeConverter}) is captured with the same {@link + * AlgorithmFactory}/{@link DigestContext} pattern as {@code SetHashAlgorithm} above, producing a + * digest child of the MGF1 node. Per the official reference, "This class is only used by + * implementations of key exchange algorithms for mask generation. Application code does not use + * this class directly" — so {@code GenerateMask(byte[], int)} is intentionally not modeled + * as a depending rule: like {@code DotNetSHA}'s {@code ComputeHash} (see that class's own javadoc), + * invoking it adds no cryptographic information beyond what the constructor already captured (the + * mask-generation algorithm is always MGF1; only the hash feeding it, already captured via {@code + * HashName}, varies). + * + *

Known gap — {@code SetKey(AsymmetricAlgorithm)}: present on all eight + * Formatter/Deformatter classes, this method is typically called with a variable holding a + * previously created {@code DSA}/{@code RSA} instance (e.g. {@code formatter.SetKey(dsa)} where + * {@code dsa} came from {@code DSA.Create()} in an earlier statement). Per the engine's documented + * limitations ({@code CSharpSymbol} — "Full symbol tracking across scopes is not supported"; {@code + * CSharpLanguageTranslation} — no parameter type resolution), the concrete key instance behind such + * a variable cannot be resolved and linked to this call. This is a known, unavoidable gap: {@code + * SetKey} is therefore intentionally not modeled as a detection rule here (no rule would + * ever usefully fire), rather than faking a value. The Formatter/Deformatter class itself is still + * fully detected in isolation via its constructor (which already identifies the algorithm family — + * DSA or RSA — from the class name alone, independent of {@code SetKey}). The same reasoning + * applies to the non-crypto-identity-bearing {@code Rng}/{@code Parameter}/{@code Parameters} + * properties on the KeyExchange formatters ({@code Rng} in particular would need to resolve a + * {@code new SomeRng()} expression assigned via a property setter, which {@code + * CSharpTreeConverter}'s {@code convertPrimaryExpressionStart} does not handle for object-creation + * right-hand sides — only literals and bare identifiers), so they are likewise left undetected. + */ +@SuppressWarnings("java:S1192") +public final class DotNetLegacyFormatters { + + private DotNetLegacyFormatters() { + // nothing + } + + // ========================================================================= + // Shared depending rules: signature operations (DSA + RSA PKCS#1 formatters) + // ========================================================================= + + // formatter.CreateSignature(hash) — byte[] override or inherited HashAlgorithm override, + // indistinguishable to this engine (see class javadoc: no padding capture, mirrors DotNetRSA). + private static final IDetectionRule CREATE_SIGNATURE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateSignature") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // deformatter.VerifySignature(hash, signature) — byte[] or inherited HashAlgorithm override. + private static final IDetectionRule VERIFY_SIGNATURE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifySignature") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // (de)formatter.SetHashAlgorithm("SHA256") — literal string, shared across all four + // Signature Formatter/Deformatter classes (same method name and semantics on each). + private static final IDetectionRule SET_HASH_ALGORITHM = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SetHashAlgorithm") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> SIGN_DEPENDING_RULES = + List.of(CREATE_SIGNATURE, SET_HASH_ALGORITHM); + + private static final List> VERIFY_DEPENDING_RULES = + List.of(VERIFY_SIGNATURE, SET_HASH_ALGORITHM); + + // ========================================================================= + // Depending rules: RSA key exchange operations (OAEP and PKCS#1 formatters) + // Constant padding value attached from which rule/arity matched (class name), not from a + // resolved parameter value — see class javadoc. + // ========================================================================= + + // oaepFormatter.CreateKeyExchange(rgbData) + private static final IDetectionRule CREATE_KEY_EXCHANGE_OAEP_1 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateKeyExchange") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withMethodParameter(MethodMatcher.ANY) // rgbData (symmetric key material) + .shouldBeDetectedAs(new PaddingFactory<>("OAEP")) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // oaepFormatter.CreateKeyExchange(rgbData, symAlgType) + private static final IDetectionRule CREATE_KEY_EXCHANGE_OAEP_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateKeyExchange") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withMethodParameter(MethodMatcher.ANY) // rgbData + .shouldBeDetectedAs(new PaddingFactory<>("OAEP")) + .withMethodParameter(MethodMatcher.ANY) // symAlgType + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // oaepDeformatter.DecryptKeyExchange(rgbData) — single overload per the official reference. + private static final IDetectionRule DECRYPT_KEY_EXCHANGE_OAEP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptKeyExchange") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withMethodParameter(MethodMatcher.ANY) // rgbIn + .shouldBeDetectedAs(new PaddingFactory<>("OAEP")) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // pkcs1Formatter.CreateKeyExchange(rgbData) + private static final IDetectionRule CREATE_KEY_EXCHANGE_PKCS1_1 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateKeyExchange") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withMethodParameter(MethodMatcher.ANY) // rgbData + .shouldBeDetectedAs(new PaddingFactory<>("PKCS1Padding")) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // pkcs1Formatter.CreateKeyExchange(rgbData, symAlgType) + private static final IDetectionRule CREATE_KEY_EXCHANGE_PKCS1_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateKeyExchange") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withMethodParameter(MethodMatcher.ANY) // rgbData + .shouldBeDetectedAs(new PaddingFactory<>("PKCS1Padding")) + .withMethodParameter(MethodMatcher.ANY) // symAlgType + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // pkcs1Deformatter.DecryptKeyExchange(rgbIn) — single overload. + private static final IDetectionRule DECRYPT_KEY_EXCHANGE_PKCS1 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptKeyExchange") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withMethodParameter(MethodMatcher.ANY) // rgbIn + .shouldBeDetectedAs(new PaddingFactory<>("PKCS1Padding")) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> OAEP_FORMATTER_DEPENDING_RULES = + List.of(CREATE_KEY_EXCHANGE_OAEP_1, CREATE_KEY_EXCHANGE_OAEP_2); + + private static final List> OAEP_DEFORMATTER_DEPENDING_RULES = + List.of(DECRYPT_KEY_EXCHANGE_OAEP); + + private static final List> PKCS1_KX_FORMATTER_DEPENDING_RULES = + List.of(CREATE_KEY_EXCHANGE_PKCS1_1, CREATE_KEY_EXCHANGE_PKCS1_2); + + private static final List> PKCS1_KX_DEFORMATTER_DEPENDING_RULES = + List.of(DECRYPT_KEY_EXCHANGE_PKCS1); + + // ========================================================================= + // Depending rule: PKCS1MaskGenerationMethod.HashName property setter + // ========================================================================= + + // mgf.HashName = "SHA256" → synthetic set_HashName("SHA256") + private static final IDetectionRule MGF1_SET_HASH_NAME = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_HashName") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Primary creation rules + // ========================================================================= + + // new DSASignatureFormatter() / new DSASignatureFormatter(AsymmetricAlgorithm) + private static final IDetectionRule DSA_SIGNATURE_FORMATTER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("DSASignatureFormatter") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("DSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "DSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(SIGN_DEPENDING_RULES); + + // new DSASignatureDeformatter() / new DSASignatureDeformatter(AsymmetricAlgorithm) + private static final IDetectionRule DSA_SIGNATURE_DEFORMATTER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("DSASignatureDeformatter") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("DSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "DSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(VERIFY_DEPENDING_RULES); + + // new RSAPKCS1SignatureFormatter() / new RSAPKCS1SignatureFormatter(AsymmetricAlgorithm) + private static final IDetectionRule RSA_PKCS1_SIGNATURE_FORMATTER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RSAPKCS1SignatureFormatter") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "RSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(SIGN_DEPENDING_RULES); + + // new RSAPKCS1SignatureDeformatter() / new RSAPKCS1SignatureDeformatter(AsymmetricAlgorithm) + private static final IDetectionRule RSA_PKCS1_SIGNATURE_DEFORMATTER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RSAPKCS1SignatureDeformatter") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "RSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(VERIFY_DEPENDING_RULES); + + // new RSAOAEPKeyExchangeFormatter() / new RSAOAEPKeyExchangeFormatter(AsymmetricAlgorithm) + private static final IDetectionRule RSA_OAEP_KEY_EXCHANGE_FORMATTER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RSAOAEPKeyExchangeFormatter") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "RSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(OAEP_FORMATTER_DEPENDING_RULES); + + // new RSAOAEPKeyExchangeDeformatter() / new RSAOAEPKeyExchangeDeformatter(AsymmetricAlgorithm) + private static final IDetectionRule RSA_OAEP_KEY_EXCHANGE_DEFORMATTER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RSAOAEPKeyExchangeDeformatter") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "RSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(OAEP_DEFORMATTER_DEPENDING_RULES); + + // new RSAPKCS1KeyExchangeFormatter() / new RSAPKCS1KeyExchangeFormatter(AsymmetricAlgorithm) + private static final IDetectionRule RSA_PKCS1_KEY_EXCHANGE_FORMATTER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RSAPKCS1KeyExchangeFormatter") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "RSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(PKCS1_KX_FORMATTER_DEPENDING_RULES); + + // new RSAPKCS1KeyExchangeDeformatter() / new + // RSAPKCS1KeyExchangeDeformatter(AsymmetricAlgorithm) + private static final IDetectionRule RSA_PKCS1_KEY_EXCHANGE_DEFORMATTER = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RSAPKCS1KeyExchangeDeformatter") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "RSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(PKCS1_KX_DEFORMATTER_DEPENDING_RULES); + + // new PKCS1MaskGenerationMethod() — single, parameterless constructor. + private static final IDetectionRule PKCS1_MASK_GENERATION_METHOD = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("PKCS1MaskGenerationMethod") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("MGF1")) + .withoutParameters() + .buildForContext(new KeyContext(Map.of("kind", "MGF1"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of(MGF1_SET_HASH_NAME)); + + @Nonnull + public static List> rules() { + return List.of( + DSA_SIGNATURE_FORMATTER, + DSA_SIGNATURE_DEFORMATTER, + RSA_PKCS1_SIGNATURE_FORMATTER, + RSA_PKCS1_SIGNATURE_DEFORMATTER, + RSA_OAEP_KEY_EXCHANGE_FORMATTER, + RSA_OAEP_KEY_EXCHANGE_DEFORMATTER, + RSA_PKCS1_KEY_EXCHANGE_FORMATTER, + RSA_PKCS1_KEY_EXCHANGE_DEFORMATTER, + PKCS1_MASK_GENERATION_METHOD); + } +} 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 000000000..1b07cbfd9 --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsa.java @@ -0,0 +1,555 @@ +/* + * 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.detection.MethodMatcher; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.ParameterIdentifierFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; +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 java.util.stream.Stream; +import javax.annotation.Nonnull; + +/** + * Detection rules for ML-DSA (Module-Lattice-Based Digital Signature Algorithm, FIPS 204) and + * Composite ML-DSA usage in {@code System.Security.Cryptography}. + * + *

API availability (verified against the official Microsoft Learn API reference, not + * assumed): {@code MLDsa}, {@code MLDsaCng}, {@code MLDsaOpenSsl}, {@code MLDsaAlgorithm}, + * {@code CompositeMLDsa}, {@code CompositeMLDsaCng} and {@code CompositeMLDsaAlgorithm} are all + * documented for the {@code net-10.0} and {@code net-11.0} monikers (the {@code net-11.0} page + * redirects/renders identically to {@code net-10.0} content — {@code defaultMoniker: net-10.0} — + * both listing "Assembly: System.Security.Cryptography.dll" as in-box, plus "Assembly: + * Microsoft.Bcl.Cryptography.dll" — package {@code Microsoft.Bcl.Cryptography + * v11.0.0-preview.7.26381.103} — as a back-compat NuGet shim for the non-OpenSSL types on older + * TFMs; {@code MLDsaOpenSsl} lists only the in-box assembly, mirroring {@code MLKemOpenSsl}). The + * plain {@code MLDsa}/{@code MLDsaCng}/{@code MLDsaOpenSsl} surface carries no {@code + * [Experimental]} attribute. {@code CompositeMLDsa}, {@code CompositeMLDsaCng} and {@code + * CompositeMLDsaAlgorithm}, by contrast, are each marked {@code + * [System.Diagnostics.CodeAnalysis.Experimental("SYSLIB5006")]} at the class level — confirming + * Composite ML-DSA specifically (unlike plain ML-DSA) is still a preview API as of .NET 10/11. + * + *

Classes covered: + * + *

    + *
  • {@code MLDsa} — abstract base. All of its members are {@code static} factory methods (the + * only listed constructor, {@code MLDsa(MLDsaAlgorithm)}, is for derived classes, not called + * by ordinary consumer code): {@code GenerateKey(MLDsaAlgorithm)}, {@code + * ImportMLDsaPrivateKey(MLDsaAlgorithm, byte[]/ReadOnlySpan<byte>)}, {@code + * ImportMLDsaPrivateSeed(MLDsaAlgorithm, byte[]/ReadOnlySpan<byte>)}, {@code + * ImportMLDsaPublicKey(MLDsaAlgorithm, byte[]/ReadOnlySpan<byte>)}, {@code + * ImportPkcs8PrivateKey(...)}, {@code ImportSubjectPublicKeyInfo(...)}, {@code + * ImportFromPem(...)}, {@code ImportEncryptedPkcs8PrivateKey(...)}, {@code + * ImportFromEncryptedPem(...)}. + *
  • {@code MLDsaCng} — CNG-backed implementation ({@code MLDsaCng(CngKey)} constructor). + *
  • {@code MLDsaOpenSsl} — OpenSSL-backed implementation ({@code + * MLDsaOpenSsl(SafeEvpPKeyHandle)} constructor). + *
  • {@code CompositeMLDsa} — abstract base for Composite ML-DSA (FIPS 204 ML-DSA combined with + * a traditional algorithm — RSA/ECDSA/Ed25519/Ed448). Structurally mirrors {@code MLDsa}: + * {@code GenerateKey(CompositeMLDsaAlgorithm)}, {@code + * ImportCompositeMLDsaPrivateKey(CompositeMLDsaAlgorithm, byte[]/ReadOnlySpan<byte>)}, + * {@code ImportCompositeMLDsaPublicKey(CompositeMLDsaAlgorithm, + * byte[]/ReadOnlySpan<byte>)}, plus the same {@code ImportPkcs8PrivateKey}/{@code + * ImportSubjectPublicKeyInfo}/{@code ImportFromPem}/{@code + * ImportEncryptedPkcs8PrivateKey}/{@code ImportFromEncryptedPem} structural-import family. + *
  • {@code CompositeMLDsaCng} — CNG-backed implementation of Composite ML-DSA ({@code + * CompositeMLDsaCng(CngKey)} constructor). + *
+ * + *

Architecture — {@code Import*} as primary creation rules, not skipped: exactly as + * established for ML-KEM (see {@code DotNetMLKem}'s javadoc for the full rationale), every {@code + * Import*} method verified above on both {@code MLDsa} and {@code CompositeMLDsa} is a {@code + * static} factory that is the only way (besides {@code GenerateKey} or the {@code Cng}/{@code + * OpenSsl} native-interop constructors) to obtain an instance in the first place — unlike the + * inherited instance {@code Import*}/{@code Export*} members on RSA/ECDsa/ECDiffieHellman, which + * are intentionally not modeled. They are therefore treated as primary creation rules here. + * + *

Two creation-rule shapes result from this, for both {@code MLDsa} and {@code CompositeMLDsa}: + * + *

    + *
  • Algorithm-parameterized ({@code GenerateKey}, {@code ImportMLDsaPrivateKey}, {@code + * ImportMLDsaPrivateSeed}, {@code ImportMLDsaPublicKey} / {@code + * ImportCompositeMLDsaPrivateKey}, {@code ImportCompositeMLDsaPublicKey}): take an {@code + * MLDsaAlgorithm}/{@code CompositeMLDsaAlgorithm} argument (e.g. {@code + * MLDsaAlgorithm.MLDsa65}), a member-access expression the engine resolves to the bare + * identifier {@code "MLDsa65"} (see {@code CSharpLanguageTranslation}, the same mechanism + * {@code DotNetMLKem}'s rules rely on for {@code MLKemAlgorithm}). Captured with {@code + * ParameterIdentifierFactory<>()} as a child of the top-level detection. + *
  • Structural imports ({@code ImportPkcs8PrivateKey}, {@code + * ImportSubjectPublicKeyInfo}, {@code ImportFromPem}, {@code ImportEncryptedPkcs8PrivateKey}, + * {@code ImportFromEncryptedPem}): the parameter set is embedded inside the encoded key + * material / PEM text, not present as a separate literal argument, so — exactly as for ML-KEM + * — it cannot be recovered by this engine. These translate to a generic node with no {@code + * ParameterSetIdentifier} child, a known, inherent precision gap, not a bug. + *
+ * + *

{@code MLDsaCng(CngKey)}, {@code MLDsaOpenSsl(SafeEvpPKeyHandle)} and {@code + * CompositeMLDsaCng(CngKey)} wrap an already-existing native key handle and never receive an + * algorithm argument at all, so they always translate to the generic node, mirroring {@code + * MLKemCng(CngKey)}/{@code MLKemOpenSsl(SafeEvpPKeyHandle)}. + * + *

Sign/Verify modeling: unlike {@code DotNetMLKem} (which models KEM-specific {@code + * Encapsulate}/{@code Decapsulate} via {@code KeyActionFactory}), {@code MLDsa} and {@code + * CompositeMLDsa} are ordinary signature primitives, so their operations are modeled with {@code + * SignatureActionFactory} under a {@code SignatureContext} — the exact same shape as {@code + * DotNetDSA}/{@code DotNetECDsa}'s {@code SignData}/{@code VerifyData} rules, reusing the existing + * generic SIGN/VERIFY dispatch in {@code CSharpSignatureContextTranslator} (no changes needed + * there: that translator already maps {@code SignatureAction.Action.SIGN}/{@code VERIFY} to {@code + * Sign}/{@code Verify} functionality nodes regardless of which algorithm produced the action, so + * this rule set is purely additive from its perspective). {@code MLDsa} additionally exposes {@code + * SignMu}/{@code VerifyMu} (signing a pre-computed FIPS 204 "mu" digest) and {@code + * SignPreHash}/{@code VerifyPreHash} (the FIPS 204 pre-hash variant); both are modeled the same way + * as {@code SignData}/{@code VerifyData} since the engine cannot meaningfully distinguish "what was + * hashed before signing" from "what was signed" without value tracking. {@code CompositeMLDsa} only + * exposes {@code SignData}/{@code VerifyData} (no mu/pre-hash variants per the verified API + * surface). The {@code *Core} overrides ({@code SignDataCore}, {@code SignMuCore}, {@code + * SignPreHashCore}, {@code VerifyDataCore}, {@code VerifyMuCore}, {@code VerifyPreHashCore}) are + * protected extensibility hooks for subclassing, not called by ordinary consumer code, and are + * intentionally not modeled — consistent with not modeling {@code EncapsulateCore}/{@code + * DecapsulateCore} in {@code DotNetMLKem}. + * + *

Mapper model reuse: {@code com.ibm.mapper.model.algorithms.MLDSA} (implementing {@code + * Signature}) already existed before this batch (added alongside {@code MLKEM} — see PR "Add + * support for MLKEM and MLDSA #219") and already exposes the exact {@code MLDSA(int + * parameterSetIdentifier, DetectionLocation)} shape {@code DotNetMLKem} relies on for {@code + * MLKEM}, so {@code asString()} yields {@code "ML-DSA-44"}/{@code "ML-DSA-65"}/{@code "ML-DSA-87"}. + * No new mapper model class was introduced for plain ML-DSA. + * + *

Composite ML-DSA modeling — a deliberate, documented compromise, not a fabrication: a + * repository-wide search ({@code grep -rl "Composite\|Hybrid" mapper/}) found no existing + * "composite"/"hybrid" algorithm concept anywhere in the mapper model — {@code CompositeMLDsa} + * combines a post-quantum algorithm (ML-DSA-44/65/87) with a classical one (e.g. RSA-2048-PSS, + * ECDSA-P256, Ed25519) into a single key, which a properly structured model would represent as two + * linked algorithm nodes. Inventing that two-algorithm "Hybrid"/"Composite" model class from + * scratch was judged out of scope for this batch (it would require new base-class/interface design + * in {@code mapper/model/}, affecting more than this one rule file — see class-level "Not covered" + * note below for the recommendation). However, the detection-rule *shape* for {@code + * CompositeMLDsa} turned out to be structurally identical to plain {@code MLDsa} (same static + * factory / structural-import / Cng-constructor pattern, confirmed from the official reference + * above, not assumed by RSA/ECDsa analogy) — there was therefore no reason to skip detecting it + * entirely. The chosen middle ground: {@code CompositeMLDsa}/{@code CompositeMLDsaCng} creation is + * detected and reuses the existing {@code MLDSA} mapper node (via a distinct {@code + * "MLDSA_COMPOSITE"} {@code KeyContext} kind, dispatched in {@code CSharpKeyContextTranslator}), + * with the full, untranslated {@code CompositeMLDsaAlgorithm} member name (e.g. {@code + * "MLDsa44WithECDsaP256"}, one of the 17 combinations verified from the official {@code + * CompositeMLDsaAlgorithm} reference page — {@code MLDsa44With\{ECDsaP256,Ed25519,RSA2048Pkcs15, + * RSA2048Pss\}}, {@code MLDsa65With\{ECDsaBrainpoolP256r1,ECDsaP256,ECDsaP384,Ed25519, + * RSA3072Pkcs15,RSA3072Pss,RSA4096Pkcs15,RSA4096Pss\}}, {@code + * MLDsa87With\{ECDsaBrainpoolP384r1,ECDsaP384,ECDsaP521,Ed448,RSA3072Pss,RSA4096Pss\}}) captured + * verbatim as the {@code ParameterSetIdentifier} string, rather than parsed into an ML-DSA + * parameter set number plus a separate classical-algorithm node. This preserves 100% of the + * information the engine can see (nothing is dropped or guessed) and yields a distinguishable, + * still-informative {@code asString()} such as {@code "ML-DSA-MLDsa44WithECDsaP256"}, at the cost + * of not exposing the classical component as a structured sibling/child node the way a "true" + * composite/hybrid model would. This is flagged here explicitly, not silently: a follow-up + * introducing a proper composite/hybrid algorithm concept in {@code mapper/model/} could later + * parse this same string into two structured nodes without touching this detection rule file. + * + *

Not covered (deliberately, consistent with the rest of this rule set): the {@code + * Algorithm}/{@code IsSupported} getters and {@code CompositeMLDsa.IsAlgorithmSupported(...)} + * (state reads, not configuration — no property setters exist on {@code MLDsa}/{@code + * CompositeMLDsa} at all), {@code MLDsaCng.GetKey()}/{@code CompositeMLDsaCng.GetKey()}/{@code + * MLDsaOpenSsl.DuplicateKeyHandle()} (native-handle export, the {@code MLDsa}-specific equivalent + * of the {@code Export*} methods skipped for RSA/ECDsa/ECDiffieHellman), {@code Dispose()}, and all + * {@code ExportXxx}/{@code TryExportXxx}/{@code ExportXxxPem} instance methods (standard + * PKCS#8/SPKI/PEM export — same convention as RSA/ECDsa/ECDiffieHellman/MLKem). + */ +@SuppressWarnings("java:S1192") +public final class DotNetMLDsa { + + private DotNetMLDsa() { + // nothing + } + + // ========================================================================= + // Sign / Verify operation rules (depending rules on any tracked MLDsa/CompositeMLDsa-family + // variable, mirroring DotNetDSA.java's SignData/VerifyData rules) + // ========================================================================= + + // mldsa.SignData(data, context) — 2 overloads (array-based and Span-based) + private static final IDetectionRule MLDSA_SIGN_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // mldsa.VerifyData(data, signature, context) — 2 overloads + private static final IDetectionRule MLDSA_VERIFY_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // mldsa.SignMu(mu[, destination]) — 3 overloads (MLDsa only, not CompositeMLDsa) + private static final IDetectionRule MLDSA_SIGN_MU = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignMu") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // mldsa.VerifyMu(mu, signature) — 2 overloads (MLDsa only) + private static final IDetectionRule MLDSA_VERIFY_MU = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyMu") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // mldsa.SignPreHash(hash, hashAlgorithmOid, context) — 2 overloads (MLDsa only) + private static final IDetectionRule MLDSA_SIGN_PRE_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignPreHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // mldsa.VerifyPreHash(hash, signature, hashAlgorithmOid, context) — 2 overloads (MLDsa only) + private static final IDetectionRule MLDSA_VERIFY_PRE_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyPreHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + /** Full set of depending rules for {@code MLDsa}/{@code MLDsaCng}/{@code MLDsaOpenSsl}. */ + private static final List> MLDSA_DEPENDING_RULES = + List.of( + MLDSA_SIGN_DATA, + MLDSA_VERIFY_DATA, + MLDSA_SIGN_MU, + MLDSA_VERIFY_MU, + MLDSA_SIGN_PRE_HASH, + MLDSA_VERIFY_PRE_HASH); + + /** + * Depending rules for {@code CompositeMLDsa}/{@code CompositeMLDsaCng} — only {@code + * SignData}/{@code VerifyData} exist on this type (no mu/pre-hash variants). + */ + private static final List> COMPOSITE_MLDSA_DEPENDING_RULES = + List.of(MLDSA_SIGN_DATA, MLDSA_VERIFY_DATA); + + // ========================================================================= + // MLDsa — primary creation rules, algorithm-parameterized (MLDsaAlgorithm argument captured + // as the parameter set, see class javadoc) + // ========================================================================= + + // MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65) + private static final IDetectionRule MLDSA_GENERATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLDsa") + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withMethodParameter(MethodMatcher.ANY) // MLDsaAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext(new KeyContext(Map.of("kind", "MLDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLDSA_DEPENDING_RULES); + + // MLDsa.ImportMLDsaPrivateKey(MLDsaAlgorithm.MLDsa65, privateKeyBytes) + private static final IDetectionRule MLDSA_IMPORT_PRIVATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLDsa") + .forMethods("ImportMLDsaPrivateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withMethodParameter(MethodMatcher.ANY) // MLDsaAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(MethodMatcher.ANY) // private key bytes + .buildForContext(new KeyContext(Map.of("kind", "MLDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLDSA_DEPENDING_RULES); + + // MLDsa.ImportMLDsaPrivateSeed(MLDsaAlgorithm.MLDsa65, seedBytes) + private static final IDetectionRule MLDSA_IMPORT_PRIVATE_SEED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLDsa") + .forMethods("ImportMLDsaPrivateSeed") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withMethodParameter(MethodMatcher.ANY) // MLDsaAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(MethodMatcher.ANY) // seed bytes + .buildForContext(new KeyContext(Map.of("kind", "MLDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLDSA_DEPENDING_RULES); + + // MLDsa.ImportMLDsaPublicKey(MLDsaAlgorithm.MLDsa65, publicKeyBytes) + private static final IDetectionRule MLDSA_IMPORT_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLDsa") + .forMethods("ImportMLDsaPublicKey") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withMethodParameter(MethodMatcher.ANY) // MLDsaAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(MethodMatcher.ANY) // public key bytes + .buildForContext(new KeyContext(Map.of("kind", "MLDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLDSA_DEPENDING_RULES); + + // ========================================================================= + // MLDsa — primary creation rules, structural imports (no MLDsaAlgorithm argument; see class + // javadoc for why the parameter set cannot be captured for these) + // ========================================================================= + + private static IDetectionRule structuralImportRule( + @Nonnull String objectType, + @Nonnull String methodName, + @Nonnull String kind, + @Nonnull List> dependingRules) { + return new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(objectType) + .forMethods(methodName) + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", kind))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(dependingRules); + } + + // MLDsa.ImportPkcs8PrivateKey(source) / MLDsa.ImportSubjectPublicKeyInfo(source) / + // MLDsa.ImportFromPem(pem) / MLDsa.ImportEncryptedPkcs8PrivateKey(...) / + // MLDsa.ImportFromEncryptedPem(...) + private static final IDetectionRule MLDSA_IMPORT_PKCS8_PRIVATE_KEY = + structuralImportRule("MLDsa", "ImportPkcs8PrivateKey", "MLDSA", MLDSA_DEPENDING_RULES); + + private static final IDetectionRule MLDSA_IMPORT_SUBJECT_PUBLIC_KEY_INFO = + structuralImportRule( + "MLDsa", "ImportSubjectPublicKeyInfo", "MLDSA", MLDSA_DEPENDING_RULES); + + private static final IDetectionRule MLDSA_IMPORT_FROM_PEM = + structuralImportRule("MLDsa", "ImportFromPem", "MLDSA", MLDSA_DEPENDING_RULES); + + private static final IDetectionRule MLDSA_IMPORT_ENCRYPTED_PKCS8_PRIVATE_KEY = + structuralImportRule( + "MLDsa", "ImportEncryptedPkcs8PrivateKey", "MLDSA", MLDSA_DEPENDING_RULES); + + private static final IDetectionRule MLDSA_IMPORT_FROM_ENCRYPTED_PEM = + structuralImportRule("MLDsa", "ImportFromEncryptedPem", "MLDSA", MLDSA_DEPENDING_RULES); + + // ========================================================================= + // MLDsa — primary creation rules, native-interop constructors (no MLDsaAlgorithm argument; + // wrap an already-existing native key handle, mirroring MLKemCng/MLKemOpenSsl in DotNetMLKem) + // ========================================================================= + + // new MLDsaCng(cngKey) + private static final IDetectionRule MLDSA_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLDsaCng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "MLDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLDSA_DEPENDING_RULES); + + // new MLDsaOpenSsl(safeEvpPKeyHandle) + private static final IDetectionRule MLDSA_OPENSSL = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLDsaOpenSsl") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "MLDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLDSA_DEPENDING_RULES); + + // ========================================================================= + // CompositeMLDsa — primary creation rules, algorithm-parameterized (CompositeMLDsaAlgorithm + // argument captured verbatim as the parameter set — see class javadoc for why this is not + // parsed into a structured ML-DSA-size + classical-algorithm pair) + // ========================================================================= + + // CompositeMLDsa.GenerateKey(CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256) + private static final IDetectionRule COMPOSITE_MLDSA_GENERATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("CompositeMLDsa") + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withMethodParameter(MethodMatcher.ANY) // CompositeMLDsaAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext(new KeyContext(Map.of("kind", "MLDSA_COMPOSITE"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(COMPOSITE_MLDSA_DEPENDING_RULES); + + // CompositeMLDsa.ImportCompositeMLDsaPrivateKey(CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256, + // privateKeyBytes) + private static final IDetectionRule COMPOSITE_MLDSA_IMPORT_PRIVATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("CompositeMLDsa") + .forMethods("ImportCompositeMLDsaPrivateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withMethodParameter(MethodMatcher.ANY) // CompositeMLDsaAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(MethodMatcher.ANY) // private key bytes + .buildForContext(new KeyContext(Map.of("kind", "MLDSA_COMPOSITE"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(COMPOSITE_MLDSA_DEPENDING_RULES); + + // CompositeMLDsa.ImportCompositeMLDsaPublicKey(CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256, + // publicKeyBytes) + private static final IDetectionRule COMPOSITE_MLDSA_IMPORT_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("CompositeMLDsa") + .forMethods("ImportCompositeMLDsaPublicKey") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withMethodParameter(MethodMatcher.ANY) // CompositeMLDsaAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(MethodMatcher.ANY) // public key bytes + .buildForContext(new KeyContext(Map.of("kind", "MLDSA_COMPOSITE"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(COMPOSITE_MLDSA_DEPENDING_RULES); + + // ========================================================================= + // CompositeMLDsa — primary creation rules, structural imports (no CompositeMLDsaAlgorithm + // argument) + // ========================================================================= + + // CompositeMLDsa.ImportPkcs8PrivateKey(source) / + // CompositeMLDsa.ImportSubjectPublicKeyInfo(source) / CompositeMLDsa.ImportFromPem(pem) / + // CompositeMLDsa.ImportEncryptedPkcs8PrivateKey(...) / + // CompositeMLDsa.ImportFromEncryptedPem(...) + private static final IDetectionRule COMPOSITE_MLDSA_IMPORT_PKCS8_PRIVATE_KEY = + structuralImportRule( + "CompositeMLDsa", + "ImportPkcs8PrivateKey", + "MLDSA_COMPOSITE", + COMPOSITE_MLDSA_DEPENDING_RULES); + + private static final IDetectionRule COMPOSITE_MLDSA_IMPORT_SUBJECT_PUBLIC_KEY_INFO = + structuralImportRule( + "CompositeMLDsa", + "ImportSubjectPublicKeyInfo", + "MLDSA_COMPOSITE", + COMPOSITE_MLDSA_DEPENDING_RULES); + + private static final IDetectionRule COMPOSITE_MLDSA_IMPORT_FROM_PEM = + structuralImportRule( + "CompositeMLDsa", + "ImportFromPem", + "MLDSA_COMPOSITE", + COMPOSITE_MLDSA_DEPENDING_RULES); + + private static final IDetectionRule + COMPOSITE_MLDSA_IMPORT_ENCRYPTED_PKCS8_PRIVATE_KEY = + structuralImportRule( + "CompositeMLDsa", + "ImportEncryptedPkcs8PrivateKey", + "MLDSA_COMPOSITE", + COMPOSITE_MLDSA_DEPENDING_RULES); + + private static final IDetectionRule COMPOSITE_MLDSA_IMPORT_FROM_ENCRYPTED_PEM = + structuralImportRule( + "CompositeMLDsa", + "ImportFromEncryptedPem", + "MLDSA_COMPOSITE", + COMPOSITE_MLDSA_DEPENDING_RULES); + + // ========================================================================= + // CompositeMLDsa — primary creation rule, native-interop constructor (no + // CompositeMLDsaAlgorithm argument; wraps an already-existing native key handle) + // ========================================================================= + + // new CompositeMLDsaCng(cngKey) + private static final IDetectionRule COMPOSITE_MLDSA_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("CompositeMLDsaCng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-DSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "MLDSA_COMPOSITE"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(COMPOSITE_MLDSA_DEPENDING_RULES); + + @Nonnull + public static List> rules() { + return Stream.of( + MLDSA_GENERATE_KEY, + MLDSA_IMPORT_PRIVATE_KEY, + MLDSA_IMPORT_PRIVATE_SEED, + MLDSA_IMPORT_PUBLIC_KEY, + MLDSA_IMPORT_PKCS8_PRIVATE_KEY, + MLDSA_IMPORT_SUBJECT_PUBLIC_KEY_INFO, + MLDSA_IMPORT_FROM_PEM, + MLDSA_IMPORT_ENCRYPTED_PKCS8_PRIVATE_KEY, + MLDSA_IMPORT_FROM_ENCRYPTED_PEM, + MLDSA_CNG, + MLDSA_OPENSSL, + COMPOSITE_MLDSA_GENERATE_KEY, + COMPOSITE_MLDSA_IMPORT_PRIVATE_KEY, + COMPOSITE_MLDSA_IMPORT_PUBLIC_KEY, + COMPOSITE_MLDSA_IMPORT_PKCS8_PRIVATE_KEY, + COMPOSITE_MLDSA_IMPORT_SUBJECT_PUBLIC_KEY_INFO, + COMPOSITE_MLDSA_IMPORT_FROM_PEM, + COMPOSITE_MLDSA_IMPORT_ENCRYPTED_PKCS8_PRIVATE_KEY, + COMPOSITE_MLDSA_IMPORT_FROM_ENCRYPTED_PEM, + COMPOSITE_MLDSA_CNG) + .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 000000000..486807afa --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKem.java @@ -0,0 +1,327 @@ +/* + * 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.detection.MethodMatcher; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.KeyAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.factory.KeyActionFactory; +import com.ibm.engine.model.factory.ParameterIdentifierFactory; +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 (Module-Lattice-Based Key-Encapsulation Mechanism, FIPS 203) usage in + * {@code System.Security.Cryptography}. + * + *

API availability (verified against the official Microsoft Learn API reference, not + * assumed): {@code MLKem}, {@code MLKemCng}, {@code MLKemOpenSsl} and {@code MLKemAlgorithm} + * are documented for the {@code net-10.0} and {@code net-11.0} monikers (both listed the "Assembly: + * System.Security.Cryptography.dll" as in-box, plus "Assembly: Microsoft.Bcl.Cryptography.dll" — + * package {@code Microsoft.Bcl.Cryptography v11.0.0-preview.7.26381.103} — as a back-compat NuGet + * shim for {@code MLKem}/{@code MLKemCng}/{@code MLKemAlgorithm} on older TFMs; {@code + * MLKemOpenSsl} lists only the in-box assembly, since its OpenSSL interop has no back-compat shim). + * {@code ImportPkcs8PrivateKey} carries a {@code + * [System.Diagnostics.CodeAnalysis.Experimental("SYSLIB5006")]} attribute on (at least) one of its + * documented overload snapshots, confirming this whole surface is still an actively evolving + * preview API as of .NET 10/11 — not a settled, stable API. + * + *

Classes covered: + * + *

    + *
  • {@code MLKem} — abstract base. All of its members are {@code static} factory methods (no + * public constructor exists — the only listed constructor, {@code MLKem(MLKemAlgorithm)}, is + * for derived classes): {@code GenerateKey(MLKemAlgorithm)}, {@code + * ImportDecapsulationKey(MLKemAlgorithm, byte[])}, {@code + * ImportEncapsulationKey(MLKemAlgorithm, byte[])}, {@code ImportPrivateSeed(MLKemAlgorithm, + * byte[])}, {@code ImportPkcs8PrivateKey(byte[])}, {@code + * ImportSubjectPublicKeyInfo(byte[])}, {@code ImportFromPem(string)}, {@code + * ImportEncryptedPkcs8PrivateKey(...)}, {@code ImportFromEncryptedPem(...)}. + *
  • {@code MLKemCng} — CNG-backed implementation ({@code MLKemCng(CngKey)} constructor). + *
  • {@code MLKemOpenSsl} — OpenSSL-backed implementation ({@code + * MLKemOpenSsl(SafeEvpPKeyHandle)} constructor). + *
+ * + *

Architecture — a "fundamental difference" from RSA/ECDsa/ECDiffieHellman justifies covering + * the {@code Import*} methods here: for RSA/ECDsa/ECDiffieHellman (see {@code DotNetRSA}, + * {@code DotNetECDsa}, {@code DotNetECDiffieHellman}), the {@code Import*}/{@code Export*} members + * inherited from {@code AsymmetricAlgorithm} are instance methods that mutate/read an + * already-constructed key object, and are intentionally not modeled (pure key-material plumbing, + * consistent with not tracking raw byte buffers per the engine's documented limitations). For + * {@code MLKem}, by contrast, every {@code Import*} method verified above is a {@code static} + * factory that is the only way (besides {@code GenerateKey} or the {@code MLKemCng}/{@code + * MLKemOpenSsl} native-interop constructors) to obtain an {@code MLKem} instance in the first place + * — structurally the same role as {@code RSA.Create(RSAParameters)}, which RSA's rule set does + * cover. They are therefore treated as primary creation rules here, not skipped. + * + *

Two creation-rule shapes result from this: + * + *

    + *
  • Algorithm-parameterized ({@code GenerateKey}, {@code ImportDecapsulationKey}, {@code + * ImportEncapsulationKey}, {@code ImportPrivateSeed}): all take an {@code MLKemAlgorithm} + * argument (e.g. {@code MLKemAlgorithm.MLKem768}), a member-access expression the engine + * resolves to the bare identifier {@code "MLKem768"} (see {@code + * CSharpLanguageTranslation#resolveIdentifierAsString} / {@code getEnumIdentifierName}, the + * same mechanism {@code DotNetAES}'s {@code AES_SET_MODE} rule relies on for {@code + * CipherMode.CBC}). This is captured with {@code ParameterIdentifierFactory<>()} as a child + * of the top-level detection, then mapped in {@code CSharpKeyContextTranslator} from {@code + * "MLKem512"/"MLKem768"/"MLKem1024"} to a {@code ParameterSetIdentifier} of {@code + * "512"/"768"/"1024"} — reproducing the exact {@code MLKEM(int, DetectionLocation)} shape + * already used by {@code JcaKemMapper}/{@code GoCryptoKEMMapper}, so {@code asString()} + * yields {@code "ML-KEM-768"} etc., consistent across all three language modules. + *
  • Structural imports ({@code ImportPkcs8PrivateKey}, {@code + * ImportSubjectPublicKeyInfo}, {@code ImportFromPem}, {@code ImportEncryptedPkcs8PrivateKey}, + * {@code ImportFromEncryptedPem}): the parameter set is embedded inside the encoded key + * material / PEM text, not present as a separate literal argument, so it cannot be recovered + * by this engine (no byte-content parsing). These translate to a generic {@code MLKEM} node + * with no {@code ParameterSetIdentifier} child (mirrors {@code MLKEM(DetectionLocation)}, the + * no-argument constructor) — a known, inherent precision gap, not a bug. + *
+ * + *

{@code MLKemCng(CngKey)} and {@code MLKemOpenSsl(SafeEvpPKeyHandle)} wrap an already-existing + * native key handle and never receive an {@code MLKemAlgorithm} argument at all, so they always + * translate to the generic {@code MLKEM} node, mirroring how {@code RSACng(CngKey)} in {@code + * DotNetRSA} captures no key size either. + * + *

Encapsulate / Decapsulate modeling — no missing {@code CipherAction.Action}/{@code + * SignatureAction.Action} enum value needed: unlike the concern raised for {@code + * ECDiffieHellman}'s derive operations (see {@code DotNetECDiffieHellman}'s javadoc), KEM + * encapsulate/decapsulate turned out to already have first-class support in the engine model: + * {@code KeyAction.Action} (in {@code com.ibm.engine.model.KeyAction}, used via {@code + * KeyActionFactory}) already defines {@code ENCAPSULATION} and {@code DECAPSULATION} members, and + * the mapper model already defines {@code com.ibm.mapper.model.functionality.Encapsulate}/{@code + * Decapsulate} functionality nodes. This exact combination is already used by the very recently + * added Go {@code crypto/mlkem} support ({@code GoCryptoMLKEM.java} / {@code + * GoKeyContextTranslator.java}), which this rule set mirrors: {@code MLKem.Encapsulate(...)} / + * {@code MLKem.Decapsulate(...)} (both instance methods, confirmed from the official + * method-reference pages, each with two overloads differing only by array-vs-{@code Span} / + * out-vs-return parameter shape, collapsed with a single {@code withAnyParameters()} rule per + * method name — {@code EncapsulateCore}/{@code DecapsulateCore} are protected extensibility hooks + * for subclassing {@code MLKem} itself, not called by ordinary consumer code, and are intentionally + * not modeled, consistent with not modeling other protected virtual hooks elsewhere in this rule + * set) are captured with {@code KeyActionFactory<>(KeyAction .Action.ENCAPSULATION / + * .DECAPSULATION)} under a {@code KeyContext} of {@code kind = "KEM"}, dispatched in {@code + * CSharpKeyContextTranslator} exactly like the Go translator does. No detection rule had to fake or + * omit an action type. + * + *

Mapper model reuse: {@code com.ibm.mapper.model.algorithms.MLKEM} (implementing {@code + * KeyEncapsulationMechanism}) already existed before this batch — it is used by the JCA, + * BouncyCastle and Go {@code crypto/mlkem} translations. No new mapper model class was introduced + * for ML-KEM itself; this rule set is purely additive (new detection rules plus new {@code "KEM"} + * dispatch branches in {@code CSharpKeyContextTranslator}). + * + *

Not covered (deliberately, consistent with the rest of this rule set): the {@code + * Algorithm}/{@code IsSupported} getters (state reads, not configuration — no property setters + * exist on {@code MLKem} at all), {@code MLKemCng.GetKey()} / {@code + * MLKemOpenSsl.DuplicateKeyHandle()} (native-handle export, the {@code MLKem}-specific equivalent + * of the {@code Export*} methods skipped for RSA/ECDsa/ECDiffieHellman), {@code Dispose()}, and all + * {@code ExportXxx}/{@code TryExportXxx}/{@code ExportXxxPem} instance methods (standard + * PKCS#8/SPKI/PEM export — same convention as RSA/ECDsa/ECDiffieHellman). + */ +@SuppressWarnings("java:S1192") +public final class DotNetMLKem { + + private DotNetMLKem() { + // nothing + } + + // ========================================================================= + // Encapsulate / Decapsulate operation rules (depending rules on any tracked MLKem-family + // variable, mirroring GoCryptoMLKEM.java's ENCAPSULATE_768/DECAPSULATE_768 rules) + // ========================================================================= + + // mlkem.Encapsulate(out ciphertext, out sharedSecret) / mlkem.Encapsulate(ciphertext, + // sharedSecret) + private static final IDetectionRule MLKEM_ENCAPSULATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Encapsulate") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.ENCAPSULATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KEM"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // mlkem.Decapsulate(ciphertext) / mlkem.Decapsulate(ciphertext, sharedSecret) + private static final IDetectionRule MLKEM_DECAPSULATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Decapsulate") + .shouldBeDetectedAs(new KeyActionFactory<>(KeyAction.Action.DECAPSULATION)) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KEM"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> MLKEM_DEPENDING_RULES = + List.of(MLKEM_ENCAPSULATE, MLKEM_DECAPSULATE); + + // ========================================================================= + // Primary creation rules — algorithm-parameterized (MLKemAlgorithm argument captured as the + // parameter set, see class javadoc) + // ========================================================================= + + // MLKem.GenerateKey(MLKemAlgorithm.MLKem768) + private static final IDetectionRule MLKEM_GENERATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLKem") + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-KEM")) + .withMethodParameter(MethodMatcher.ANY) // MLKemAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext(new KeyContext(Map.of("kind", "KEM"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLKEM_DEPENDING_RULES); + + // MLKem.ImportDecapsulationKey(MLKemAlgorithm.MLKem768, decapsulationKeyBytes) + private static final IDetectionRule MLKEM_IMPORT_DECAPSULATION_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLKem") + .forMethods("ImportDecapsulationKey") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-KEM")) + .withMethodParameter(MethodMatcher.ANY) // MLKemAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(MethodMatcher.ANY) // decapsulation key bytes + .buildForContext(new KeyContext(Map.of("kind", "KEM"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLKEM_DEPENDING_RULES); + + // MLKem.ImportEncapsulationKey(MLKemAlgorithm.MLKem768, encapsulationKeyBytes) + private static final IDetectionRule MLKEM_IMPORT_ENCAPSULATION_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLKem") + .forMethods("ImportEncapsulationKey") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-KEM")) + .withMethodParameter(MethodMatcher.ANY) // MLKemAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(MethodMatcher.ANY) // encapsulation key bytes + .buildForContext(new KeyContext(Map.of("kind", "KEM"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLKEM_DEPENDING_RULES); + + // MLKem.ImportPrivateSeed(MLKemAlgorithm.MLKem768, seedBytes) + private static final IDetectionRule MLKEM_IMPORT_PRIVATE_SEED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLKem") + .forMethods("ImportPrivateSeed") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-KEM")) + .withMethodParameter(MethodMatcher.ANY) // MLKemAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(MethodMatcher.ANY) // seed bytes + .buildForContext(new KeyContext(Map.of("kind", "KEM"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLKEM_DEPENDING_RULES); + + // ========================================================================= + // Primary creation rules — structural imports (no MLKemAlgorithm argument; see class javadoc + // for why the parameter set cannot be captured for these) + // ========================================================================= + + // MLKem.ImportPkcs8PrivateKey(source) / MLKem.ImportSubjectPublicKeyInfo(source) / + // MLKem.ImportFromPem(pem) / MLKem.ImportEncryptedPkcs8PrivateKey(...) / + // MLKem.ImportFromEncryptedPem(...) + private static IDetectionRule structuralImportRule(@Nonnull String methodName) { + return new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLKem") + .forMethods(methodName) + .shouldBeDetectedAs(new ValueActionFactory<>("ML-KEM")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KEM"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLKEM_DEPENDING_RULES); + } + + private static final IDetectionRule MLKEM_IMPORT_PKCS8_PRIVATE_KEY = + structuralImportRule("ImportPkcs8PrivateKey"); + + private static final IDetectionRule MLKEM_IMPORT_SUBJECT_PUBLIC_KEY_INFO = + structuralImportRule("ImportSubjectPublicKeyInfo"); + + private static final IDetectionRule MLKEM_IMPORT_FROM_PEM = + structuralImportRule("ImportFromPem"); + + private static final IDetectionRule MLKEM_IMPORT_ENCRYPTED_PKCS8_PRIVATE_KEY = + structuralImportRule("ImportEncryptedPkcs8PrivateKey"); + + private static final IDetectionRule MLKEM_IMPORT_FROM_ENCRYPTED_PEM = + structuralImportRule("ImportFromEncryptedPem"); + + // ========================================================================= + // Primary creation rules — native-interop constructors (no MLKemAlgorithm argument; wrap an + // already-existing native key handle, mirroring RSACng(CngKey)/RSAOpenSsl(SafeEvpPKeyHandle) in + // DotNetRSA) + // ========================================================================= + + // new MLKemCng(cngKey) + private static final IDetectionRule MLKEM_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLKemCng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-KEM")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KEM"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLKEM_DEPENDING_RULES); + + // new MLKemOpenSsl(safeEvpPKeyHandle) + private static final IDetectionRule MLKEM_OPENSSL = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MLKemOpenSsl") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("ML-KEM")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KEM"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(MLKEM_DEPENDING_RULES); + + @Nonnull + public static List> rules() { + return List.of( + MLKEM_GENERATE_KEY, + MLKEM_IMPORT_DECAPSULATION_KEY, + MLKEM_IMPORT_ENCAPSULATION_KEY, + MLKEM_IMPORT_PRIVATE_SEED, + MLKEM_IMPORT_PKCS8_PRIVATE_KEY, + MLKEM_IMPORT_SUBJECT_PUBLIC_KEY_INFO, + MLKEM_IMPORT_FROM_PEM, + MLKEM_IMPORT_ENCRYPTED_PKCS8_PRIVATE_KEY, + MLKEM_IMPORT_FROM_ENCRYPTED_PEM, + MLKEM_CNG, + MLKEM_OPENSSL); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetProtectedData.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetProtectedData.java new file mode 100644 index 000000000..d85ab0211 --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetProtectedData.java @@ -0,0 +1,292 @@ +/* + * 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.detection.MethodMatcher; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.CipherActionFactory; +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 javax.annotation.Nonnull; + +/** + * Detection rules for the Windows Data Protection API (DPAPI) surface of {@code + * System.Security.Cryptography}. + * + *

Classes covered (per the official API reference, verified via WebFetch of learn.microsoft.com + * — not guessed): + * + *

    + *
  • {@code ProtectedData} — static-only class ({@code System.Security.Cryptography} namespace, + * assembly {@code System.Security.Cryptography.ProtectedData.dll} / {@code + * System.Security.dll}). Methods: {@code Protect(byte[], byte[], DataProtectionScope)}, + * {@code Protect(ReadOnlySpan, DataProtectionScope, ReadOnlySpan)}, {@code + * Protect(ReadOnlySpan, DataProtectionScope, Span, ReadOnlySpan)}, {@code + * TryProtect(...)}, and the {@code Unprotect}/{@code TryUnprotect} mirror overloads. + *
  • {@code ProtectedMemory} — static-only class (assembly {@code System.Security.dll}). + * Methods: {@code Protect(byte[], MemoryProtectionScope)}, {@code Unprotect(byte[], + * MemoryProtectionScope)}. No {@code Try*} variants are published for this class. + *
  • {@code DpapiDataProtector} — a real, documented class ({@code System.Security.Cryptography} + * namespace, assembly {@code System.Security.dll}), sealed and deriving from the abstract + * {@code DataProtector} base class. Verified via WebFetch to exist as an actual API member + * (not a misnaming of something from {@code Microsoft.AspNetCore.DataProtection}, which is an + * entirely different, unrelated API — the ASP.NET Core Data Protection stack has its own + * {@code IDataProtector}/{@code IDataProtectionProvider} types, not this class). Constructor + * {@code DpapiDataProtector(string appName, string primaryPurpose, params string[] + * specificPurpose)}; instance methods {@code Protect(byte[])}/{@code Unprotect(byte[])} + * (inherited, non-overridable, from {@code DataProtector}) delegate internally to {@code + * ProtectedData.Protect}/{@code Unprotect}. Important availability caveat found during + * verification: unlike {@code ProtectedData}/{@code ProtectedMemory} (which ship for + * modern .NET on Windows via the {@code System.Security.Cryptography.ProtectedData} NuGet + * package, per the moniker list on their doc pages: {@code netcore-1.0} onward, {@code + * windowsdesktop-*}, {@code net-11.0-pp}), {@code DpapiDataProtector}'s doc page lists + * only {@code netframework-4.5} through {@code netframework-4.8.1} monikers — it is + * .NET Framework legacy-only and has no modern-.NET equivalent. It remains valid, detectable + * legacy source, so it is covered here. + *
+ * + *

Platform note: per the official remarks on both {@code ProtectedData} and {@code + * ProtectedMemory}, "this class provides access to the Data Protection API (DPAPI) available in + * Windows operating systems" and its use "on platforms other than Windows throws a {@code + * PlatformNotSupportedException}". This does not affect detection: these are source-level patterns + * matched independently of the runtime platform the scanned code would actually execute on. + * + *

Modeling decision — algorithm identity: DPAPI deliberately does not expose which + * concrete symmetric algorithm it uses underneath (this has changed across Windows versions and is + * not selectable by the caller), so translating it as a specific algorithm such as {@code AES} or + * {@code DESede} would be inventing information that is not present in the source code. Instead, + * following the same precedent as {@code CSharpPRNGContextTranslator}'s {@code "NATIVEPRNG"} (used + * for .NET's {@code RandomNumberGenerator}, an equally vendor/platform-opaque primitive — see + * {@link DotNetRandomNumberGenerator}), this file captures the vendor-specific value {@code + * "DPAPI"} and dispatches it in {@code CSharpCipherContextTranslator} to the generic mapper {@code + * Algorithm} constructor ({@code new Algorithm("DPAPI", Cipher.class, detectionLocation)}), using + * the existing generic {@code Cipher} primitive marker interface (already present in the mapper + * model, parent of {@code BlockCipher}/{@code StreamCipher}) as the closest honest classification: + * "used to encrypt/decrypt, kind and identity unspecified". No new mapper model class was created + * for this batch — the existing generic {@code Algorithm(name, kind, detectionLocation)} concept is + * sufficient, exactly as it already was for {@code NATIVEPRNG}. + * + *

Modeling decision — static one-shot calls vs. instance depending-rule operations: + * mirrors the {@code HKDF}/{@code RandomNumberGenerator} static-methods precedent (see {@link + * DotNetKeyDerivation}, {@link DotNetRandomNumberGenerator}). {@code ProtectedData.Protect}/{@code + * TryProtect}/{@code Unprotect}/{@code TryUnprotect} and {@code ProtectedMemory.Protect}/{@code + * Unprotect} are each a complete, self-contained, fully static call with no instance to track — the + * "algorithm identity" (DPAPI) and the "operation" (encrypt/decrypt) happen at once. Because a + * detection rule's top-level {@code shouldBeDetectedAs} can only capture one thing (see + * DETECTION_RULE_STRUCTURE.md), and {@code CipherContext}'s existing translation branches already + * distinguish a {@code ValueAction} (algorithm identity) from a {@code CipherAction} + * (encrypt/decrypt functionality) as two independent, mutually exclusive top-level captures, these + * static calls capture a single merged {@code ValueActionFactory<>("DPAPI_PROTECT")} / {@code + * ValueActionFactory<>("DPAPI_UNPROTECT")} value instead. {@code CSharpCipherContextTranslator} + * then builds the composite node directly (the {@code DPAPI} {@code Algorithm} node with an {@code + * Encrypt}/{@code Decrypt} functionality child already attached) — the same "single detected string + * encodes both identity and purpose" idiom already used elsewhere in this codebase (e.g. {@code + * JavaKeyContextTranslator}'s {@code algo.put(new KeyGeneration(detectionLocation))} pattern). + * + *

By contrast, {@code DpapiDataProtector} does have a real tracked instance (created + * via {@code new DpapiDataProtector(...)}), so it follows the AES-family creation/depending-rule + * pattern instead (see {@link DotNetAES}): the constructor is a top-level rule capturing the {@code + * "DPAPI"} identity, with the instance {@code Protect}/{@code Unprotect} calls as depending rules + * capturing {@code CipherActionFactory<>(CipherAction.Action.ENCRYPT/DECRYPT)} — translated as + * children of the already-identified {@code DPAPI} node by the existing (unmodified) {@code + * CipherAction} branch of {@code CSharpCipherContextTranslator}. + * + *

As with every other file in this rule set, the ANTLR4-based C# engine cannot resolve parameter + * types (see {@code CSharpLanguageTranslation}), so all overloads that only differ by {@code + * byte[]} vs. {@code ReadOnlySpan}/{@code Span}, or by the presence of an output-buffer + * / {@code out} parameter, are collapsed into a single {@code withAnyParameters()} rule per method + * name — exactly like {@code TryEncryptCbc} etc. in {@link DotNetAES}. + * + *

Known gap — {@code DataProtectionScope}/{@code MemoryProtectionScope} parameter, and {@code + * DpapiDataProtector.Scope} property setter, are not decoded: unlike {@code CipherMode}/{@code + * PaddingMode} (decoded by {@code AES_SET_MODE}/{@code AES_SET_PADDING} in {@link DotNetAES} + * because the mapper model already has generic, cross-library {@code Mode}/{@code Padding} concepts + * that {@code CipherMode.CBC}/{@code PaddingMode.PKCS7} map onto), {@code DataProtectionScope} + * ({@code CurrentUser}/{@code LocalMachine}) and {@code MemoryProtectionScope} ({@code + * SameProcess}/{@code CrossProcess}/{@code SameLogon}) describe who can decrypt the data + * (a key-custody / access-scope concept), not a cipher mode, padding, or any other concept the + * mapper model currently represents. Forcing it through {@code ModeFactory} or a similar existing + * factory would silently mislabel an access-scope value as a block-cipher mode once it reaches + * {@code JcaModeMapper}, which would not recognize e.g. {@code "CurrentUser"} and would just drop + * it — arguably worse than not capturing it at all. The engine can technically resolve + * these enum member-access expressions to their member name (see {@code + * CSharpDetectionEngine#resolveValues}, which handles {@code CSharpMemberAccessTree} generically, + * not only for {@code Mode}/{@code Padding}), so this is a genuine modeling gap — not an engine + * limitation — flagged here per the "document, don't force" principle rather than silently deciding + * one way. The scope parameter position is still captured (via {@code MethodMatcher.ANY}, + * uninterpreted) so the rule's parameter arity/shape stays accurate; only its value is left + * uncaptured. For the same reason, {@code DpapiDataProtector}'s {@code appName}/{@code + * primaryPurpose}/{@code specificPurpose} constructor arguments (arbitrary caller-chosen strings, + * not cryptographic identifiers) and its {@code Scope} property setter are not decoded either. + */ +@SuppressWarnings("java:S1192") +public final class DotNetProtectedData { + + private DotNetProtectedData() { + // nothing + } + + // ========================================================================= + // ProtectedData — static-only class. Each static call is both the "creation" and + // the "operation" at once (see class javadoc "Modeling decision"). + // ========================================================================= + + // ProtectedData.Protect(byte[] userData, byte[] optionalEntropy, DataProtectionScope scope) + // / Protect(ReadOnlySpan, DataProtectionScope, ReadOnlySpan) + // / Protect(ReadOnlySpan, DataProtectionScope, Span, ReadOnlySpan) + private static final IDetectionRule PROTECTED_DATA_PROTECT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("ProtectedData") + .forMethods("Protect") + .shouldBeDetectedAs(new ValueActionFactory<>("DPAPI_PROTECT")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ProtectedData.Unprotect(...) — same overload shapes as Protect, mirrored. + private static final IDetectionRule PROTECTED_DATA_UNPROTECT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("ProtectedData") + .forMethods("Unprotect") + .shouldBeDetectedAs(new ValueActionFactory<>("DPAPI_UNPROTECT")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ProtectedData.TryProtect(ReadOnlySpan, DataProtectionScope, Span, out int, + // ReadOnlySpan) + private static final IDetectionRule PROTECTED_DATA_TRY_PROTECT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("ProtectedData") + .forMethods("TryProtect") + .shouldBeDetectedAs(new ValueActionFactory<>("DPAPI_PROTECT")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ProtectedData.TryUnprotect(...) — mirrors TryProtect. + private static final IDetectionRule PROTECTED_DATA_TRY_UNPROTECT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("ProtectedData") + .forMethods("TryUnprotect") + .shouldBeDetectedAs(new ValueActionFactory<>("DPAPI_UNPROTECT")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // ProtectedMemory — static-only class. No Try* overloads are published for this class. + // ========================================================================= + + // ProtectedMemory.Protect(byte[] userData, MemoryProtectionScope scope) + private static final IDetectionRule PROTECTED_MEMORY_PROTECT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("ProtectedMemory") + .forMethods("Protect") + .shouldBeDetectedAs(new ValueActionFactory<>("DPAPI_PROTECT")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ProtectedMemory.Unprotect(byte[] encryptedData, MemoryProtectionScope scope) + private static final IDetectionRule PROTECTED_MEMORY_UNPROTECT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("ProtectedMemory") + .forMethods("Unprotect") + .shouldBeDetectedAs(new ValueActionFactory<>("DPAPI_UNPROTECT")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // DpapiDataProtector — real tracked instance (new DpapiDataProtector(...)), so this follows + // the AES-family creation/depending-rule pattern instead of the static one-shot pattern above + // (see class javadoc). + // ========================================================================= + + // protector.Protect(byte[] userData) — instance method inherited from DataProtector. + private static final IDetectionRule DPAPI_INSTANCE_PROTECT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Protect") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // protector.Unprotect(byte[] encryptedData) — instance method inherited from DataProtector. + private static final IDetectionRule DPAPI_INSTANCE_UNPROTECT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Unprotect") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> DPAPI_INSTANCE_DEPENDING_RULES = + List.of(DPAPI_INSTANCE_PROTECT, DPAPI_INSTANCE_UNPROTECT); + + // new DpapiDataProtector(appName, primaryPurpose, specificPurpose...) — single constructor + // overload, but with a params array (variable arity), so withAnyParameters() is used exactly + // like the AesCng / SP800108HmacCounterKdf precedents for the same reason. + private static final IDetectionRule DPAPI_DATA_PROTECTOR_CTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("DpapiDataProtector") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("DPAPI")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(DPAPI_INSTANCE_DEPENDING_RULES); + + @Nonnull + public static List> rules() { + return List.of( + PROTECTED_DATA_PROTECT, + PROTECTED_DATA_UNPROTECT, + PROTECTED_DATA_TRY_PROTECT, + PROTECTED_DATA_TRY_UNPROTECT, + PROTECTED_MEMORY_PROTECT, + PROTECTED_MEMORY_UNPROTECT, + DPAPI_DATA_PROTECTOR_CTOR); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRC2.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRC2.java index 5c73f7ada..cfb99d81f 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRC2.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRC2.java @@ -19,23 +19,45 @@ */ package com.ibm.plugin.rules.detection.dotnet; +import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.Size; import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.BlockSizeFactory; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.ModeFactory; +import com.ibm.engine.model.factory.PaddingFactory; 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.stream.Stream; import javax.annotation.Nonnull; /** * Detection rules for RC2 usage in System.Security.Cryptography. * - *

Detects: + *

Classes covered: * *

    - *
  • {@code RC2.Create()} — abstract factory (weak cipher) - *
  • {@code new RC2CryptoServiceProvider()} — CAPI-backed (weak cipher) + *
  • {@code RC2} — abstract base ({@code RC2.Create()}, {@code RC2.Create(string)}) + *
  • {@code RC2CryptoServiceProvider} — legacy CAPI implementation *
+ * + *

Architecture: all methods inherited from {@code SymmetricAlgorithm} (EncryptCbc, DecryptCbc, + * CreateEncryptor, property setters, etc.) are expressed as depending rules attached to + * each primary creation rule. The detection engine tracks the variable and fires these rules on + * every matching method call, regardless of the concrete RC2 subclass. Like {@code DES}, {@code + * RC2} has no CNG-backed subclass and no AEAD variant, so it mirrors {@code DotNetDES}'s + * depending-rule coverage, with one addition: RC2's own {@code EffectiveKeySize} property. + * + *

Known gap: {@code RC2CryptoServiceProvider.UseSalt} (a CAPI-only boolean flag controlling + * whether an 11-byte zero-value salt is appended when deriving a key) has no corresponding concept + * in the detection model (no boolean/flag value factory exists, and it does not map to a CBOM + * property such as mode, padding or key/block size). It is therefore intentionally left undetected. + * // TODO: RC2CryptoServiceProvider.UseSalt is not detected as a known gap. */ @SuppressWarnings("java:S1192") public final class DotNetRC2 { @@ -44,6 +66,525 @@ private DotNetRC2() { // nothing } + // ========================================================================= + // Property setter rules (synthetic set_X method invocations) + // ========================================================================= + + // rc2.Mode = CipherMode.CBC → synthetic set_Mode(CipherMode.CBC) + private static final IDetectionRule RC2_SET_MODE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_Mode") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new ModeFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rc2.KeySize = 128 → synthetic set_KeySize(128) + private static final IDetectionRule RC2_SET_KEY_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_KeySize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rc2.EffectiveKeySize = 64 → synthetic set_EffectiveKeySize(64) + // RC2-specific property (not present on SymmetricAlgorithm nor on Aes/DES). Semantically it + // constrains the effective cryptographic strength of the key in bits, so it is reused as a + // KeySize detection (same as set_KeySize above) rather than introducing a dedicated factory. + private static final IDetectionRule RC2_SET_EFFECTIVE_KEY_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_EffectiveKeySize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rc2.Padding = PaddingMode.PKCS7 → synthetic set_Padding(PaddingMode.PKCS7) + private static final IDetectionRule RC2_SET_PADDING = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_Padding") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rc2.FeedbackSize = 8 → synthetic set_FeedbackSize(8) + private static final IDetectionRule RC2_SET_FEEDBACK_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_FeedbackSize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new BlockSizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> PROPERTY_SETTER_RULES = + List.of( + RC2_SET_MODE, + RC2_SET_KEY_SIZE, + RC2_SET_EFFECTIVE_KEY_SIZE, + RC2_SET_PADDING, + RC2_SET_FEEDBACK_SIZE); + + // ========================================================================= + // CreateEncryptor / CreateDecryptor rules + // ========================================================================= + + // rc2.CreateEncryptor() + private static final IDetectionRule RC2_CREATE_ENCRYPTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateEncryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rc2.CreateEncryptor(byte[] key, byte[] iv) + private static final IDetectionRule RC2_CREATE_ENCRYPTOR_WITH_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateEncryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withMethodParameter(MethodMatcher.ANY) // key bytes + .withMethodParameter(MethodMatcher.ANY) // iv bytes + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rc2.CreateDecryptor() + private static final IDetectionRule RC2_CREATE_DECRYPTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateDecryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rc2.CreateDecryptor(byte[] key, byte[] iv) + private static final IDetectionRule RC2_CREATE_DECRYPTOR_WITH_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateDecryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withMethodParameter(MethodMatcher.ANY) // key bytes + .withMethodParameter(MethodMatcher.ANY) // iv bytes + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptCbc / DecryptCbc rules + // Mode is constant "CBC" (from method name); padding is detected from last param. + // Two overloads: 3-param and 4-param (with output buffer). + // ========================================================================= + + // EncryptCbc(plaintext, iv, padding) + private static final IDetectionRule RC2_ENCRYPT_CBC_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCbc(plaintext, iv, destination, padding) [output-buffer overload] + private static final IDetectionRule RC2_ENCRYPT_CBC_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCbc(ciphertext, iv, padding) + private static final IDetectionRule RC2_DECRYPT_CBC_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCbc(ciphertext, iv, destination, padding) + private static final IDetectionRule RC2_DECRYPT_CBC_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptEcb / DecryptEcb rules + // Mode is constant "ECB" (from method name); no IV parameter. + // Two overloads: 2-param and 3-param (with output buffer). + // ========================================================================= + + // EncryptEcb(plaintext, padding) + private static final IDetectionRule RC2_ENCRYPT_ECB_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptEcb(plaintext, destination, padding) + private static final IDetectionRule RC2_ENCRYPT_ECB_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptEcb(ciphertext, padding) + private static final IDetectionRule RC2_DECRYPT_ECB_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptEcb(ciphertext, destination, padding) + private static final IDetectionRule RC2_DECRYPT_ECB_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptCfb / DecryptCfb rules + // Mode is constant "CFB"; padding detected from 3rd param; feedbackSize ignored. + // Two overloads: 4-param and 5-param (with output buffer). + // ========================================================================= + + // EncryptCfb(plaintext, iv, padding, feedbackSize) + private static final IDetectionRule RC2_ENCRYPT_CFB_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCfb(plaintext, iv, destination, padding, feedbackSize) + private static final IDetectionRule RC2_ENCRYPT_CFB_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCfb(ciphertext, iv, padding, feedbackSize) + private static final IDetectionRule RC2_DECRYPT_CFB_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCfb(ciphertext, iv, destination, padding, feedbackSize) + private static final IDetectionRule RC2_DECRYPT_CFB_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // TryEncrypt* / TryDecrypt* rules + // Signatures: + // TryEncryptCbc(plaintext, iv, destination, out bytesWritten, padding) — 5 params + // TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, padding) — 5 params + // TryEncryptEcb(plaintext, destination, padding, out bytesWritten) — 4 params + // TryDecryptEcb(ciphertext, destination, padding, out bytesWritten) — 4 params + // TryEncryptCfb(plaintext, iv, destination, out bytesWritten, padding, fs) — 6 params + // TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, padding, fs) — 6 params + // ========================================================================= + + // TryEncryptCbc(plaintext, iv, destination, out bytesWritten, padding) + private static final IDetectionRule RC2_TRY_ENCRYPT_CBC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, padding) + private static final IDetectionRule RC2_TRY_DECRYPT_CBC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptEcb(plaintext, destination, padding, out bytesWritten) + private static final IDetectionRule RC2_TRY_ENCRYPT_ECB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptEcb(ciphertext, destination, padding, out bytesWritten) + private static final IDetectionRule RC2_TRY_DECRYPT_ECB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptCfb(plaintext, iv, destination, out bytesWritten, padding, feedbackSize) + private static final IDetectionRule RC2_TRY_ENCRYPT_CFB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, padding, feedbackSize) + private static final IDetectionRule RC2_TRY_DECRYPT_CFB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Key / IV generation rules + // ========================================================================= + + // rc2.GenerateKey() — generates a new random key (size determined by KeySize property) + private static final IDetectionRule RC2_GENERATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("GenerateKey")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rc2.GenerateIV() — generates a new random initialization vector + private static final IDetectionRule RC2_GENERATE_IV = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GenerateIV") + .shouldBeDetectedAs(new ValueActionFactory<>("GenerateIV")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Aggregated depending-rule lists + // ========================================================================= + + /** + * All cipher operation rules that fire on a tracked RC2-family variable. Includes + * CreateEncryptor/CreateDecryptor, direct mode-specific Encrypt/Decrypt methods, Try* variants, + * and key/IV generation. + */ + private static final List> CIPHER_OP_RULES = + List.of( + RC2_CREATE_ENCRYPTOR, + RC2_CREATE_ENCRYPTOR_WITH_KEY, + RC2_CREATE_DECRYPTOR, + RC2_CREATE_DECRYPTOR_WITH_KEY, + RC2_ENCRYPT_CBC_3, + RC2_ENCRYPT_CBC_4, + RC2_DECRYPT_CBC_3, + RC2_DECRYPT_CBC_4, + RC2_ENCRYPT_ECB_2, + RC2_ENCRYPT_ECB_3, + RC2_DECRYPT_ECB_2, + RC2_DECRYPT_ECB_3, + RC2_ENCRYPT_CFB_4, + RC2_ENCRYPT_CFB_5, + RC2_DECRYPT_CFB_4, + RC2_DECRYPT_CFB_5, + RC2_TRY_ENCRYPT_CBC, + RC2_TRY_DECRYPT_CBC, + RC2_TRY_ENCRYPT_ECB, + RC2_TRY_DECRYPT_ECB, + RC2_TRY_ENCRYPT_CFB, + RC2_TRY_DECRYPT_CFB, + RC2_GENERATE_KEY, + RC2_GENERATE_IV); + + /** Full set of depending rules for all RC2-derived classes. */ + private static final List> RC2_DEPENDING_RULES = + Stream.concat(PROPERTY_SETTER_RULES.stream(), CIPHER_OP_RULES.stream()).toList(); + + // ========================================================================= + // Primary creation rules + // ========================================================================= + + // RC2.Create() — abstract factory, no parameters private static final IDetectionRule RC2_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -53,8 +594,21 @@ private DotNetRC2() { .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(RC2_DEPENDING_RULES); + + // RC2.Create("RC2") — named factory (obsolete, still detectable) + private static final IDetectionRule RC2_CREATE_NAMED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RC2") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("RC2")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(RC2_DEPENDING_RULES); + // new RC2CryptoServiceProvider() — legacy CAPI implementation private static final IDetectionRule RC2_CSP = new DetectionRuleBuilder() .createDetectionRule() @@ -64,10 +618,10 @@ private DotNetRC2() { .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(RC2_DEPENDING_RULES); @Nonnull public static List> rules() { - return List.of(RC2_CREATE, RC2_CSP); + return List.of(RC2_CREATE, RC2_CREATE_NAMED, RC2_CSP); } } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRSA.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRSA.java index 43cb3f561..65ef688fe 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRSA.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRSA.java @@ -19,26 +19,49 @@ */ package com.ibm.plugin.rules.detection.dotnet; +import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.Size; +import com.ibm.engine.model.context.CipherContext; import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; 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 java.util.stream.Stream; import javax.annotation.Nonnull; /** * Detection rules for RSA usage in System.Security.Cryptography. * - *

Detects: + *

Classes covered: * *

    - *
  • {@code RSA.Create()} — abstract factory, no key size - *
  • {@code RSA.Create(2048)} — factory with key size - *
  • {@code new RSACryptoServiceProvider()} — CAPI-backed, no key size - *
  • {@code new RSACryptoServiceProvider(2048)} — CAPI-backed with key size + *
  • {@code RSA} — abstract base ({@code RSA.Create()}, {@code RSA.Create(int)}, {@code + * RSA.Create(RSAParameters)}, {@code RSA.Create(string)}) + *
  • {@code RSACryptoServiceProvider} — legacy CAPI implementation + *
  • {@code RSACng} — CNG-backed implementation, Windows-only (ephemeral and persisted-key + * constructors) + *
  • {@code RSAOpenSsl} — OpenSSL-backed implementation, non-Windows only *
+ * + *

Architecture: all members inherited from {@code RSA} / {@code AsymmetricAlgorithm} (the {@code + * KeySize} property, {@code Encrypt}/{@code Decrypt}, {@code SignData}/{@code VerifyData}, {@code + * SignHash}/{@code VerifyHash}, and their {@code Try*} variants) are expressed as depending + * rules attached to each primary creation rule. The detection engine tracks the variable and + * fires these rules on every matching method call, regardless of the concrete RSA subclass. Method + * overloads that only differ by array-vs-{@code Span}, offset/length, or output-buffer parameters + * are intentionally collapsed into a single {@code withAnyParameters()} rule per method name: the + * ANTLR4-based C# engine cannot resolve parameter types (see {@code CSharpLanguageTranslation}), so + * distinguishing overloads by parameter type is not possible, and none of the extra parameters + * carry additional cryptographic information worth extracting. */ @SuppressWarnings("java:S1192") public final class DotNetRSA { @@ -47,6 +70,189 @@ private DotNetRSA() { // nothing } + // ========================================================================= + // Property setter rules (synthetic set_X method invocations) + // ========================================================================= + + // rsa.KeySize = 2048 → synthetic set_KeySize(2048) + private static final IDetectionRule RSA_SET_KEY_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_KeySize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new KeyContext(Map.of("kind", "RSA"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Encryption / decryption operation rules (RSA used as a public-key cipher) + // Each rule covers every overload of the given method name (arities vary only + // by array-vs-Span / output-buffer parameters, which are not individually + // tracked), mirroring the AES direct Encrypt/Decrypt rules. + // ========================================================================= + + // rsa.Encrypt(data, padding) / rsa.Encrypt(data, destination, padding) + private static final IDetectionRule RSA_ENCRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Encrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rsa.Decrypt(data, padding) / rsa.Decrypt(data, destination, padding) + private static final IDetectionRule RSA_DECRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("Decrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rsa.TryEncrypt(data, destination, padding, out bytesWritten) + private static final IDetectionRule RSA_TRY_ENCRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rsa.TryDecrypt(data, destination, padding, out bytesWritten) + private static final IDetectionRule RSA_TRY_DECRYPT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecrypt") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> CIPHER_OP_RULES = + List.of(RSA_ENCRYPT, RSA_DECRYPT, RSA_TRY_ENCRYPT, RSA_TRY_DECRYPT); + + // ========================================================================= + // Signing / verification operation rules + // Each rule covers every overload of the given method name (arities vary only + // by hash-algorithm / signature-padding / offset-length / output-buffer + // parameters, which are not individually tracked), mirroring the DSA + // SignData()/VerifyData() rules. Note that RSA has no TryVerifyData/ + // TryVerifyHash methods (verification returns a bool directly, so there is no + // output buffer to size). + // ========================================================================= + + // rsa.SignData(data, hashAlgorithm, padding) [+ offset/length or Stream overloads] + private static final IDetectionRule RSA_SIGN_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rsa.TrySignData(data, destination, hashAlgorithm, padding, out bytesWritten) + private static final IDetectionRule RSA_TRY_SIGN_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TrySignData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rsa.SignHash(hash, hashAlgorithm, padding) + private static final IDetectionRule RSA_SIGN_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rsa.TrySignHash(hash, destination, hashAlgorithm, padding, out bytesWritten) + private static final IDetectionRule RSA_TRY_SIGN_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TrySignHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rsa.VerifyData(data, signature, hashAlgorithm, padding) [+ offset/length or Stream overloads] + private static final IDetectionRule RSA_VERIFY_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rsa.VerifyHash(hash, signature, hashAlgorithm, padding) + private static final IDetectionRule RSA_VERIFY_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> SIGNATURE_OP_RULES = + List.of( + RSA_SIGN_DATA, + RSA_TRY_SIGN_DATA, + RSA_SIGN_HASH, + RSA_TRY_SIGN_HASH, + RSA_VERIFY_DATA, + RSA_VERIFY_HASH); + + // ========================================================================= + // Aggregated depending-rule list + // ========================================================================= + + /** Full set of depending rules for all RSA-derived classes. */ + private static final List> RSA_DEPENDING_RULES = + Stream.of( + Stream.of(RSA_SET_KEY_SIZE), + CIPHER_OP_RULES.stream(), + SIGNATURE_OP_RULES.stream()) + .flatMap(i -> i) + .toList(); + + // ========================================================================= + // Primary creation rules + // ========================================================================= + + // RSA.Create() / RSA.Create(int) / RSA.Create(RSAParameters) / RSA.Create(string) private static final IDetectionRule RSA_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -56,8 +262,9 @@ private DotNetRSA() { .withAnyParameters() .buildForContext(new KeyContext(Map.of("kind", "RSA"))) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(RSA_DEPENDING_RULES); + // new RSACryptoServiceProvider() / (int) / (CspParameters) / (int, CspParameters) private static final IDetectionRule RSA_CRYPTO_SERVICE_PROVIDER = new DetectionRuleBuilder() .createDetectionRule() @@ -67,10 +274,36 @@ private DotNetRSA() { .withAnyParameters() .buildForContext(new KeyContext(Map.of("kind", "RSA"))) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(RSA_DEPENDING_RULES); + + // new RSACng() / new RSACng(CngKey) / new RSACng(int) — CNG-backed implementation. + // Uses withAnyParameters() to avoid double-detection that would occur if a separate + // withoutParameters() rule were added alongside this one (see AES_CNG_NAMED). + private static final IDetectionRule RSA_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RSACng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "RSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(RSA_DEPENDING_RULES); + + // new RSAOpenSsl() / (int) / (IntPtr) / (RSAParameters) / (SafeEvpPKeyHandle) + private static final IDetectionRule RSA_OPENSSL = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RSAOpenSsl") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("RSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "RSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(RSA_DEPENDING_RULES); @Nonnull public static List> rules() { - return List.of(RSA_CREATE, RSA_CRYPTO_SERVICE_PROVIDER); + return List.of(RSA_CREATE, RSA_CRYPTO_SERVICE_PROVIDER, RSA_CNG, RSA_OPENSSL); } } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGenerator.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGenerator.java new file mode 100644 index 000000000..dcceafa4e --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGenerator.java @@ -0,0 +1,321 @@ +/* + * 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.detection.MethodMatcher; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.context.PRNGContext; +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 javax.annotation.Nonnull; + +/** + * Detection rules for {@code RandomNumberGenerator} and {@code RNGCryptoServiceProvider} in + * System.Security.Cryptography. + * + *

Classes/members covered (per the official API reference, verified via WebFetch of + * learn.microsoft.com — not guessed): + * + *

    + *
  • {@code RandomNumberGenerator.Create()} / {@code Create(string)} — instance factory (the + * {@code Create(string)} overload is marked {@code Obsolete} in recent .NET versions but + * remains valid, detectable legacy source). The returned instance exposes the abstract + * instance methods {@code GetBytes(byte[])}, {@code GetBytes(byte[], int, int)} and {@code + * GetNonZeroBytes(byte[])}, tracked as depending rules. + *
  • {@code RandomNumberGenerator}'s static-only members — the actual focus of this + * batch, not just {@code Create()}: {@code Fill(Span)}, {@code GetBytes(int)} / {@code + * GetBytes(Span)}, {@code GetHexString(int, bool)} / {@code GetHexString(Span, + * bool)}, {@code GetInt32(int)} / {@code GetInt32(int, int)}, {@code + * GetItems(ReadOnlySpan, int)} / {@code GetItems(ReadOnlySpan, Span)}, {@code + * GetNonZeroBytes(Span)}, {@code GetString(ReadOnlySpan, int)}, and {@code + * Shuffle(Span)}. "Using the static members of this class is the preferred way to + * generate random values" per the official documentation. + *
  • {@code RNGCryptoServiceProvider} — legacy CSP-backed implementation ({@code Obsolete} since + * .NET 6, still valid, detectable legacy source). Four constructor overloads ({@code ()}, + * {@code (byte[])}, {@code (CspParameters)}, {@code (string)}), with instance {@code + * GetBytes}/{@code GetNonZeroBytes} (each with a {@code byte[]} and a {@code Span}-based + * overload) tracked as depending rules. + *
+ * + *

Modeling decision — reused the existing {@code PseudorandomNumberGenerator} /{@code + * PRNGContext} concept instead of inventing a new mapper model class: the mapper module already + * has a generic "randomness source" concept used by both the Java and Go modules for the exact same + * kind of API (a call that produces cryptographically strong random output with no user-selectable + * algorithm identity): {@code java.security.SecureRandom} ({@link + * com.ibm.plugin.rules.detection.random.SecureRandomGetInstance} in the {@code java} module) and + * Go's {@code crypto/rand} ({@link com.ibm.plugin.rules.detection.gocrypto.GoCryptoRand} in the + * {@code go} module) both translate through {@code PRNGContext} to a {@code + * com.ibm.mapper.model.Algorithm} node typed as {@code + * com.ibm.mapper.model.PseudorandomNumberGenerator}. The Go precedent is architecturally the + * closest analogue to .NET's {@code RandomNumberGenerator}: both are "ask the OS/platform for + * cryptographically strong random bytes, no algorithm selection" APIs (unlike Java's {@code + * SecureRandom}, which supports named provider algorithms such as {@code "NativePRNG"} or {@code + * "SHA1PRNG"}). This file follows the Go precedent exactly: every self-contained creation/static + * call is captured with {@code ValueActionFactory<>("NATIVEPRNG")} and dispatched in the new {@code + * CSharpPRNGContextTranslator} to {@code new Algorithm("NATIVEPRNG", + * PseudorandomNumberGenerator.class, detectionLocation)} — no new mapper model class was created + * for this batch. + * + *

Modeling decision — static self-contained calls vs. instance depending-rule operations: + * mirrors the {@code HKDF} vs. {@code SP800108HmacCounterKdf} distinction established in {@link + * DotNetKeyDerivation}. {@code RandomNumberGenerator}'s static methods ({@code Fill}, {@code + * GetBytes(int)}/{@code GetBytes(Span)}, {@code GetHexString}, {@code GetInt32}, {@code + * GetItems}, {@code GetNonZeroBytes(Span)}, {@code GetString}, {@code Shuffle}) are each a + * complete, self-contained "the platform CSPRNG was used" event with no instance to track — they + * are top-level rules mapping directly to the {@code NATIVEPRNG} algorithm identity, exactly like + * {@code HKDF.Extract}/{@code Expand}/{@code DeriveKey}. Where an actual instance *is* tracked + * ({@code RandomNumberGenerator.Create()}'s or {@code RNGCryptoServiceProvider}'s instance {@code + * GetBytes}/{@code GetNonZeroBytes}), those calls are depending rules attached to the creation + * rule, translated to the generic {@code Generate} functionality node (mirrors {@code + * AES_GENERATE_IV} in {@link DotNetAES}, which also has no more specific {@code CipherAction} + * available) as a child of the already-identified {@code NATIVEPRNG} algorithm node. + * + *

As with every other file in this rule set, the ANTLR4-based C# engine cannot resolve parameter + * types (see {@code CSharpLanguageTranslation}) or values held in variables (see {@code + * CSharpTreeConverter} — only literals and bare identifiers are read), so all overloads that only + * differ by {@code byte[]} vs. {@code Span}/{@code ReadOnlySpan}, by an optional trailing + * {@code bool}/output-buffer parameter, or by generic type argument are collapsed into a single + * {@code withAnyParameters()} rule per method name. + * + *

Known gap — {@code RandomNumberGenerator.Fill}/{@code GetNonZeroBytes(Span)}/etc. + * called through a base-class-typed local variable that is itself the result of {@code + * RandomNumberGenerator.Create()}: only the truly-static call form ({@code + * RandomNumberGenerator.Fill(...)}, receiver text literally {@code "RandomNumberGenerator"}) is + * covered by the top-level static rules in this file. {@code Fill} and the static {@code + * GetBytes(Span)}/{@code GetNonZeroBytes(Span)} overloads are not also + * addressable as instance methods on a concrete {@code RandomNumberGenerator} object in real .NET + * (they are {@code static} only), so this is not an actual coverage gap — it is called out here + * only because it might look, at a glance, like a missing depending rule. + */ +@SuppressWarnings("java:S1192") +public final class DotNetRandomNumberGenerator { + + private DotNetRandomNumberGenerator() { + // nothing + } + + // ========================================================================= + // Depending rules — instance operations on an already-tracked RNG object, + // shared by RandomNumberGenerator.Create()/Create(string) and + // RNGCryptoServiceProvider's constructors (RNGCryptoServiceProvider derives from + // RandomNumberGenerator and exposes the identical GetBytes/GetNonZeroBytes instance API). + // ========================================================================= + + // rng.GetBytes(data) / rng.GetBytes(data, offset, count) + private static final IDetectionRule RNG_INSTANCE_GET_BYTES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GetBytes") + .shouldBeDetectedAs(new ValueActionFactory<>("GetBytes")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // rng.GetNonZeroBytes(data) + private static final IDetectionRule RNG_INSTANCE_GET_NON_ZERO_BYTES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GetNonZeroBytes") + .shouldBeDetectedAs(new ValueActionFactory<>("GetNonZeroBytes")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> RNG_INSTANCE_DEPENDING_RULES = + List.of(RNG_INSTANCE_GET_BYTES, RNG_INSTANCE_GET_NON_ZERO_BYTES); + + // ========================================================================= + // RandomNumberGenerator.Create() / Create(string) — instance factory. + // ========================================================================= + + // RandomNumberGenerator.Create() + private static final IDetectionRule RNG_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RandomNumberGenerator") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withoutParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(RNG_INSTANCE_DEPENDING_RULES); + + // RandomNumberGenerator.Create(string rngName) — Obsolete in recent .NET, still detectable. + private static final IDetectionRule RNG_CREATE_NAMED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RandomNumberGenerator") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(RNG_INSTANCE_DEPENDING_RULES); + + // ========================================================================= + // RandomNumberGenerator static-only methods — each call is both the "creation" and + // the "operation" at once (see class javadoc "Modeling decision"). Overloads distinguished + // only by byte[] vs. Span/ReadOnlySpan, by an optional trailing bool/output-buffer + // parameter, or by generic type argument are collapsed via withAnyParameters(). + // ========================================================================= + + // RandomNumberGenerator.Fill(Span data) + private static final IDetectionRule RNG_FILL = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RandomNumberGenerator") + .forMethods("Fill") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // RandomNumberGenerator.GetBytes(int count) / GetBytes(Span data) + private static final IDetectionRule RNG_STATIC_GET_BYTES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RandomNumberGenerator") + .forMethods("GetBytes") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // RandomNumberGenerator.GetHexString(int count, bool lowercase = false) + // / GetHexString(Span destination, bool lowercase = false) + private static final IDetectionRule RNG_GET_HEX_STRING = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RandomNumberGenerator") + .forMethods("GetHexString") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // RandomNumberGenerator.GetInt32(int toExclusive) / GetInt32(int fromInclusive, int + // toExclusive) + private static final IDetectionRule RNG_GET_INT32 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RandomNumberGenerator") + .forMethods("GetInt32") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // RandomNumberGenerator.GetItems(ReadOnlySpan choices, int length) + // / GetItems(ReadOnlySpan choices, Span destination) + private static final IDetectionRule RNG_GET_ITEMS = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RandomNumberGenerator") + .forMethods("GetItems") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // RandomNumberGenerator.GetNonZeroBytes(Span data) — static overload + private static final IDetectionRule RNG_STATIC_GET_NON_ZERO_BYTES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RandomNumberGenerator") + .forMethods("GetNonZeroBytes") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // RandomNumberGenerator.GetString(ReadOnlySpan choices, int length) + private static final IDetectionRule RNG_GET_STRING = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RandomNumberGenerator") + .forMethods("GetString") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // RandomNumberGenerator.Shuffle(Span values) + private static final IDetectionRule RNG_SHUFFLE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RandomNumberGenerator") + .forMethods("Shuffle") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // RNGCryptoServiceProvider — legacy CSP-backed implementation (Obsolete since .NET 6, still + // detectable legacy source). Constructor + instance GetBytes/GetNonZeroBytes, reusing the + // same depending-rule pair as RandomNumberGenerator.Create() above since both classes expose + // the identical instance API. + // ========================================================================= + + // new RNGCryptoServiceProvider() / (byte[] rgb) / (CspParameters cspParams) / (string str) + // — 4 constructor overloads, all collapsed via withAnyParameters() (mirrors the AesCng + // precedent in DotNetAES: a single withAnyParameters() rule avoids double-detection that + // would occur if a separate withoutParameters() rule were added alongside it). + private static final IDetectionRule RNG_CSP_CTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RNGCryptoServiceProvider") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) + .withAnyParameters() + .buildForContext(new PRNGContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(RNG_INSTANCE_DEPENDING_RULES); + + @Nonnull + public static List> rules() { + return List.of( + RNG_CREATE, + RNG_CREATE_NAMED, + RNG_FILL, + RNG_STATIC_GET_BYTES, + RNG_GET_HEX_STRING, + RNG_GET_INT32, + RNG_GET_ITEMS, + RNG_STATIC_GET_NON_ZERO_BYTES, + RNG_GET_STRING, + RNG_SHUFFLE, + RNG_CSP_CTOR); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA.java index 5911f9c64..c07eccd62 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA.java @@ -19,6 +19,7 @@ */ package com.ibm.plugin.rules.detection.dotnet; +import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; import com.ibm.engine.model.context.DigestContext; import com.ibm.engine.model.factory.ValueActionFactory; @@ -28,10 +29,37 @@ import javax.annotation.Nonnull; /** - * Detection rules for SHA hash algorithms in System.Security.Cryptography. + * Detection rules for the SHA/MD5 hash algorithm family in System.Security.Cryptography. * - *

Detects factory methods and concrete class constructors for SHA-1, SHA-256, SHA-384, and - * SHA-512 (and their {@code Managed} concrete variants). + *

Classes covered: + * + *

    + *
  • {@code MD5} — abstract base ({@code MD5.Create()}, {@code MD5.Create(string)}), {@code + * MD5Cng}, {@code MD5CryptoServiceProvider} + *
  • {@code SHA1} — abstract base ({@code SHA1.Create()}, {@code SHA1.Create(string)}), {@code + * SHA1Managed}, {@code SHA1Cng}, {@code SHA1CryptoServiceProvider} + *
  • {@code SHA256} — abstract base ({@code SHA256.Create()}, {@code SHA256.Create(string)}), + * {@code SHA256Managed}, {@code SHA256Cng}, {@code SHA256CryptoServiceProvider} + *
  • {@code SHA384} — abstract base ({@code SHA384.Create()}, {@code SHA384.Create(string)}), + * {@code SHA384Managed}, {@code SHA384Cng}, {@code SHA384CryptoServiceProvider} + *
  • {@code SHA512} — abstract base ({@code SHA512.Create()}, {@code SHA512.Create(string)}), + * {@code SHA512Managed}, {@code SHA512Cng}, {@code SHA512CryptoServiceProvider} + *
  • {@code RIPEMD160} — abstract base ({@code RIPEMD160.Create()}, {@code + * RIPEMD160.Create(string)}), {@code RIPEMD160Managed}. Note: RIPEMD160 only ever existed in + * .NET Framework (documented up to net framework 4.8.1); it was never ported to .NET Core/5+. + * It is detected here for legacy/.NET Framework source code. + *
+ * + *

Architecture: unlike cipher/signature algorithms, a hash algorithm's complete + * cryptographically-relevant information (which digest algorithm, which digest length) is already + * fully captured by the creation rule itself (e.g. {@code new SHA256Managed()} already says + * everything necessary — SHA-256, 256 bits). Operation methods inherited from {@code HashAlgorithm} + * ({@code ComputeHash}, {@code ComputeHash(byte[])}, {@code ComputeHash(Stream)}, {@code + * TransformBlock}, {@code TransformFinalBlock}, {@code TryComputeHash}, the static {@code + * HashData}/{@code TryHashData} helpers) do not add cryptographically relevant information to the + * CBOM model (unlike Encrypt vs. Decrypt for ciphers, or Sign vs. Verify for signatures). + * Consistent with the pre-existing rules in this file (which never attached depending rules for + * these operations), no depending rules are added here either. */ @SuppressWarnings("java:S1192") public final class DotNetSHA { @@ -40,7 +68,63 @@ private DotNetSHA() { // nothing } + // ========================================================================= + // MD5 + // ========================================================================= + + // MD5.Create() — abstract factory, no parameters + private static final IDetectionRule MD5_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MD5") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("MD5")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // MD5.Create("MD5") — named factory (obsolete in .NET 7+, still detectable) + private static final IDetectionRule MD5_CREATE_NAMED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MD5") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("MD5")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new MD5CryptoServiceProvider() — legacy CAPI implementation + private static final IDetectionRule MD5_CSP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MD5CryptoServiceProvider") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("MD5")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new MD5Cng() — CNG-backed implementation + private static final IDetectionRule MD5_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("MD5Cng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("MD5")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // ========================================================================= // SHA1 + // ========================================================================= + + // SHA1.Create() — abstract factory, no parameters private static final IDetectionRule SHA1_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -52,6 +136,19 @@ private DotNetSHA() { .inBundle(() -> "DotNet") .withDependingDetectionRules(List.of()); + // SHA1.Create("SHA1") — named factory (obsolete in .NET 7+, still detectable) + private static final IDetectionRule SHA1_CREATE_NAMED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA1") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA1")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new SHA1Managed() — pure-managed implementation private static final IDetectionRule SHA1_MANAGED = new DetectionRuleBuilder() .createDetectionRule() @@ -63,7 +160,35 @@ private DotNetSHA() { .inBundle(() -> "DotNet") .withDependingDetectionRules(List.of()); + // new SHA1Cng() — CNG-backed implementation + private static final IDetectionRule SHA1_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA1Cng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA1")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new SHA1CryptoServiceProvider() — legacy CAPI implementation + private static final IDetectionRule SHA1_CSP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA1CryptoServiceProvider") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA1")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // ========================================================================= // SHA256 + // ========================================================================= + + // SHA256.Create() — abstract factory, no parameters private static final IDetectionRule SHA256_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -75,6 +200,19 @@ private DotNetSHA() { .inBundle(() -> "DotNet") .withDependingDetectionRules(List.of()); + // SHA256.Create("SHA256") — named factory (obsolete in .NET 7+, still detectable) + private static final IDetectionRule SHA256_CREATE_NAMED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA256") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA256")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new SHA256Managed() — pure-managed implementation private static final IDetectionRule SHA256_MANAGED = new DetectionRuleBuilder() .createDetectionRule() @@ -86,7 +224,35 @@ private DotNetSHA() { .inBundle(() -> "DotNet") .withDependingDetectionRules(List.of()); + // new SHA256Cng() — CNG-backed implementation + private static final IDetectionRule SHA256_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA256Cng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA256")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new SHA256CryptoServiceProvider() — legacy CAPI implementation + private static final IDetectionRule SHA256_CSP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA256CryptoServiceProvider") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA256")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // ========================================================================= // SHA384 + // ========================================================================= + + // SHA384.Create() — abstract factory, no parameters private static final IDetectionRule SHA384_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -98,6 +264,19 @@ private DotNetSHA() { .inBundle(() -> "DotNet") .withDependingDetectionRules(List.of()); + // SHA384.Create("SHA384") — named factory (obsolete in .NET 7+, still detectable) + private static final IDetectionRule SHA384_CREATE_NAMED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA384") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA384")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new SHA384Managed() — pure-managed implementation private static final IDetectionRule SHA384_MANAGED = new DetectionRuleBuilder() .createDetectionRule() @@ -109,7 +288,35 @@ private DotNetSHA() { .inBundle(() -> "DotNet") .withDependingDetectionRules(List.of()); + // new SHA384Cng() — CNG-backed implementation + private static final IDetectionRule SHA384_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA384Cng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA384")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new SHA384CryptoServiceProvider() — legacy CAPI implementation + private static final IDetectionRule SHA384_CSP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA384CryptoServiceProvider") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA384")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // ========================================================================= // SHA512 + // ========================================================================= + + // SHA512.Create() — abstract factory, no parameters private static final IDetectionRule SHA512_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -121,6 +328,19 @@ private DotNetSHA() { .inBundle(() -> "DotNet") .withDependingDetectionRules(List.of()); + // SHA512.Create("SHA512") — named factory (obsolete in .NET 7+, still detectable) + private static final IDetectionRule SHA512_CREATE_NAMED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA512") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA512")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new SHA512Managed() — pure-managed implementation private static final IDetectionRule SHA512_MANAGED = new DetectionRuleBuilder() .createDetectionRule() @@ -132,24 +352,68 @@ private DotNetSHA() { .inBundle(() -> "DotNet") .withDependingDetectionRules(List.of()); - // MD5 - private static final IDetectionRule MD5_CREATE = + // new SHA512Cng() — CNG-backed implementation + private static final IDetectionRule SHA512_CNG = new DetectionRuleBuilder() .createDetectionRule() - .forObjectTypes("MD5") + .forObjectTypes("SHA512Cng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA512")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new SHA512CryptoServiceProvider() — legacy CAPI implementation + private static final IDetectionRule SHA512_CSP = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA512CryptoServiceProvider") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA512")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // ========================================================================= + // RIPEMD160 + // Note: RIPEMD160/RIPEMD160Managed only ever existed in .NET Framework (documentation + // covers up to netframework-4.8.1 only; no netcoreapp/net5+ monikers). It was never ported + // to .NET Core / modern .NET, so it is only relevant for legacy .NET Framework source code. + // ========================================================================= + + // RIPEMD160.Create() — abstract factory, no parameters + private static final IDetectionRule RIPEMD160_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RIPEMD160") .forMethods("Create") - .shouldBeDetectedAs(new ValueActionFactory<>("MD5")) + .shouldBeDetectedAs(new ValueActionFactory<>("RIPEMD160")) .withoutParameters() .buildForContext(new DigestContext()) .inBundle(() -> "DotNet") .withDependingDetectionRules(List.of()); - private static final IDetectionRule MD5_CSP = + // RIPEMD160.Create("System.Security.Cryptography.RIPEMD160") — named factory + private static final IDetectionRule RIPEMD160_CREATE_NAMED = new DetectionRuleBuilder() .createDetectionRule() - .forObjectTypes("MD5CryptoServiceProvider") + .forObjectTypes("RIPEMD160") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("RIPEMD160")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new RIPEMD160Managed() — pure-managed implementation + private static final IDetectionRule RIPEMD160_MANAGED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("RIPEMD160Managed") .forMethods("") - .shouldBeDetectedAs(new ValueActionFactory<>("MD5")) + .shouldBeDetectedAs(new ValueActionFactory<>("RIPEMD160")) .withoutParameters() .buildForContext(new DigestContext()) .inBundle(() -> "DotNet") @@ -158,15 +422,32 @@ private DotNetSHA() { @Nonnull public static List> rules() { return List.of( + MD5_CREATE, + MD5_CREATE_NAMED, + MD5_CSP, + MD5_CNG, SHA1_CREATE, + SHA1_CREATE_NAMED, SHA1_MANAGED, + SHA1_CNG, + SHA1_CSP, SHA256_CREATE, + SHA256_CREATE_NAMED, SHA256_MANAGED, + SHA256_CNG, + SHA256_CSP, SHA384_CREATE, + SHA384_CREATE_NAMED, SHA384_MANAGED, + SHA384_CNG, + SHA384_CSP, SHA512_CREATE, + SHA512_CREATE_NAMED, SHA512_MANAGED, - MD5_CREATE, - MD5_CSP); + SHA512_CNG, + SHA512_CSP, + RIPEMD160_CREATE, + RIPEMD160_CREATE_NAMED, + RIPEMD160_MANAGED); } } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA3.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA3.java new file mode 100644 index 000000000..c9244d052 --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA3.java @@ -0,0 +1,157 @@ +/* + * 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.DigestContext; +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 javax.annotation.Nonnull; + +/** + * Detection rules for the SHA-3/SHAKE hash algorithm family in System.Security.Cryptography. + * + *

Classes covered: + * + *

    + *
  • {@code SHA3_256} — abstract base, {@code SHA3_256.Create()} only + *
  • {@code SHA3_384} — abstract base, {@code SHA3_384.Create()} only + *
  • {@code SHA3_512} — abstract base, {@code SHA3_512.Create()} only + *
  • {@code Shake128} — sealed class, directly constructible via {@code new Shake128()} + *
  • {@code Shake256} — sealed class, directly constructible via {@code new Shake256()} + *
+ * + *

Unlike the SHA-1/SHA-2/MD5/RIPEMD160 family in {@link DotNetSHA}, none of these five classes + * has a {@code Create(string)} named-factory overload (verified against the official API reference + * for .NET 8/9/10/11 — the {@code Create()} method table for {@code SHA3_256}/{@code SHA3_384}/ + * {@code SHA3_512} lists only the parameterless overload), and none has legacy {@code *Managed}, + * {@code *Cng} or {@code *CryptoServiceProvider} implementations — these algorithms were introduced + * directly with .NET 8 and are platform-dependent (see {@code IsSupported}, which is a pure + * availability check and intentionally not modeled here, consistent with how other {@code + * IsSupported} properties are ignored across this rule set). + * + *

{@code Shake128}/{@code Shake256} are sealed classes (not abstract), have a public + * parameterless constructor, and have no {@code Create()} factory at all — they are + * instantiated directly with {@code new Shake128()} / {@code new Shake256()}, which is why their + * detection rule matches the constructor ({@code }) rather than a static factory method, the + * same pattern used for {@code AesManaged} in {@link DotNetAES}. + * + *

Why SHAKE's output-length parameter is not modeled as a depending rule: SHAKE128/256 + * are extendable-output functions (XOF) whose output length is variable and specified per call + * (e.g. {@code Shake128.HashData(data, outputLength)}, {@code shake.GetHashAndReset(outputLength)}, + * {@code shake.Read(outputLength)}). Per the official remarks: "The size of the XOF indicates the + * security strength of the algorithm, not the output size" — i.e. the "128"/"256" in the class name + * is a security-strength parameter (captured by the creation rule itself, analogous to AES-128 vs. + * AES-256), while the length passed to {@code HashData}/{@code GetHashAndReset}/{@code + * GetCurrentHash}/{@code Read} is an arbitrary output-length request that carries no additional + * cryptographically relevant information about the algorithm. This mirrors two independent existing + * conventions: (1) the pre-established rule in {@link DotNetSHA} that {@code ComputeHash} and + * friends add nothing beyond what the creation rule already captures, and (2) the Go module's + * {@code golang.org/x/crypto/sha3} rules ({@code GoCryptoSHA3}), which likewise detect only {@code + * NewShake128()}/{@code NewShake256()} and attach no depending rules for any subsequent read/output + * operation. No depending rules are added here either. + * + *

Both {@code SHA3_256}/{@code SHA3_384}/{@code SHA3_512} (fixed-length digests) and {@code + * Shake128}/{@code Shake256} (XOF) inherit the same set of hash-computation operations ({@code + * HashData}, {@code AppendData}, {@code TryHashData}, etc.) that, following the established + * convention above, are not modeled as depending rules for the fixed-digest classes either. + */ +@SuppressWarnings("java:S1192") +public final class DotNetSHA3 { + + private DotNetSHA3() { + // nothing + } + + // ========================================================================= + // SHA3-256 / SHA3-384 / SHA3-512 + // ========================================================================= + + // SHA3_256.Create() — abstract factory, no parameters, no named-factory overload + private static final IDetectionRule SHA3_256_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA3_256") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA3_256")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // SHA3_384.Create() — abstract factory, no parameters, no named-factory overload + private static final IDetectionRule SHA3_384_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA3_384") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA3_384")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // SHA3_512.Create() — abstract factory, no parameters, no named-factory overload + private static final IDetectionRule SHA3_512_CREATE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SHA3_512") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("SHA3_512")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // ========================================================================= + // Shake128 / Shake256 (extendable-output functions, XOF) + // ========================================================================= + + // new Shake128() — sealed class, no Create() factory, only a public constructor + private static final IDetectionRule SHAKE_128 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Shake128") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("Shake128")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + // new Shake256() — sealed class, no Create() factory, only a public constructor + private static final IDetectionRule SHAKE_256 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Shake256") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("Shake256")) + .withoutParameters() + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(List.of()); + + @Nonnull + public static List> rules() { + return List.of(SHA3_256_CREATE, SHA3_384_CREATE, SHA3_512_CREATE, SHAKE_128, SHAKE_256); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSlhDsa.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSlhDsa.java new file mode 100644 index 000000000..53406ecf2 --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetSlhDsa.java @@ -0,0 +1,370 @@ +/* + * 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.detection.MethodMatcher; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.SignatureContext; +import com.ibm.engine.model.factory.ParameterIdentifierFactory; +import com.ibm.engine.model.factory.SignatureActionFactory; +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 SLH-DSA (Stateless Hash-Based Digital Signature Algorithm, FIPS 205, formerly + * known as SPHINCS+) usage in {@code System.Security.Cryptography}. + * + *

API availability (verified against the official Microsoft Learn API reference, not + * assumed): {@code SlhDsa}, {@code SlhDsaCng}, {@code SlhDsaOpenSsl} and {@code + * SlhDsaAlgorithm} are documented for the {@code net-10.0} and {@code net-11.0} monikers (the + * {@code net-11.0} page redirects/renders identically to {@code net-10.0} content — {@code + * defaultMoniker: net-10.0} — both listing "Assembly: System.Security.Cryptography.dll" as in-box, + * plus "Assembly: Microsoft.Bcl.Cryptography.dll" — package {@code Microsoft.Bcl.Cryptography + * v11.0.0-preview.7.26381.103} — as a back-compat NuGet shim for {@code SlhDsa}/{@code + * SlhDsaCng}/{@code SlhDsaAlgorithm} on older TFMs; {@code SlhDsaOpenSsl} lists only the in-box + * assembly, mirroring {@code MLKemOpenSsl}/{@code MLDsaOpenSsl}). Unlike plain {@code MLDsa}/{@code + * MLKem}, the whole {@code SlhDsa} surface (base class, both derived classes, and {@code + * SlhDsaAlgorithm}) carries a class-level {@code + * [System.Diagnostics.CodeAnalysis.Experimental("SYSLIB5006")]} attribute — SLH-DSA is still a + * preview API as of .NET 10/11, unlike the now-stable plain ML-DSA/ML-KEM surface. + * + *

Classes covered: + * + *

    + *
  • {@code SlhDsa} — abstract base. All of its members are {@code static} factory methods (the + * only listed constructor, {@code SlhDsa(SlhDsaAlgorithm)}, is for derived classes, not + * called by ordinary consumer code): {@code GenerateKey(SlhDsaAlgorithm)}, {@code + * ImportSlhDsaPrivateKey(SlhDsaAlgorithm, byte[]/ReadOnlySpan<byte>)}, {@code + * ImportSlhDsaPublicKey(SlhDsaAlgorithm, byte[]/ReadOnlySpan<byte>)}, {@code + * ImportPkcs8PrivateKey(...)}, {@code ImportSubjectPublicKeyInfo(...)}, {@code + * ImportFromPem(...)}, {@code ImportEncryptedPkcs8PrivateKey(...)}, {@code + * ImportFromEncryptedPem(...)}. + *
  • {@code SlhDsaCng} — CNG-backed implementation ({@code SlhDsaCng(CngKey)} constructor). + *
  • {@code SlhDsaOpenSsl} — OpenSSL-backed implementation ({@code + * SlhDsaOpenSsl(SafeEvpPKeyHandle)} constructor). + *
+ * + *

Differences from ML-DSA, verified from the official reference rather than assumed by + * analogy (SLH-DSA is a hash-based signature scheme per FIPS 205, structurally unrelated to the + * lattice-based FIPS 204 ML-DSA, despite both exposing a similar C# surface): + * + *

    + *
  • No private-seed import. {@code SlhDsa} has no {@code ImportSlhDsaPrivateSeed} + * equivalent to {@code MLDsa.ImportMLDsaPrivateSeed} — confirmed absent from the verified + * method table. This makes sense: ML-DSA's seed-expansion key derivation is a + * lattice-specific construction, not part of the FIPS 205 SLH-DSA private key format. + *
  • No {@code SignMu}/{@code VerifyMu}. ML-DSA's {@code SignMu}/{@code VerifyMu} + * (signing a pre-computed FIPS 204 "mu" digest) are absent from {@code SlhDsa} — confirmed + * absent from the verified method table. This is expected: "mu" is a + * ML-DSA/Dilithium-specific message-representative construction with no FIPS 205 equivalent. + *
  • {@code SignPreHash}/{@code VerifyPreHash} do exist (the FIPS 205 pre-hash signing + * variant, {@code SignPreHash(byte[]/ReadOnlySpan<byte>, ..., string hashAlgorithmOid, + * ...)}) — confirmed present in the verified method table, with the exact same shape as + * ML-DSA's pre-hash methods. + *
  • No Composite variant. Unlike {@code CompositeMLDsa}, no {@code CompositeSlhDsa} + * class exists in the official reference (the {@code SlhDsa} page's "Derived" list contains + * only {@code SlhDsaCng} and {@code SlhDsaOpenSsl}), so no composite/hybrid detection or + * modeling compromise is needed here. + *
  • Parameter set names are completely different. {@code SlhDsaAlgorithm} exposes twelve + * static properties, verified from the official {@code SlhDsaAlgorithm} reference page: + * {@code SlhDsaSha2_128s}, {@code SlhDsaSha2_128f}, {@code SlhDsaSha2_192s}, {@code + * SlhDsaSha2_192f}, {@code SlhDsaSha2_256s}, {@code SlhDsaSha2_256f}, {@code + * SlhDsaShake128s}, {@code SlhDsaShake128f}, {@code SlhDsaShake192s}, {@code + * SlhDsaShake192f}, {@code SlhDsaShake256s}, {@code SlhDsaShake256f} — a hash-family + * (SHA2/SHAKE) x security-level (128/192/256) x speed-tradeoff (s=small signature/slower, + * f=fast/larger signature) matrix, nothing like ML-DSA's simple {@code MLDsa44}/{@code + * MLDsa65}/{@code MLDsa87} three-value set. + *
+ * + *

Architecture — {@code Import*} as primary creation rules, not skipped: exactly as + * established for ML-KEM/ML-DSA (see {@code DotNetMLKem}'s javadoc for the full rationale), every + * {@code Import*} method verified above on {@code SlhDsa} is a {@code static} factory that is the + * only way (besides {@code GenerateKey} or the {@code Cng}/{@code OpenSsl} native-interop + * constructors) to obtain an instance in the first place. They are therefore treated as primary + * creation rules here. + * + *

Two creation-rule shapes result from this: + * + *

    + *
  • Algorithm-parameterized ({@code GenerateKey}, {@code ImportSlhDsaPrivateKey}, {@code + * ImportSlhDsaPublicKey}): take an {@code SlhDsaAlgorithm} argument (e.g. {@code + * SlhDsaAlgorithm.SlhDsaSha2_128s}), a member-access expression the engine resolves to the + * bare identifier {@code "SlhDsaSha2_128s"} (see {@code CSharpLanguageTranslation}, the same + * mechanism {@code DotNetMLDsa}/{@code DotNetMLKem}'s rules rely on). Captured with {@code + * ParameterIdentifierFactory<>()} as a child of the top-level detection. + *
  • Structural imports ({@code ImportPkcs8PrivateKey}, {@code + * ImportSubjectPublicKeyInfo}, {@code ImportFromPem}, {@code ImportEncryptedPkcs8PrivateKey}, + * {@code ImportFromEncryptedPem}): the parameter set is embedded inside the encoded key + * material / PEM text, not present as a separate literal argument, so — exactly as for + * ML-KEM/ML-DSA — it cannot be recovered by this engine. These translate to a generic node + * with no {@code ParameterSetIdentifier} child, a known, inherent precision gap, not a bug. + *
+ * + *

{@code SlhDsaCng(CngKey)} and {@code SlhDsaOpenSsl(SafeEvpPKeyHandle)} wrap an + * already-existing native key handle and never receive an algorithm argument at all, so they always + * translate to the generic node, mirroring {@code MLDsaCng(CngKey)}/{@code + * MLDsaOpenSsl(SafeEvpPKeyHandle)}. + * + *

Sign/Verify modeling: {@code SlhDsa} is an ordinary signature primitive, so its + * operations are modeled with {@code SignatureActionFactory} under a {@code SignatureContext} — the + * exact same shape as {@code DotNetDSA}/{@code DotNetECDsa}/{@code DotNetMLDsa}'s {@code + * SignData}/{@code VerifyData} rules, reusing the existing generic SIGN/VERIFY dispatch in {@code + * CSharpSignatureContextTranslator} (no changes needed there: it already maps {@code + * SignatureAction.Action.SIGN}/{@code VERIFY} to {@code Sign}/{@code Verify} functionality nodes + * regardless of which algorithm produced the action, so this rule set is purely additive from its + * perspective). {@code SignPreHash}/{@code VerifyPreHash} (the FIPS 205 pre-hash variant) are + * modeled the same way as {@code SignData}/{@code VerifyData}, since the engine cannot meaningfully + * distinguish "what was hashed before signing" from "what was signed" without value tracking — no + * {@code SignMu}/{@code VerifyMu} rules exist here, since (per the verified reference) {@code + * SlhDsa} has no such methods. The {@code *Core} overrides ({@code SignDataCore}, {@code + * SignPreHashCore}, {@code VerifyDataCore}, {@code VerifyPreHashCore}) are protected extensibility + * hooks for subclassing, not called by ordinary consumer code, and are intentionally not modeled — + * consistent with not modeling {@code SignMuCore}/{@code VerifyMuCore} in {@code DotNetMLDsa}. + * + *

Mapper model reuse — no new model class needed: a repository-wide search ({@code grep + * -rl "SlhDsa\|SLHDSA\|SLH-DSA\|SPHINCS" mapper/}) found that {@code + * com.ibm.mapper.model.algorithms.SPHINCSPlus} (implementing {@code Signature}, {@code NAME = + * "SLH-DSA"}, documented as "Other Names and Related Standards: SPHINCS+") already existed before + * this batch — used by the BouncyCastle {@code SPHINCSPlusSigner} translation ({@code + * BcMessageSignerMapper}) — but its {@code asString()} only appended a {@code MessageDigest} child + * (never actually exercised — {@code SPHINCSPlusSigner} maps to a bare {@code new + * SPHINCSPlus(detectionLocation)} with no children, confirmed by grep — no existing test asserts + * {@code asString()} behavior with a child attached, so extending it is safe). This batch adds one + * new, purely additive {@code SPHINCSPlus(String parameterSetIdentifier, DetectionLocation)} + * constructor and extends {@code asString()} to also check for a {@code ParameterSetIdentifier} + * child (prepended with "-", independent of and before the pre-existing {@code MessageDigest} + * check, whose no-args/no-children call path is completely unchanged), so {@code asString()} now + * yields e.g. {@code "SLH-DSA-SHA2-128s"} — matching the exact FIPS 205 parameter set naming + * convention (hash family + security level + speed tradeoff) referenced by the class's own + * "cyclonedx.org/schema/cryptography-defs.json (algorithmName: SLH-DSA)" specification link. No new + * mapper model class was introduced for SLH-DSA — an existing, if previously underused, class was + * completed instead. + * + *

Not covered (deliberately, consistent with the rest of this rule set): the {@code + * Algorithm}/{@code IsSupported} getters (state reads, not configuration — no property setters + * exist on {@code SlhDsa} at all), {@code SlhDsaCng.GetKey()}/{@code + * SlhDsaOpenSsl.DuplicateKeyHandle()} (native-handle export, the {@code SlhDsa}-specific equivalent + * of the {@code Export*} methods skipped for RSA/ECDsa/ECDiffieHellman/MLDsa), {@code Dispose()}, + * and all {@code ExportXxx}/{@code TryExportXxx}/{@code ExportXxxPem} instance methods (standard + * PKCS#8/SPKI/PEM export — same convention as RSA/ECDsa/ECDiffieHellman/MLKem/MLDsa). + */ +@SuppressWarnings("java:S1192") +public final class DotNetSlhDsa { + + private DotNetSlhDsa() { + // nothing + } + + // ========================================================================= + // Sign / Verify operation rules (depending rules on any tracked SlhDsa-family variable, + // mirroring DotNetMLDsa.java's SignData/VerifyData/SignPreHash/VerifyPreHash rules; no + // SignMu/VerifyMu — confirmed absent from the official SlhDsa reference, see class javadoc) + // ========================================================================= + + // slhDsa.SignData(data, context) — 2 overloads (array-based and Span-based) + private static final IDetectionRule SLHDSA_SIGN_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // slhDsa.VerifyData(data, signature, context) — 2 overloads + private static final IDetectionRule SLHDSA_VERIFY_DATA = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyData") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // slhDsa.SignPreHash(hash, hashAlgorithmOid, context) — 2 overloads + private static final IDetectionRule SLHDSA_SIGN_PRE_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignPreHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // slhDsa.VerifyPreHash(hash, signature, hashAlgorithmOid, context) — 2 overloads + private static final IDetectionRule SLHDSA_VERIFY_PRE_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyPreHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + /** Full set of depending rules for {@code SlhDsa}/{@code SlhDsaCng}/{@code SlhDsaOpenSsl}. */ + private static final List> SLHDSA_DEPENDING_RULES = + List.of( + SLHDSA_SIGN_DATA, + SLHDSA_VERIFY_DATA, + SLHDSA_SIGN_PRE_HASH, + SLHDSA_VERIFY_PRE_HASH); + + // ========================================================================= + // SlhDsa — primary creation rules, algorithm-parameterized (SlhDsaAlgorithm argument captured + // as the parameter set, see class javadoc) + // ========================================================================= + + // SlhDsa.GenerateKey(SlhDsaAlgorithm.SlhDsaSha2_128s) + private static final IDetectionRule SLHDSA_GENERATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SlhDsa") + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("SLH-DSA")) + .withMethodParameter(MethodMatcher.ANY) // SlhDsaAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .buildForContext(new KeyContext(Map.of("kind", "SLHDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(SLHDSA_DEPENDING_RULES); + + // SlhDsa.ImportSlhDsaPrivateKey(SlhDsaAlgorithm.SlhDsaSha2_128s, privateKeyBytes) + private static final IDetectionRule SLHDSA_IMPORT_PRIVATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SlhDsa") + .forMethods("ImportSlhDsaPrivateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("SLH-DSA")) + .withMethodParameter(MethodMatcher.ANY) // SlhDsaAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(MethodMatcher.ANY) // private key bytes + .buildForContext(new KeyContext(Map.of("kind", "SLHDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(SLHDSA_DEPENDING_RULES); + + // SlhDsa.ImportSlhDsaPublicKey(SlhDsaAlgorithm.SlhDsaSha2_128s, publicKeyBytes) + private static final IDetectionRule SLHDSA_IMPORT_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SlhDsa") + .forMethods("ImportSlhDsaPublicKey") + .shouldBeDetectedAs(new ValueActionFactory<>("SLH-DSA")) + .withMethodParameter(MethodMatcher.ANY) // SlhDsaAlgorithm + .shouldBeDetectedAs(new ParameterIdentifierFactory<>()) + .asChildOfParameterWithId(-1) + .withMethodParameter(MethodMatcher.ANY) // public key bytes + .buildForContext(new KeyContext(Map.of("kind", "SLHDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(SLHDSA_DEPENDING_RULES); + + // ========================================================================= + // SlhDsa — primary creation rules, structural imports (no SlhDsaAlgorithm argument; see class + // javadoc for why the parameter set cannot be captured for these) + // ========================================================================= + + private static IDetectionRule structuralImportRule(@Nonnull String methodName) { + return new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SlhDsa") + .forMethods(methodName) + .shouldBeDetectedAs(new ValueActionFactory<>("SLH-DSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "SLHDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(SLHDSA_DEPENDING_RULES); + } + + // SlhDsa.ImportPkcs8PrivateKey(source) / SlhDsa.ImportSubjectPublicKeyInfo(source) / + // SlhDsa.ImportFromPem(pem) / SlhDsa.ImportEncryptedPkcs8PrivateKey(...) / + // SlhDsa.ImportFromEncryptedPem(...) + private static final IDetectionRule SLHDSA_IMPORT_PKCS8_PRIVATE_KEY = + structuralImportRule("ImportPkcs8PrivateKey"); + + private static final IDetectionRule SLHDSA_IMPORT_SUBJECT_PUBLIC_KEY_INFO = + structuralImportRule("ImportSubjectPublicKeyInfo"); + + private static final IDetectionRule SLHDSA_IMPORT_FROM_PEM = + structuralImportRule("ImportFromPem"); + + private static final IDetectionRule SLHDSA_IMPORT_ENCRYPTED_PKCS8_PRIVATE_KEY = + structuralImportRule("ImportEncryptedPkcs8PrivateKey"); + + private static final IDetectionRule SLHDSA_IMPORT_FROM_ENCRYPTED_PEM = + structuralImportRule("ImportFromEncryptedPem"); + + // ========================================================================= + // SlhDsa — primary creation rules, native-interop constructors (no SlhDsaAlgorithm argument; + // wrap an already-existing native key handle, mirroring MLDsaCng/MLDsaOpenSsl in DotNetMLDsa) + // ========================================================================= + + // new SlhDsaCng(cngKey) + private static final IDetectionRule SLHDSA_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SlhDsaCng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SLH-DSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "SLHDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(SLHDSA_DEPENDING_RULES); + + // new SlhDsaOpenSsl(safeEvpPKeyHandle) + private static final IDetectionRule SLHDSA_OPENSSL = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("SlhDsaOpenSsl") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("SLH-DSA")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "SLHDSA"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(SLHDSA_DEPENDING_RULES); + + @Nonnull + public static List> rules() { + return List.of( + SLHDSA_GENERATE_KEY, + SLHDSA_IMPORT_PRIVATE_KEY, + SLHDSA_IMPORT_PUBLIC_KEY, + SLHDSA_IMPORT_PKCS8_PRIVATE_KEY, + SLHDSA_IMPORT_SUBJECT_PUBLIC_KEY_INFO, + SLHDSA_IMPORT_FROM_PEM, + SLHDSA_IMPORT_ENCRYPTED_PKCS8_PRIVATE_KEY, + SLHDSA_IMPORT_FROM_ENCRYPTED_PEM, + SLHDSA_CNG, + SLHDSA_OPENSSL); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetTripleDES.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetTripleDES.java index 2f1892e4d..bb9ebff72 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetTripleDES.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetTripleDES.java @@ -19,23 +19,43 @@ */ package com.ibm.plugin.rules.detection.dotnet; +import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.Size; import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.factory.BlockSizeFactory; +import com.ibm.engine.model.factory.CipherActionFactory; +import com.ibm.engine.model.factory.KeySizeFactory; +import com.ibm.engine.model.factory.ModeFactory; +import com.ibm.engine.model.factory.PaddingFactory; 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.stream.Stream; import javax.annotation.Nonnull; /** - * Detection rules for Triple DES usage in System.Security.Cryptography. + * Detection rules for the Triple DES (3DES) family in System.Security.Cryptography. * - *

Detects: + *

Classes covered: * *

    - *
  • {@code TripleDES.Create()} — abstract factory - *
  • {@code new TripleDESCryptoServiceProvider()} — CAPI-backed (deprecated) + *
  • {@code TripleDES} — abstract base ({@code TripleDES.Create()}, {@code + * TripleDES.Create(string)}) + *
  • {@code TripleDESCryptoServiceProvider} — legacy CAPI implementation + *
  • {@code TripleDESCng} — CNG-backed implementation (ephemeral and persisted-key constructors) *
+ * + *

Architecture: all methods inherited from {@code SymmetricAlgorithm} (EncryptCbc, DecryptCbc, + * CreateEncryptor, property setters, etc.) are expressed as depending rules attached to + * each primary creation rule. The detection engine tracks the variable and fires these rules on + * every matching method call, regardless of the concrete TripleDES subclass. Like {@code Aes}, + * {@code TripleDES} has a CNG-backed subclass ({@code TripleDESCng}), but unlike {@code Aes} it has + * no AEAD variant, so depending-rule coverage mirrors {@code DotNetDES} and {@code DotNetRC2} + * exactly (no TripleDES-specific properties exist beyond those inherited from {@code + * SymmetricAlgorithm}). */ @SuppressWarnings("java:S1192") public final class DotNetTripleDES { @@ -44,6 +64,509 @@ private DotNetTripleDES() { // nothing } + // ========================================================================= + // Property setter rules (synthetic set_X method invocations) + // ========================================================================= + + // tdes.Mode = CipherMode.CBC → synthetic set_Mode(CipherMode.CBC) + private static final IDetectionRule TRIPLE_DES_SET_MODE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_Mode") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new ModeFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // tdes.KeySize = 192 → synthetic set_KeySize(192) + private static final IDetectionRule TRIPLE_DES_SET_KEY_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_KeySize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new KeySizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // tdes.Padding = PaddingMode.PKCS7 → synthetic set_Padding(PaddingMode.PKCS7) + private static final IDetectionRule TRIPLE_DES_SET_PADDING = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_Padding") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // tdes.FeedbackSize = 8 → synthetic set_FeedbackSize(8) + private static final IDetectionRule TRIPLE_DES_SET_FEEDBACK_SIZE = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_FeedbackSize") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new BlockSizeFactory<>(Size.UnitType.BIT)) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> PROPERTY_SETTER_RULES = + List.of( + TRIPLE_DES_SET_MODE, + TRIPLE_DES_SET_KEY_SIZE, + TRIPLE_DES_SET_PADDING, + TRIPLE_DES_SET_FEEDBACK_SIZE); + + // ========================================================================= + // CreateEncryptor / CreateDecryptor rules + // ========================================================================= + + // tdes.CreateEncryptor() + private static final IDetectionRule TRIPLE_DES_CREATE_ENCRYPTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateEncryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // tdes.CreateEncryptor(byte[] key, byte[] iv) + private static final IDetectionRule TRIPLE_DES_CREATE_ENCRYPTOR_WITH_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateEncryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withMethodParameter(MethodMatcher.ANY) // key bytes + .withMethodParameter(MethodMatcher.ANY) // iv bytes + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // tdes.CreateDecryptor() + private static final IDetectionRule TRIPLE_DES_CREATE_DECRYPTOR = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateDecryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // tdes.CreateDecryptor(byte[] key, byte[] iv) + private static final IDetectionRule TRIPLE_DES_CREATE_DECRYPTOR_WITH_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CreateDecryptor") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withMethodParameter(MethodMatcher.ANY) // key bytes + .withMethodParameter(MethodMatcher.ANY) // iv bytes + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptCbc / DecryptCbc rules + // Mode is constant "CBC" (from method name); padding is detected from last param. + // Two overloads: 3-param and 4-param (with output buffer). + // ========================================================================= + + // EncryptCbc(plaintext, iv, padding) + private static final IDetectionRule TRIPLE_DES_ENCRYPT_CBC_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCbc(plaintext, iv, destination, padding) [output-buffer overload] + private static final IDetectionRule TRIPLE_DES_ENCRYPT_CBC_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCbc(ciphertext, iv, padding) + private static final IDetectionRule TRIPLE_DES_DECRYPT_CBC_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCbc(ciphertext, iv, destination, padding) + private static final IDetectionRule TRIPLE_DES_DECRYPT_CBC_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptEcb / DecryptEcb rules + // Mode is constant "ECB" (from method name); no IV parameter. + // Two overloads: 2-param and 3-param (with output buffer). + // ========================================================================= + + // EncryptEcb(plaintext, padding) + private static final IDetectionRule TRIPLE_DES_ENCRYPT_ECB_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptEcb(plaintext, destination, padding) + private static final IDetectionRule TRIPLE_DES_ENCRYPT_ECB_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptEcb(ciphertext, padding) + private static final IDetectionRule TRIPLE_DES_DECRYPT_ECB_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptEcb(ciphertext, destination, padding) + private static final IDetectionRule TRIPLE_DES_DECRYPT_ECB_3 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // EncryptCfb / DecryptCfb rules + // Mode is constant "CFB"; padding detected from 3rd param; feedbackSize ignored. + // Two overloads: 4-param and 5-param (with output buffer). + // ========================================================================= + + // EncryptCfb(plaintext, iv, padding, feedbackSize) + private static final IDetectionRule TRIPLE_DES_ENCRYPT_CFB_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // EncryptCfb(plaintext, iv, destination, padding, feedbackSize) + private static final IDetectionRule TRIPLE_DES_ENCRYPT_CFB_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCfb(ciphertext, iv, padding, feedbackSize) + private static final IDetectionRule TRIPLE_DES_DECRYPT_CFB_4 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // DecryptCfb(ciphertext, iv, destination, padding, feedbackSize) + private static final IDetectionRule TRIPLE_DES_DECRYPT_CFB_5 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize (int) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // TryEncrypt* / TryDecrypt* rules + // Signatures: + // TryEncryptCbc(plaintext, iv, destination, out bytesWritten, padding) — 5 params + // TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, padding) — 5 params + // TryEncryptEcb(plaintext, destination, padding, out bytesWritten) — 4 params + // TryDecryptEcb(ciphertext, destination, padding, out bytesWritten) — 4 params + // TryEncryptCfb(plaintext, iv, destination, out bytesWritten, padding, fs) — 6 params + // TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, padding, fs) — 6 params + // ========================================================================= + + // TryEncryptCbc(plaintext, iv, destination, out bytesWritten, padding) + private static final IDetectionRule TRIPLE_DES_TRY_ENCRYPT_CBC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptCbc") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, padding) + private static final IDetectionRule TRIPLE_DES_TRY_DECRYPT_CBC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptCbc") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CBC")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptEcb(plaintext, destination, padding, out bytesWritten) + private static final IDetectionRule TRIPLE_DES_TRY_ENCRYPT_ECB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptEcb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptEcb(ciphertext, destination, padding, out bytesWritten) + private static final IDetectionRule TRIPLE_DES_TRY_DECRYPT_ECB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptEcb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .shouldBeDetectedAs(new ModeFactory<>("ECB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryEncryptCfb(plaintext, iv, destination, out bytesWritten, padding, feedbackSize) + private static final IDetectionRule TRIPLE_DES_TRY_ENCRYPT_CFB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryEncryptCfb") + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, padding, feedbackSize) + private static final IDetectionRule TRIPLE_DES_TRY_DECRYPT_CFB = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptCfb") + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // iv + .shouldBeDetectedAs(new ModeFactory<>("CFB")) + .withMethodParameter(MethodMatcher.ANY) // destination + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .withMethodParameter(MethodMatcher.ANY) // padding + .shouldBeDetectedAs(new PaddingFactory<>()) + .withMethodParameter(MethodMatcher.ANY) // feedbackSize + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Key / IV generation rules + // ========================================================================= + + // tdes.GenerateKey() — generates a new random key (size determined by KeySize property) + private static final IDetectionRule TRIPLE_DES_GENERATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("GenerateKey")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // tdes.GenerateIV() — generates a new random initialization vector + private static final IDetectionRule TRIPLE_DES_GENERATE_IV = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GenerateIV") + .shouldBeDetectedAs(new ValueActionFactory<>("GenerateIV")) + .withoutParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Aggregated depending-rule lists + // ========================================================================= + + /** + * All cipher operation rules that fire on a tracked TripleDES-family variable. Includes + * CreateEncryptor/CreateDecryptor, direct mode-specific Encrypt/Decrypt methods, Try* variants, + * and key/IV generation. + */ + private static final List> CIPHER_OP_RULES = + List.of( + TRIPLE_DES_CREATE_ENCRYPTOR, + TRIPLE_DES_CREATE_ENCRYPTOR_WITH_KEY, + TRIPLE_DES_CREATE_DECRYPTOR, + TRIPLE_DES_CREATE_DECRYPTOR_WITH_KEY, + TRIPLE_DES_ENCRYPT_CBC_3, + TRIPLE_DES_ENCRYPT_CBC_4, + TRIPLE_DES_DECRYPT_CBC_3, + TRIPLE_DES_DECRYPT_CBC_4, + TRIPLE_DES_ENCRYPT_ECB_2, + TRIPLE_DES_ENCRYPT_ECB_3, + TRIPLE_DES_DECRYPT_ECB_2, + TRIPLE_DES_DECRYPT_ECB_3, + TRIPLE_DES_ENCRYPT_CFB_4, + TRIPLE_DES_ENCRYPT_CFB_5, + TRIPLE_DES_DECRYPT_CFB_4, + TRIPLE_DES_DECRYPT_CFB_5, + TRIPLE_DES_TRY_ENCRYPT_CBC, + TRIPLE_DES_TRY_DECRYPT_CBC, + TRIPLE_DES_TRY_ENCRYPT_ECB, + TRIPLE_DES_TRY_DECRYPT_ECB, + TRIPLE_DES_TRY_ENCRYPT_CFB, + TRIPLE_DES_TRY_DECRYPT_CFB, + TRIPLE_DES_GENERATE_KEY, + TRIPLE_DES_GENERATE_IV); + + /** Full set of depending rules for all TripleDES-derived classes. */ + private static final List> TRIPLE_DES_DEPENDING_RULES = + Stream.concat(PROPERTY_SETTER_RULES.stream(), CIPHER_OP_RULES.stream()).toList(); + + // ========================================================================= + // Primary creation rules + // ========================================================================= + + // TripleDES.Create() — abstract factory, no parameters private static final IDetectionRule TRIPLE_DES_CREATE = new DetectionRuleBuilder() .createDetectionRule() @@ -53,8 +576,21 @@ private DotNetTripleDES() { .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(TRIPLE_DES_DEPENDING_RULES); + // TripleDES.Create("TripleDES") — named factory (obsolete in .NET 7+, still detectable) + private static final IDetectionRule TRIPLE_DES_CREATE_NAMED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("TripleDES") + .forMethods("Create") + .shouldBeDetectedAs(new ValueActionFactory<>("TRIPLEDES")) + .withMethodParameter(MethodMatcher.ANY) // algorithm name string + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(TRIPLE_DES_DEPENDING_RULES); + + // new TripleDESCryptoServiceProvider() — legacy CAPI implementation private static final IDetectionRule TRIPLE_DES_CSP = new DetectionRuleBuilder() .createDetectionRule() @@ -64,10 +600,26 @@ private DotNetTripleDES() { .withoutParameters() .buildForContext(new CipherContext()) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(TRIPLE_DES_DEPENDING_RULES); + + // new TripleDESCng() / new TripleDESCng("keyName") / new TripleDESCng("keyName", provider) / + // ... + // Matches all TripleDESCng constructors (ephemeral 0-param and persisted 1-3 params). + // Uses withAnyParameters() to avoid double-detection that would occur if a separate + // withoutParameters() rule were added alongside this one. + private static final IDetectionRule TRIPLE_DES_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("TripleDESCng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("TRIPLEDES")) + .withAnyParameters() + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(TRIPLE_DES_DEPENDING_RULES); @Nonnull public static List> rules() { - return List.of(TRIPLE_DES_CREATE, TRIPLE_DES_CSP); + return List.of(TRIPLE_DES_CREATE, TRIPLE_DES_CREATE_NAMED, TRIPLE_DES_CSP, TRIPLE_DES_CNG); } } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellman.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellman.java new file mode 100644 index 000000000..cf7b4bdfc --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellman.java @@ -0,0 +1,195 @@ +/* + * 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.detection.MethodMatcher; +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 X25519 (Curve25519-based Diffie-Hellman key agreement) usage in {@code + * System.Security.Cryptography}. + * + *

API verification (Microsoft Learn, 2026-08-20): {@code X25519DiffieHellman}, {@code + * X25519DiffieHellmanCng} and {@code X25519DiffieHellmanOpenSsl} were confirmed to exist under + * these exact names via {@code learn.microsoft.com/en-us/dotnet/api/...?view=net-11.0}. This is a + * brand-new, still-in-preview API: the doc pages show {@code Package: + * Microsoft.Bcl.Cryptography v11.0.0-preview.7.26381.103} and {@code ms.date: 2025-07-01} — i.e. it + * ships as a preview NuGet package layered on top of {@code System.Security.Cryptography.dll} (also + * backportable to older TFMs per the {@code netframework-4.6.2-pp}…{@code netframework-4.8.1-pp} + * "preview package" monikers listed on the class pages), not yet a stable in-box .NET API. + * Detection rules are added regardless, mirroring how the rest of this plugin detects APIs across + * their full supported version range. + * + *

Classes covered: + * + *

    + *
  • {@code X25519DiffieHellman} — abstract base. Verified to have no {@code Create()} + * factory (unlike {@code ECDiffieHellman}); the only way to obtain a fresh instance is the + * static factory {@code X25519DiffieHellman.GenerateKey()} (confirmed via the dedicated + * method-reference page: {@code public static X25519DiffieHellman GenerateKey()}). Its + * constructor is {@code protected X25519DiffieHellman()} (confirmed via the dedicated + * constructor page), so the class cannot be instantiated directly — only through {@code + * GenerateKey()} or one of the two concrete subclasses below. + *
  • {@code X25519DiffieHellmanCng} — CNG-backed implementation. Single constructor {@code + * X25519DiffieHellmanCng(CngKey)} (wraps an existing key). + *
  • {@code X25519DiffieHellmanOpenSsl} — OpenSSL-backed implementation. Single constructor + * {@code X25519DiffieHellmanOpenSsl(SafeEvpPKeyHandle)} (wraps an existing key handle). + *
+ * + *

Architecture: mirrors {@code DotNetECDiffieHellman.java} (Batch 3) as closely as the actual, + * verified API shape allows. The overload-collapsing rationale documented there (the ANTLR4-based + * C# engine cannot resolve parameter types — see {@code CSharpLanguageTranslation}) applies here + * too: all four {@code DeriveRawSecretAgreement} overloads (array-returning, {@code + * Span}-returning, and both taking either a raw public-key byte array or another {@code + * X25519DiffieHellman} instance as the peer) are collapsed into a single {@code + * withAnyParameters()} rule per method name. + * + *

Deliberately NOT ported from {@code DotNetECDiffieHellman.java}: + * + *

    + *
  • {@code DeriveKeyMaterial}, {@code DeriveKeyFromHash}, {@code DeriveKeyFromHmac}, {@code + * DeriveKeyTls} — these methods do not exist on {@code X25519DiffieHellman}. Verified + * against the full, official method table on the class's Microsoft Learn reference page: the + * only key-agreement-derivation method is {@code DeriveRawSecretAgreement} (4 overloads). + * X25519 in .NET is a strictly lower-level, "raw shared secret only" API compared to {@code + * ECDiffieHellman} — callers are expected to apply their own KDF. + *
  • a settable {@code KeySize} property — {@code X25519DiffieHellman} exposes {@code + * PrivateKeySizeInBytes}, {@code PublicKeySizeInBytes} and {@code SecretAgreementSizeInBytes} + * only as read-only {@code const int} fields (all fixed at 32 for Curve25519), not + * as a settable property — so there is no {@code set_KeySize}-style assignment to detect, + * unlike the configurable-curve {@code ECDiffieHellman.KeySize}. + *
+ * + *

Following the same modeling convention as {@code DotNetECDiffieHellman.java}: {@code + * DeriveRawSecretAgreement} returns the raw shared secret with no KDF post-processing, so it is + * translated to the generic {@code Generate} functionality node (not {@code KeyDerivation}) in + * {@code CSharpKeyContextTranslator}. + * + *

Mapper model: a dedicated {@link com.ibm.mapper.model.algorithms.X25519} algorithm + * model class already exists (verified via {@code grep -rl "X25519" mapper/} — it is already used + * by the JCA {@code XDH}/{@code X25519} key-agreement translation and by the Go {@code crypto/ecdh} + * curve translation), so it is reused as-is here; no new mapper model class was needed. + * + *

Known gaps (same reasoning as {@code DotNetECDiffieHellman.java}): + * + *

    + *
  • Reading the public key (e.g. {@code x25519.ExportPublicKey()} to send to a peer) is not + * modeled as a depending rule, mirroring the {@code PublicKey} property gap documented in + * {@code DotNetECDiffieHellman.java} for the same underlying reason: these calls carry no + * additional cryptographic information beyond "an X25519 key exists" (already captured by the + * primary creation rule), and speculatively wiring up the full family of {@code Import} / + * {@code Export} / {@code TryExport} methods (PKCS8, SPKI, PEM, encrypted-PKCS8 — none of + * which are X25519-specific) would add many rules without adding any new detectable + * cryptographic fact. // TODO: revisit if a future need arises to track key material + * export/import as its own finding. + *
  • {@code X25519DiffieHellmanCng.GetKey()} and {@code + * X25519DiffieHellmanOpenSsl.DuplicateKeyHandle()} — these return the underlying platform key + * handle ({@code CngKey} / {@code SafeEvpPKeyHandle}), not a new cryptographic fact; skipped + * for the same reason as the export methods above. + *
+ */ +@SuppressWarnings("java:S1192") +public final class DotNetX25519DiffieHellman { + + private DotNetX25519DiffieHellman() { + // nothing + } + + // ========================================================================= + // Key-derivation / secret-agreement operation rule + // ========================================================================= + + // x25519.DeriveRawSecretAgreement(otherPartyPublicKey) — raw shared secret, no KDF applied. + // Collapses all 4 overloads (byte[]/Span-returning, byte[]-vs-X25519DiffieHellman peer param). + private static final IDetectionRule X25519_DERIVE_RAW_SECRET_AGREEMENT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DeriveRawSecretAgreement") + .shouldBeDetectedAs(new ValueActionFactory<>("DeriveRawSecretAgreement")) + .withAnyParameters() + .buildForContext( + new KeyContext(Map.of("kind", "X25519_DERIVE_RAW_SECRET_AGREEMENT"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // ========================================================================= + // Aggregated depending-rule list + // ========================================================================= + + /** Full set of depending rules for all X25519DiffieHellman-derived classes. */ + private static final List> X25519_DEPENDING_RULES = + List.of(X25519_DERIVE_RAW_SECRET_AGREEMENT); + + // ========================================================================= + // Primary creation rules + // ========================================================================= + + // X25519DiffieHellman.GenerateKey() — static factory, no parameters (no Create() exists). + private static final IDetectionRule X25519_GENERATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("X25519DiffieHellman") + .forMethods("GenerateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("X25519")) + .withoutParameters() + .buildForContext(new KeyContext(Map.of("kind", "X25519"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(X25519_DEPENDING_RULES); + + // new X25519DiffieHellmanCng(CngKey) — CNG-backed implementation, wraps an existing key. + // Uses withAnyParameters() for consistency with ECDH_CNG / AES_CNG_NAMED even though only one + // constructor overload exists, to avoid a brittle single-parameter-type assumption. + private static final IDetectionRule X25519_CNG = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("X25519DiffieHellmanCng") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("X25519")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "X25519"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(X25519_DEPENDING_RULES); + + // new X25519DiffieHellmanOpenSsl(SafeEvpPKeyHandle) — OpenSSL-backed implementation, wraps an + // existing key handle. Uses withAnyParameters() for the same reason as X25519_CNG. + private static final IDetectionRule X25519_OPENSSL = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("X25519DiffieHellmanOpenSsl") + .forMethods("") + .shouldBeDetectedAs(new ValueActionFactory<>("X25519")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "X25519"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(X25519_DEPENDING_RULES); + + @Nonnull + public static List> rules() { + return List.of(X25519_GENERATE_KEY, X25519_CNG, X25519_OPENSSL); + } +} diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java index 59839d6e6..97ad85de1 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java @@ -42,6 +42,7 @@ import com.ibm.plugin.translation.translator.contexts.CSharpDigestContextTranslator; import com.ibm.plugin.translation.translator.contexts.CSharpKeyContextTranslator; import com.ibm.plugin.translation.translator.contexts.CSharpMacContextTranslator; +import com.ibm.plugin.translation.translator.contexts.CSharpPRNGContextTranslator; import com.ibm.plugin.translation.translator.contexts.CSharpSignatureContextTranslator; import java.util.List; import java.util.Optional; @@ -94,8 +95,12 @@ public Optional translate( .translate(bundleIdentifier, value, detectionValueContext, detectionLocation); } - if (detectionValueContext.is(PRNGContext.class) - || detectionValueContext.is(ProtocolContext.class)) { + if (detectionValueContext.is(PRNGContext.class)) { + return new CSharpPRNGContextTranslator() + .translate(bundleIdentifier, value, detectionValueContext, detectionLocation); + } + + if (detectionValueContext.is(ProtocolContext.class)) { return Optional.empty(); } diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java index 33da6d5cd..21724c80a 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java @@ -19,6 +19,7 @@ */ package com.ibm.plugin.translation.translator.contexts; +import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.BlockSize; import com.ibm.engine.model.CipherAction; import com.ibm.engine.model.IValue; @@ -33,9 +34,11 @@ import com.ibm.mapper.mapper.jca.JcaCipherOperationModeMapper; import com.ibm.mapper.mapper.jca.JcaModeMapper; import com.ibm.mapper.mapper.jca.JcaPaddingMapper; +import com.ibm.mapper.model.Cipher; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyLength; import com.ibm.mapper.model.algorithms.AES; +import com.ibm.mapper.model.algorithms.ChaCha20Poly1305; import com.ibm.mapper.model.algorithms.DES; import com.ibm.mapper.model.algorithms.DESede; import com.ibm.mapper.model.algorithms.RC2; @@ -73,8 +76,36 @@ public final class CSharpCipherContextTranslator Optional.of(new DESede(detectionLocation)); case "RSA" -> Optional.of(new RSA(detectionLocation)); case "RC2" -> Optional.of(new RC2(detectionLocation)); + case "CHACHA20-POLY1305" -> + Optional.of(new ChaCha20Poly1305(detectionLocation)); case "GENERATEKEY" -> Optional.of(new KeyGeneration(detectionLocation)); case "GENERATEIV" -> Optional.of(new Generate(detectionLocation)); + // DPAPI (System.Security.Cryptography.ProtectedData / ProtectedMemory / + // DpapiDataProtector — see DotNetProtectedData): the concrete underlying + // algorithm is not exposed by the API, so it is captured as a generic + // Cipher-kind Algorithm rather than an invented concrete algorithm name + // (mirrors the NATIVEPRNG precedent in CSharpPRNGContextTranslator). + case "DPAPI" -> + Optional.of( + new com.ibm.mapper.model.Algorithm( + "DPAPI", Cipher.class, detectionLocation)); + // Static one-shot ProtectedData/ProtectedMemory calls: identity + action + // are captured together (see DotNetProtectedData class javadoc "Modeling + // decision — static one-shot calls"). + case "DPAPI_PROTECT" -> { + com.ibm.mapper.model.Algorithm dpapi = + new com.ibm.mapper.model.Algorithm( + "DPAPI", Cipher.class, detectionLocation); + dpapi.put(new Encrypt(detectionLocation)); + yield Optional.of(dpapi); + } + case "DPAPI_UNPROTECT" -> { + com.ibm.mapper.model.Algorithm dpapi = + new com.ibm.mapper.model.Algorithm( + "DPAPI", Cipher.class, detectionLocation); + dpapi.put(new Decrypt(detectionLocation)); + yield Optional.of(dpapi); + } default -> Optional.empty(); }; if (result.isPresent()) { @@ -107,6 +138,19 @@ public final class CSharpCipherContextTranslator // From set_Padding property setter: PaddingMode.PKCS7 → "PKCS7" JcaPaddingMapper paddingMapper = new JcaPaddingMapper(); return paddingMapper.parse(padding.asString(), detectionLocation).map(p -> p); + } else if (value instanceof Algorithm) { + // SymmetricAlgorithm.Create(string) — unlike its four sibling Create(string) overloads + // (HashAlgorithm/KeyedHashAlgorithm/HMAC/AsymmetricAlgorithm), the official API + // reference for this one does not publish an explicit value table (see + // DotNetAlgorithmFactory javadoc), so this reuses the same well-known names already + // accepted in the ValueAction branch above for this codebase's .NET support. + return switch (value.asString().toUpperCase().trim()) { + case "AES" -> Optional.of(new AES(detectionLocation)); + case "DES" -> Optional.of(new DES(detectionLocation)); + case "3DES", "DESEDE", "TRIPLEDES" -> Optional.of(new DESede(detectionLocation)); + case "RC2" -> Optional.of(new RC2(detectionLocation)); + default -> Optional.empty(); + }; } return Optional.empty(); diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpDigestContextTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpDigestContextTranslator.java index 121acad2b..c5e30d716 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpDigestContextTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpDigestContextTranslator.java @@ -20,6 +20,7 @@ package com.ibm.plugin.translation.translator.contexts; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.IDetectionContext; @@ -27,8 +28,11 @@ import com.ibm.mapper.IContextTranslation; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.algorithms.MD5; +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.shake.SHAKE; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; import javax.annotation.Nonnull; @@ -50,6 +54,33 @@ public final class CSharpDigestContextTranslator implements IContextTranslation< case "SHA384" -> Optional.of(new SHA2(384, detectionLocation)); case "SHA512" -> Optional.of(new SHA2(512, detectionLocation)); case "MD5" -> Optional.of(new MD5(detectionLocation)); + case "RIPEMD160" -> Optional.of(new RIPEMD(160, detectionLocation)); + case "SHA3_256" -> Optional.of(new SHA3(256, detectionLocation)); + case "SHA3_384" -> Optional.of(new SHA3(384, detectionLocation)); + 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)); + default -> Optional.empty(); + }; + } else if (value instanceof Algorithm) { + // HashAlgorithm.Create(string) — string table verified against the official API + // reference (learn.microsoft.com), see DotNetAlgorithmFactory javadoc. Unlike the + // ValueAction branch above (fixed per-class identity), the concrete digest here is + // resolved purely from the captured runtime string. + return switch (value.asString().toUpperCase().trim()) { + case "SHA", + "SHA1", + "SYSTEM.SECURITY.CRYPTOGRAPHY.SHA1", + "SYSTEM.SECURITY.CRYPTOGRAPHY.HASHALGORITHM" -> + Optional.of(new SHA(detectionLocation)); + case "MD5", "SYSTEM.SECURITY.CRYPTOGRAPHY.MD5" -> + Optional.of(new MD5(detectionLocation)); + case "SHA256", "SHA-256", "SYSTEM.SECURITY.CRYPTOGRAPHY.SHA256" -> + Optional.of(new SHA2(256, detectionLocation)); + case "SHA384", "SHA-384", "SYSTEM.SECURITY.CRYPTOGRAPHY.SHA384" -> + Optional.of(new SHA2(384, detectionLocation)); + case "SHA512", "SHA-512", "SYSTEM.SECURITY.CRYPTOGRAPHY.SHA512" -> + Optional.of(new SHA2(512, detectionLocation)); default -> Optional.empty(); }; } 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 6b50ce83c..6dbb96e5d 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 @@ -20,8 +20,11 @@ package com.ibm.plugin.translation.translator.contexts; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeyAction; import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.ParameterIdentifier; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.DetectionContext; import com.ibm.engine.model.context.IDetectionContext; @@ -29,11 +32,24 @@ import com.ibm.mapper.IContextTranslation; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.ParameterSetIdentifier; 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.HKDF; +import com.ibm.mapper.model.algorithms.KDFCounter; +import com.ibm.mapper.model.algorithms.MGF1; +import com.ibm.mapper.model.algorithms.MLDSA; +import com.ibm.mapper.model.algorithms.MLKEM; +import com.ibm.mapper.model.algorithms.PBKDF1; import com.ibm.mapper.model.algorithms.PBKDF2; import com.ibm.mapper.model.algorithms.RSA; +import com.ibm.mapper.model.algorithms.SPHINCSPlus; +import com.ibm.mapper.model.algorithms.X25519; +import com.ibm.mapper.model.functionality.Decapsulate; +import com.ibm.mapper.model.functionality.Encapsulate; +import com.ibm.mapper.model.functionality.Generate; +import com.ibm.mapper.model.functionality.KeyDerivation; import com.ibm.mapper.utils.DetectionLocation; import java.util.Optional; import javax.annotation.Nonnull; @@ -55,12 +71,196 @@ public final class CSharpKeyContextTranslator implements IContextTranslation Optional.of(new RSA(detectionLocation)); case "ECDSA" -> Optional.of(new ECDSA(detectionLocation)); case "ECDH" -> Optional.of(new ECDH(detectionLocation)); + // X25519 (DotNetX25519DiffieHellman.java): reuses the existing X25519 mapper + // model class (already used by the JCA XDH/X25519 and Go crypto/ecdh + // translations) — no new mapper model class was needed. See that rule class's + // javadoc for why X25519DiffieHellman's API shape (no Create(), no + // DeriveKeyMaterial/FromHash/FromHmac/Tls, no settable KeySize) differs from + // ECDiffieHellman's. + case "X25519" -> Optional.of(new X25519(detectionLocation)); case "DSA" -> Optional.of(new DSA(detectionLocation)); + // MGF1 (DotNetLegacyFormatters.java, PKCS1MaskGenerationMethod): reuses the + // existing MGF1 mapper model class (already used by JcaMGFMapper/ + // JcaOAEPPaddingMapper for JCA's OAEP padding translation) — no new mapper model + // class was needed. KeyContext is reused here purely as the generic "identify + // which standalone algorithm was just constructed" context already used for the + // other cases in this switch, not because MGF1 is a "key" algorithm per se — see + // that rule class's javadoc for the full rationale. + case "MGF1" -> Optional.of(new MGF1(detectionLocation)); + // ML-KEM (DotNetMLKem.java): generic top-level node. The parameter set (when + // captured — see the ParameterIdentifier branch below) is attached as a child by + // the tree-shape-preserving translation process, yielding "ML-KEM-768" etc. via + // MLKEM#asString(). Reuses the mapper model class already used by the JCA, + // BouncyCastle and Go crypto/mlkem translations — no new model class needed. + case "KEM" -> Optional.of(new MLKEM(detectionLocation)); + // ML-DSA (DotNetMLDsa.java): generic top-level node, same shape as the "KEM" case + // above. The parameter set (when captured — see the ParameterIdentifier branch + // below) is attached as a child, yielding "ML-DSA-44"/"ML-DSA-65"/"ML-DSA-87" via + // MLDSA#asString(). Reuses the MLDSA mapper model class that already existed + // (added alongside MLKEM) — no new model class needed for plain ML-DSA. + // "MLDSA_COMPOSITE" (CompositeMLDsa/CompositeMLDsaCng) intentionally maps to the + // very same MLDSA node: no composite/hybrid algorithm concept exists yet in the + // mapper model (verified: grep -rl "Composite\|Hybrid" mapper/ found nothing), so + // rather than fabricate one or drop the detection, the full CompositeMLDsaAlgorithm + // name (e.g. "MLDsa44WithECDsaP256") is captured verbatim as the + // ParameterSetIdentifier below — see DotNetMLDsa's class javadoc for the full + // rationale and the recommendation for a future proper composite/hybrid model. + case "MLDSA", "MLDSA_COMPOSITE" -> Optional.of(new MLDSA(detectionLocation)); + // SLH-DSA (DotNetSlhDsa.java): generic top-level node, same shape as the "MLDSA" + // case above. The parameter set (when captured — see the ParameterIdentifier + // branch below) is attached as a child, yielding e.g. "SLH-DSA-SHA2-128s" via + // SPHINCSPlus#asString(). Reuses the SPHINCSPlus mapper model class that already + // existed (used by the BouncyCastle SPHINCSPlusSigner translation) — no new model + // class needed, per the class's "SLH-DSA .. Other Names: SPHINCS+" documentation. + case "SLHDSA" -> Optional.of(new SPHINCSPlus(detectionLocation)); case "KDF" -> Optional.of(new PBKDF2(detectionLocation)); + // Key derivation functions (DotNetKeyDerivation.java): HKDF and + // SP800108HmacCounterKdf's static one-shot calls, and PasswordDeriveBytes's + // constructor, map directly to their KDF algorithm model node — the class itself + // *is* the KDF (mirrors the "KDF" -> PBKDF2 case above for Rfc2898DeriveBytes), + // unlike the ECDiffieHellman derive operations below (see that case's comment). + case "KDF_HKDF" -> Optional.of(new HKDF(detectionLocation)); + case "KDF_SP800108" -> Optional.of(new KDFCounter(detectionLocation)); + case "KDF_PASSWORD_DERIVE_BYTES" -> Optional.of(new PBKDF1(detectionLocation)); + // Instance derive-operations on an already-identified KDF object + // (SP800108HmacCounterKdf.DeriveKey, PasswordDeriveBytes.GetBytes/ + // CryptDeriveKey): reuses the same generic KeyDerivation functionality node as + // the ECDiffieHellman derive operations below (Batch 3 pattern), captured as a + // child of the already-typed KDF algorithm node rather than folded into it. + case "KDF_SP800108_DERIVE_KEY", "KDF_PDB_GET_BYTES", "KDF_PDB_CRYPT_DERIVE_KEY" -> + Optional.of(new KeyDerivation(detectionLocation)); + // ECDiffieHellman key-derivation operations (DotNetECDiffieHellman.java): + // no typed CipherAction.Action fits "derive a key", so each operation is + // captured with a generic ValueActionFactory under its own "kind" and + // dispatched here (see that class's javadoc for the full rationale). + case "ECDH_DERIVE_KEY_MATERIAL", + "ECDH_DERIVE_KEY_FROM_HASH", + "ECDH_DERIVE_KEY_FROM_HMAC", + "ECDH_DERIVE_KEY_TLS" -> + Optional.of(new KeyDerivation(detectionLocation)); + // DeriveRawSecretAgreement returns the raw shared secret with no KDF + // post-processing, so it maps to the generic Generate functionality instead + // of KeyDerivation (mirroring how GenerateIV is translated in + // CSharpCipherContextTranslator). + case "ECDH_DERIVE_RAW_SECRET_AGREEMENT" -> + Optional.of(new Generate(detectionLocation)); + // X25519 DeriveRawSecretAgreement (DotNetX25519DiffieHellman.java): same + // reasoning as the ECDH case immediately above — raw shared secret, no KDF + // post-processing, so it maps to Generate rather than KeyDerivation. + case "X25519_DERIVE_RAW_SECRET_AGREEMENT" -> + Optional.of(new Generate(detectionLocation)); default -> Optional.empty(); }; } else if (value instanceof KeySize keySize) { return Optional.of(new KeyLength(keySize.getValue(), detectionLocation)); + } else if (value instanceof ParameterIdentifier parameterIdentifier + && detectionContext instanceof DetectionContext context + && "KEM".equals(context.get("kind").orElse(""))) { + // ML-KEM parameter set (DotNetMLKem.java): captured from the MLKemAlgorithm argument + // (e.g. MLKemAlgorithm.MLKem768, resolved to the bare enum member name "MLKem768" — see + // that class's javadoc). Mapped to the numeric suffix expected by + // MLKEM(int, DetectionLocation) / ParameterSetIdentifier, matching the + // "768"/"512"/"1024" + // values already produced by JcaKemMapper/GoCryptoKEMMapper for the same algorithm. + return switch (parameterIdentifier.asString()) { + case "MLKem512" -> + Optional.of(new ParameterSetIdentifier("512", detectionLocation)); + case "MLKem768" -> + Optional.of(new ParameterSetIdentifier("768", detectionLocation)); + case "MLKem1024" -> + Optional.of(new ParameterSetIdentifier("1024", detectionLocation)); + default -> Optional.empty(); + }; + } else if (value instanceof ParameterIdentifier parameterIdentifier + && detectionContext instanceof DetectionContext context + && "MLDSA".equals(context.get("kind").orElse(""))) { + // ML-DSA parameter set (DotNetMLDsa.java): captured from the MLDsaAlgorithm argument + // (e.g. MLDsaAlgorithm.MLDsa65, resolved to the bare enum member name "MLDsa65"), + // mapped to the numeric suffix expected by MLDSA(int, DetectionLocation) / + // ParameterSetIdentifier — mirrors the "KEM" branch above for MLKemAlgorithm. + return switch (parameterIdentifier.asString()) { + case "MLDsa44" -> Optional.of(new ParameterSetIdentifier("44", detectionLocation)); + case "MLDsa65" -> Optional.of(new ParameterSetIdentifier("65", detectionLocation)); + case "MLDsa87" -> Optional.of(new ParameterSetIdentifier("87", detectionLocation)); + default -> Optional.empty(); + }; + } else if (value instanceof ParameterIdentifier parameterIdentifier + && detectionContext instanceof DetectionContext context + && "MLDSA_COMPOSITE".equals(context.get("kind").orElse(""))) { + // Composite ML-DSA parameter set (DotNetMLDsa.java): the CompositeMLDsaAlgorithm + // member name (e.g. "MLDsa44WithECDsaP256") is a compound identifier, not a simple + // numeric parameter set, so it is captured verbatim rather than parsed — see + // DotNetMLDsa's class javadoc for why no structured composite/hybrid model is used. + return Optional.of( + new ParameterSetIdentifier(parameterIdentifier.asString(), detectionLocation)); + } else if (value instanceof ParameterIdentifier parameterIdentifier + && detectionContext instanceof DetectionContext context + && "SLHDSA".equals(context.get("kind").orElse(""))) { + // SLH-DSA parameter set (DotNetSlhDsa.java): captured from the SlhDsaAlgorithm + // argument (e.g. SlhDsaAlgorithm.SlhDsaSha2_128s, resolved to the bare enum member + // name "SlhDsaSha2_128s"), mapped to the FIPS 205 parameter set suffix (e.g. + // "SHA2-128s") expected by SPHINCSPlus's String constructor / ParameterSetIdentifier — + // mirrors the "MLDSA" branch above for MLDsaAlgorithm. All 12 values verified against + // the official SlhDsaAlgorithm reference page (SHA2 and SHAKE families, each with + // 128/192/256-bit security levels and s(mall)/f(ast) speed tradeoffs). + return switch (parameterIdentifier.asString()) { + case "SlhDsaSha2_128s" -> + Optional.of(new ParameterSetIdentifier("SHA2-128s", detectionLocation)); + case "SlhDsaSha2_128f" -> + Optional.of(new ParameterSetIdentifier("SHA2-128f", detectionLocation)); + case "SlhDsaSha2_192s" -> + Optional.of(new ParameterSetIdentifier("SHA2-192s", detectionLocation)); + case "SlhDsaSha2_192f" -> + Optional.of(new ParameterSetIdentifier("SHA2-192f", detectionLocation)); + case "SlhDsaSha2_256s" -> + Optional.of(new ParameterSetIdentifier("SHA2-256s", detectionLocation)); + case "SlhDsaSha2_256f" -> + Optional.of(new ParameterSetIdentifier("SHA2-256f", detectionLocation)); + case "SlhDsaShake128s" -> + Optional.of(new ParameterSetIdentifier("SHAKE-128s", detectionLocation)); + case "SlhDsaShake128f" -> + Optional.of(new ParameterSetIdentifier("SHAKE-128f", detectionLocation)); + case "SlhDsaShake192s" -> + Optional.of(new ParameterSetIdentifier("SHAKE-192s", detectionLocation)); + case "SlhDsaShake192f" -> + Optional.of(new ParameterSetIdentifier("SHAKE-192f", detectionLocation)); + case "SlhDsaShake256s" -> + Optional.of(new ParameterSetIdentifier("SHAKE-256s", detectionLocation)); + case "SlhDsaShake256f" -> + Optional.of(new ParameterSetIdentifier("SHAKE-256f", detectionLocation)); + default -> Optional.empty(); + }; + } else if (value instanceof KeyAction keyAction) { + // ML-KEM encapsulate/decapsulate (DotNetMLKem.java): KeyAction.Action already defines + // ENCAPSULATION/DECAPSULATION (used identically by the Go crypto/mlkem translation in + // GoKeyContextTranslator), so no new engine or mapper enum value was needed. + return switch (keyAction.getAction()) { + case ENCAPSULATION -> Optional.of(new Encapsulate(detectionLocation)); + case DECAPSULATION -> Optional.of(new Decapsulate(detectionLocation)); + default -> Optional.empty(); + }; + } else if (value instanceof Algorithm) { + // AsymmetricAlgorithm.Create(string) — string table verified against the official API + // reference (learn.microsoft.com), see DotNetAlgorithmFactory javadoc. Unlike the + // ValueAction branch above (dispatched by a fixed per-rule "kind" property), a single + // AsymmetricAlgorithm.Create(string) call site can produce any of RSA/DSA/ECDSA/ECDH, + // so the concrete algorithm here is resolved purely from the captured runtime string. + return switch (value.asString().toUpperCase().trim()) { + case "RSA", "SYSTEM.SECURITY.CRYPTOGRAPHY.RSA" -> + Optional.of(new RSA(detectionLocation)); + case "DSA", "SYSTEM.SECURITY.CRYPTOGRAPHY.DSA" -> + Optional.of(new DSA(detectionLocation)); + case "ECDSA", "ECDSACNG", "SYSTEM.SECURITY.CRYPTOGRAPHY.ECDSACNG" -> + Optional.of(new ECDSA(detectionLocation)); + case "ECDH", + "ECDIFFIEHELLMAN", + "ECDIFFIEHELLMANCNG", + "SYSTEM.SECURITY.CRYPTOGRAPHY.ECDIFFIEHELLMANCNG" -> + Optional.of(new ECDH(detectionLocation)); + // System.Security.Cryptography.AsymmetricAlgorithm has no concrete algorithm of + // its own per the official reference table — intentionally left unresolved. + default -> Optional.empty(); + }; } return Optional.empty(); diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpMacContextTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpMacContextTranslator.java index c0e0ddd68..9736892fd 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpMacContextTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpMacContextTranslator.java @@ -20,16 +20,22 @@ package com.ibm.plugin.translation.translator.contexts; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.IDetectionContext; import com.ibm.engine.rule.IBundle; import com.ibm.mapper.IContextTranslation; import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.mapper.model.algorithms.DESede; import com.ibm.mapper.model.algorithms.HMAC; +import com.ibm.mapper.model.algorithms.KMAC; import com.ibm.mapper.model.algorithms.MD5; +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.utils.DetectionLocation; import java.util.Optional; import javax.annotation.Nonnull; @@ -51,6 +57,50 @@ public final class CSharpMacContextTranslator implements IContextTranslation Optional.of(new HMAC(new SHA2(384, detectionLocation))); case "HMACSHA512" -> Optional.of(new HMAC(new SHA2(512, detectionLocation))); case "HMACMD5" -> Optional.of(new HMAC(new MD5(detectionLocation))); + case "HMACRIPEMD160" -> Optional.of(new HMAC(new RIPEMD(160, detectionLocation))); + case "HMACSHA3_256" -> Optional.of(new HMAC(new SHA3(256, detectionLocation))); + case "HMACSHA3_384" -> Optional.of(new HMAC(new SHA3(384, detectionLocation))); + case "HMACSHA3_512" -> Optional.of(new HMAC(new SHA3(512, detectionLocation))); + // Kmac128/KmacXof128 and Kmac256/KmacXof256 both reuse the existing KMAC mapper + // model (already used by the BouncyCastle KMAC mapper). The model has no concept + // distinguishing the fixed-output KMAC construction from the true + // extendable-output KMACXOF construction, so both collapse to the identical + // translated node per security-strength pair — see DotNetKMAC javadoc "Known + // modeling gap" section for the full rationale. + case "KMAC128", "KMACXOF128" -> Optional.of(new KMAC(128, detectionLocation)); + case "KMAC256", "KMACXOF256" -> Optional.of(new KMAC(256, detectionLocation)); + // MACTripleDES is not HMAC-based (see DotNetHMAC javadoc); reuse the DESede + // algorithm model "as" a Mac kind, the same idiom used elsewhere in this codebase + // to reinterpret DESede as a KeyWrap (JcaCipherMapper / BcWrapperMapper). + case "MACTRIPLEDES" -> + Optional.of(new DESede(Mac.class, new DESede(detectionLocation))); + default -> Optional.empty(); + }; + } else if (value instanceof Algorithm) { + // KeyedHashAlgorithm.Create(string) / HMAC.Create(string) — both methods document the + // identical string table, verified against the official API reference + // (learn.microsoft.com), see DotNetAlgorithmFactory javadoc. Unlike the ValueAction + // branch above (fixed per-class identity), the concrete MAC here is resolved purely + // from the captured runtime string. + return switch (value.asString().toUpperCase().trim()) { + case "HMACSHA1", + "SYSTEM.SECURITY.CRYPTOGRAPHY.HMACSHA1", + "SYSTEM.SECURITY.CRYPTOGRAPHY.HMAC", + "SYSTEM.SECURITY.CRYPTOGRAPHY.KEYEDHASHALGORITHM" -> + Optional.of(new HMAC(new SHA(detectionLocation))); + case "HMACSHA256", "SYSTEM.SECURITY.CRYPTOGRAPHY.HMACSHA256" -> + Optional.of(new HMAC(new SHA2(256, detectionLocation))); + case "HMACSHA384", "SYSTEM.SECURITY.CRYPTOGRAPHY.HMACSHA384" -> + Optional.of(new HMAC(new SHA2(384, detectionLocation))); + case "HMACSHA512", "SYSTEM.SECURITY.CRYPTOGRAPHY.HMACSHA512" -> + Optional.of(new HMAC(new SHA2(512, detectionLocation))); + case "HMACMD5", "SYSTEM.SECURITY.CRYPTOGRAPHY.HMACMD5" -> + Optional.of(new HMAC(new MD5(detectionLocation))); + case "HMACRIPEMD160", "SYSTEM.SECURITY.CRYPTOGRAPHY.HMACRIPEMD160" -> + Optional.of(new HMAC(new RIPEMD(160, detectionLocation))); + // Same DESede-as-Mac idiom as the ValueAction branch above. + case "MACTRIPLEDES", "SYSTEM.SECURITY.CRYPTOGRAPHY.MACTRIPLEDES" -> + Optional.of(new DESede(Mac.class, new DESede(detectionLocation))); default -> Optional.empty(); }; } diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpPRNGContextTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpPRNGContextTranslator.java new file mode 100644 index 000000000..f243d05fa --- /dev/null +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpPRNGContextTranslator.java @@ -0,0 +1,81 @@ +/* + * 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.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.IDetectionContext; +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.model.functionality.Generate; +import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; +import javax.annotation.Nonnull; + +/** + * Translates {@link com.ibm.engine.model.context.PRNGContext} detections for .NET APIs ({@code + * RandomNumberGenerator}, {@code RNGCryptoServiceProvider} — see {@link + * com.ibm.plugin.rules.detection.dotnet.DotNetRandomNumberGenerator}). + * + *

Mirrors {@code GoPRNGContextTranslator}: .NET's {@code RandomNumberGenerator} is + * architecturally the same shape as Go's {@code crypto/rand} (ask the platform for + * cryptographically strong random output, no user-selectable algorithm), so the same {@code + * "NATIVEPRNG"} {@link Algorithm} identity is reused instead of introducing a new mapper model + * class. + */ +public final class CSharpPRNGContextTranslator implements IContextTranslation { + + @Override + public @Nonnull Optional translate( + @Nonnull IBundle bundleIdentifier, + @Nonnull IValue value, + @Nonnull IDetectionContext detectionContext, + @Nonnull DetectionLocation detectionLocation) { + + if (value instanceof ValueAction) { + return switch (value.asString().toUpperCase().trim()) { + // RandomNumberGenerator.Create()/Create(string), every static self-contained + // method (Fill/GetBytes/GetHexString/GetInt32/GetItems/GetNonZeroBytes/ + // GetString/Shuffle), and the RNGCryptoServiceProvider constructor all identify + // the same underlying platform CSPRNG. + case "NATIVEPRNG" -> + Optional.of( + new Algorithm( + "NATIVEPRNG", + PseudorandomNumberGenerator.class, + detectionLocation)); + // Instance operations on an already-identified RNG object + // (RandomNumberGenerator.Create()'s or RNGCryptoServiceProvider's + // GetBytes/GetNonZeroBytes) — the RNG identity is already captured by the parent + // node, so these depending calls only need to record that a generate operation + // happened (mirrors GenerateIV in CSharpCipherContextTranslator, which has no + // more specific CipherAction available either). + case "GETBYTES", "GETNONZEROBYTES" -> Optional.of(new Generate(detectionLocation)); + default -> Optional.empty(); + }; + } + + return Optional.empty(); + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetAlgorithmFactoryTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetAlgorithmFactoryTestFile.cs new file mode 100644 index 000000000..eeca66d06 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetAlgorithmFactoryTestFile.cs @@ -0,0 +1,126 @@ +/* + * Test file for the generic, string-based Create(string) factory methods declared directly on + * the abstract base classes of System.Security.Cryptography (DotNetAlgorithmFactory.java): + * - SymmetricAlgorithm.Create(string) + * - HashAlgorithm.Create(string) + * - KeyedHashAlgorithm.Create(string) + * - HMAC.Create(string) + * - AsymmetricAlgorithm.Create(string) + */ + +using System.Security.Cryptography; + +public class DotNetAlgorithmFactoryTest +{ + // ------------------------------------------------------------------------- + // SymmetricAlgorithm.Create(string algName) + // ------------------------------------------------------------------------- + + public void TestSymmetricAlgorithmCreateAes() + { + SymmetricAlgorithm alg = SymmetricAlgorithm.Create("AES"); + } + + public void TestSymmetricAlgorithmCreateRc2() + { + SymmetricAlgorithm alg = SymmetricAlgorithm.Create("RC2"); + } + + public void TestSymmetricAlgorithmCreateTripleDes() + { + SymmetricAlgorithm alg = SymmetricAlgorithm.Create("3DES"); + } + + public void TestSymmetricAlgorithmCreateDes() + { + SymmetricAlgorithm alg = SymmetricAlgorithm.Create("DES"); + } + + // SymmetricAlgorithm.Create(string) followed by CreateEncryptor/CreateDecryptor: verifies that + // the generic, algorithm-independent depending rules attached to SYMMETRIC_ALGORITHM_CREATE + // (mirroring DotNetAES/DotNetRC2's CIPHER_OP_RULES) fire on the tracked variable. + public void TestSymmetricAlgorithmCreateAesWithEncryptorAndDecryptor() + { + var alg = SymmetricAlgorithm.Create("AES"); + byte[] key = new byte[32]; + byte[] iv = new byte[16]; + var enc = alg.CreateEncryptor(key, iv); + var dec = alg.CreateDecryptor(key, iv); + } + + // ------------------------------------------------------------------------- + // HashAlgorithm.Create(string hashName) + // ------------------------------------------------------------------------- + + public void TestHashAlgorithmCreateSha256() + { + HashAlgorithm alg = HashAlgorithm.Create("SHA256"); + } + + public void TestHashAlgorithmCreateShaHyphenated() + { + HashAlgorithm alg = HashAlgorithm.Create("SHA-512"); + } + + public void TestHashAlgorithmCreateMd5() + { + HashAlgorithm alg = HashAlgorithm.Create("MD5"); + } + + public void TestHashAlgorithmCreateShaBare() + { + HashAlgorithm alg = HashAlgorithm.Create("SHA"); + } + + // ------------------------------------------------------------------------- + // KeyedHashAlgorithm.Create(string algName) + // ------------------------------------------------------------------------- + + public void TestKeyedHashAlgorithmCreateHmacSha256() + { + KeyedHashAlgorithm alg = KeyedHashAlgorithm.Create("HMACSHA256"); + } + + public void TestKeyedHashAlgorithmCreateMacTripleDes() + { + KeyedHashAlgorithm alg = KeyedHashAlgorithm.Create("MACTripleDES"); + } + + // ------------------------------------------------------------------------- + // HMAC.Create(string algorithmName) + // ------------------------------------------------------------------------- + + public void TestHmacCreateHmacSha1() + { + HMAC alg = HMAC.Create("HMACSHA1"); + } + + public void TestHmacCreateFullyQualified() + { + HMAC alg = HMAC.Create("System.Security.Cryptography.HMACSHA256"); + } + + // ------------------------------------------------------------------------- + // AsymmetricAlgorithm.Create(string algName) + // ------------------------------------------------------------------------- + + public void TestAsymmetricAlgorithmCreateRsa() + { + AsymmetricAlgorithm alg = AsymmetricAlgorithm.Create("RSA"); + } + + public void TestAsymmetricAlgorithmCreateDsa() + { + AsymmetricAlgorithm alg = AsymmetricAlgorithm.Create("DSA"); + } + + public void TestAsymmetricAlgorithmCreateEcdsa() + { + AsymmetricAlgorithm alg = AsymmetricAlgorithm.Create("ECDsa"); + } + + public void TestAsymmetricAlgorithmCreateEcdh() + { + AsymmetricAlgorithm alg = AsymmetricAlgorithm.Create("ECDH"); + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetChaCha20Poly1305TestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetChaCha20Poly1305TestFile.cs new file mode 100644 index 000000000..7b321330d --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetChaCha20Poly1305TestFile.cs @@ -0,0 +1,92 @@ +/* + * Test file for System.Security.Cryptography ChaCha20Poly1305 detection rules. + * + * ChaCha20Poly1305 is a sealed AEAD cipher class, structurally analogous to AesGcm/AesCcm: + * constructed from a key, with Encrypt/Decrypt methods taking nonce, plaintext/ciphertext, + * tag and an optional associated-data buffer. + */ + +using System.Security.Cryptography; + +public class DotNetChaCha20Poly1305Test +{ + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + public void TestChaCha20Poly1305Ctor() + { + byte[] key = new byte[32]; + var chaCha20Poly1305 = new ChaCha20Poly1305(key); + } + + // ------------------------------------------------------------------------- + // Encrypt (with and without the optional associated-data argument) + // ------------------------------------------------------------------------- + + public void TestChaCha20Poly1305Encrypt() + { + byte[] key = new byte[32]; + var chaCha20Poly1305 = new ChaCha20Poly1305(key); + byte[] nonce = new byte[12]; + byte[] plaintext = new byte[32]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + byte[] associatedData = new byte[8]; + chaCha20Poly1305.Encrypt(nonce, plaintext, ciphertext, tag, associatedData); + } + + public void TestChaCha20Poly1305EncryptNoAad() + { + byte[] key = new byte[32]; + var chaCha20Poly1305 = new ChaCha20Poly1305(key); + byte[] nonce = new byte[12]; + byte[] plaintext = new byte[32]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + chaCha20Poly1305.Encrypt(nonce, plaintext, ciphertext, tag); + } + + // ------------------------------------------------------------------------- + // Decrypt (with and without the optional associated-data argument) + // ------------------------------------------------------------------------- + + public void TestChaCha20Poly1305Decrypt() + { + byte[] key = new byte[32]; + var chaCha20Poly1305 = new ChaCha20Poly1305(key); + byte[] nonce = new byte[12]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + byte[] plaintext = new byte[32]; + byte[] associatedData = new byte[8]; + chaCha20Poly1305.Decrypt(nonce, ciphertext, tag, plaintext, associatedData); + } + + public void TestChaCha20Poly1305DecryptNoAad() + { + byte[] key = new byte[32]; + var chaCha20Poly1305 = new ChaCha20Poly1305(key); + byte[] nonce = new byte[12]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + byte[] plaintext = new byte[32]; + chaCha20Poly1305.Decrypt(nonce, ciphertext, tag, plaintext); + } + + // ------------------------------------------------------------------------- + // Combined usage pattern (real-world scenario) + // ------------------------------------------------------------------------- + + public void TestChaCha20Poly1305FullFlow() + { + byte[] key = new byte[32]; + var chaCha20Poly1305 = new ChaCha20Poly1305(key); + byte[] nonce = new byte[12]; + byte[] plaintext = new byte[32]; + byte[] ciphertext = new byte[32]; + byte[] tag = new byte[16]; + byte[] associatedData = new byte[8]; + chaCha20Poly1305.Encrypt(nonce, plaintext, ciphertext, tag, associatedData); + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetECDiffieHellmanTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetECDiffieHellmanTestFile.cs index 2c918535e..700cfca9c 100755 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetECDiffieHellmanTestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetECDiffieHellmanTestFile.cs @@ -1,4 +1,123 @@ +/* + * Comprehensive test file for System.Security.Cryptography ECDiffieHellman (ECDH) + * detection rules. + * + * Covers all three ECDiffieHellman-related classes and their complete operational API surface: + * - ECDiffieHellman (abstract base) + * - ECDiffieHellmanCng, ECDiffieHellmanOpenSsl (derived from ECDiffieHellman) + * + * Architecture note: all methods inherited from ECDiffieHellman / AsymmetricAlgorithm + * (KeySize, DeriveKeyMaterial, DeriveKeyFromHash, DeriveKeyFromHmac, DeriveKeyTls, + * DeriveRawSecretAgreement) are covered once here. The detection engine tracks the variable + * and fires the same depending rules for every concrete ECDiffieHellman subclass. + * + * Known gap: the PublicKey property cannot be detected at all (see DotNetECDiffieHellman.java + * class javadoc for the full, verified explanation) — no test attempts it. + */ + using System.Security.Cryptography; -public class DotNetECDiffieHellmanTest { - public void TestECDHCreate() { var ecdh = ECDiffieHellman.Create(); } // Noncompliant + +public class DotNetECDiffieHellmanTest +{ + // ------------------------------------------------------------------------- + // Section 1: Factory methods / constructors + // ------------------------------------------------------------------------- + + public void TestECDHCreate() + { + var ecdh = ECDiffieHellman.Create(); + } + + public void TestECDHCreateWithCurve() + { + var ecdh = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256); + } + + public void TestECDHCng() + { + var ecdh = new ECDiffieHellmanCng(); + } + + public void TestECDHOpenSsl() + { + var ecdh = new ECDiffieHellmanOpenSsl(); + } + + // ------------------------------------------------------------------------- + // Section 2: Property setters (via assignment → synthetic set_X invocations) + // ------------------------------------------------------------------------- + + public void TestPropertyKeySize256() + { + var ecdh = ECDiffieHellman.Create(); + ecdh.KeySize = 256; + } + + public void TestPropertyKeySize384() + { + var ecdh = ECDiffieHellman.Create(); + ecdh.KeySize = 384; + } + + // ------------------------------------------------------------------------- + // Section 3: Key-derivation operations + // ------------------------------------------------------------------------- + + public void TestDeriveKeyMaterial() + { + var ecdh = ECDiffieHellman.Create(); + ECDiffieHellmanPublicKey otherPartyKey = null; + byte[] keyMaterial = ecdh.DeriveKeyMaterial(otherPartyKey); + } + + public void TestDeriveKeyFromHash() + { + var ecdh = ECDiffieHellman.Create(); + ECDiffieHellmanPublicKey otherPartyKey = null; + byte[] keyMaterial = ecdh.DeriveKeyFromHash(otherPartyKey, HashAlgorithmName.SHA256); + } + + public void TestDeriveKeyFromHmac() + { + var ecdh = ECDiffieHellman.Create(); + ECDiffieHellmanPublicKey otherPartyKey = null; + byte[] hmacKey = new byte[32]; + byte[] keyMaterial = ecdh.DeriveKeyFromHmac(otherPartyKey, HashAlgorithmName.SHA256, hmacKey); + } + + public void TestDeriveKeyTls() + { + var ecdh = ECDiffieHellman.Create(); + ECDiffieHellmanPublicKey otherPartyKey = null; + byte[] prfLabel = new byte[16]; + byte[] prfSeed = new byte[32]; + byte[] keyMaterial = ecdh.DeriveKeyTls(otherPartyKey, prfLabel, prfSeed); + } + + public void TestDeriveRawSecretAgreement() + { + var ecdh = ECDiffieHellman.Create(); + ECDiffieHellmanPublicKey otherPartyKey = null; + byte[] secret = ecdh.DeriveRawSecretAgreement(otherPartyKey); + } + + // ------------------------------------------------------------------------- + // Section 4: Combined usage patterns (real-world scenarios) + // Demonstrates that depending rules fire correctly for ALL derived classes. + // ------------------------------------------------------------------------- + + public void TestECDHCngFullFlow() + { + var ecdh = new ECDiffieHellmanCng(); + ecdh.KeySize = 384; + ECDiffieHellmanPublicKey otherPartyKey = null; + byte[] keyMaterial = ecdh.DeriveKeyMaterial(otherPartyKey); + } + + public void TestECDHOpenSslDeriveFlow() + { + var ecdh = new ECDiffieHellmanOpenSsl(); + ECDiffieHellmanPublicKey otherPartyKey = null; + byte[] keyMaterial = ecdh.DeriveKeyFromHash(otherPartyKey, HashAlgorithmName.SHA256); + } } diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetECDsaTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetECDsaTestFile.cs index addadc0c4..44cdf2734 100755 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetECDsaTestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetECDsaTestFile.cs @@ -1,4 +1,153 @@ +/* + * Comprehensive test file for System.Security.Cryptography ECDsa detection rules. + * + * Covers all three ECDsa-related classes and their complete operational API surface: + * - ECDsa (abstract base) + * - ECDsaCng, ECDsaOpenSsl (derived from ECDsa) + * + * Architecture note: all methods inherited from ECDsa / AsymmetricAlgorithm (KeySize, + * SignData, VerifyData, SignHash, VerifyHash, Try* variants, etc.) are covered once here. + * The detection engine tracks the variable and fires the same depending rules for every + * concrete ECDsa subclass. Unlike RSA, ECDSA has no Encrypt/Decrypt operations. + * + * The ECDsaCng constructor rule uses withAnyParameters() and therefore matches all four + * constructor overloads: ECDsaCng(), ECDsaCng(CngKey), ECDsaCng(ECCurve), ECDsaCng(int). + */ + using System.Security.Cryptography; -public class DotNetECDsaTest { - public void TestECDsaCreate() { var ecdsa = ECDsa.Create(); } // Noncompliant + +public class DotNetECDsaTest +{ + // ------------------------------------------------------------------------- + // Section 1: Factory methods / constructors + // ------------------------------------------------------------------------- + + public void TestECDsaCreate() + { + var ecdsa = ECDsa.Create(); // Noncompliant + } + + public void TestECDsaCreateWithCurve() + { + var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256); + } + + public void TestECDsaCng() + { + var ecdsa = new ECDsaCng(); + } + + public void TestECDsaOpenSsl() + { + var ecdsa = new ECDsaOpenSsl(); + } + + public void TestECDsaCngWithKey() + { + CngKey cngKey = null; + var ecdsa = new ECDsaCng(cngKey); + } + + public void TestECDsaCngWithCurve() + { + var ecdsa = new ECDsaCng(ECCurve.NamedCurves.nistP521); + } + + public void TestECDsaCngWithKeySize() + { + var ecdsa = new ECDsaCng(521); + } + + // ------------------------------------------------------------------------- + // Section 2: Property setters (via assignment → synthetic set_X invocations) + // ------------------------------------------------------------------------- + + public void TestPropertyKeySize256() + { + var ecdsa = ECDsa.Create(); + ecdsa.KeySize = 256; + } + + public void TestPropertyKeySize384() + { + var ecdsa = ECDsa.Create(); + ecdsa.KeySize = 384; + } + + // ------------------------------------------------------------------------- + // Section 3: SignData / TrySignData / VerifyData + // ------------------------------------------------------------------------- + + public void TestSignData() + { + var ecdsa = ECDsa.Create(); + byte[] data = new byte[64]; + byte[] signature = ecdsa.SignData(data, HashAlgorithmName.SHA256); + } + + public void TestTrySignData() + { + var ecdsa = ECDsa.Create(); + byte[] data = new byte[64]; + byte[] destination = new byte[256]; + int bytesWritten; + ecdsa.TrySignData(data, destination, HashAlgorithmName.SHA256, out bytesWritten); + } + + public void TestVerifyData() + { + var ecdsa = ECDsa.Create(); + byte[] data = new byte[64]; + byte[] signature = new byte[256]; + bool valid = ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256); + } + + // ------------------------------------------------------------------------- + // Section 4: SignHash / TrySignHash / VerifyHash + // ------------------------------------------------------------------------- + + public void TestSignHash() + { + var ecdsa = ECDsa.Create(); + byte[] hash = new byte[32]; + byte[] signature = ecdsa.SignHash(hash); + } + + public void TestTrySignHash() + { + var ecdsa = ECDsa.Create(); + byte[] hash = new byte[32]; + byte[] destination = new byte[256]; + int bytesWritten; + ecdsa.TrySignHash(hash, destination, out bytesWritten); + } + + public void TestVerifyHash() + { + var ecdsa = ECDsa.Create(); + byte[] hash = new byte[32]; + byte[] signature = new byte[256]; + bool valid = ecdsa.VerifyHash(hash, signature); + } + + // ------------------------------------------------------------------------- + // Section 5: Combined usage patterns (real-world scenarios) + // Demonstrates that depending rules fire correctly for ALL derived classes. + // ------------------------------------------------------------------------- + + public void TestECDsaCngFullFlow() + { + var ecdsa = new ECDsaCng(); + ecdsa.KeySize = 384; + byte[] data = new byte[64]; + byte[] signature = ecdsa.SignData(data, HashAlgorithmName.SHA384); + } + + public void TestECDsaOpenSslVerifyFlow() + { + var ecdsa = new ECDsaOpenSsl(); + byte[] data = new byte[64]; + byte[] signature = new byte[256]; + bool valid = ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256); + } } diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetHMACTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetHMACTestFile.cs index b652359c4..89d54ca7f 100755 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetHMACTestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetHMACTestFile.cs @@ -5,4 +5,9 @@ public class DotNetHMACTest { public void TestHmacSha384() { var h = new HMACSHA384(); } // Noncompliant public void TestHmacSha512() { var h = new HMACSHA512(); } // Noncompliant public void TestHmacMd5() { var h = new HMACMD5(); } // Noncompliant + public void TestHmacRipemd160() { var h = new HMACRIPEMD160(); } // Noncompliant + public void TestHmacSha3_256() { var h = new HMACSHA3_256(); } // Noncompliant + public void TestHmacSha3_384() { var h = new HMACSHA3_384(); } // Noncompliant + public void TestHmacSha3_512() { var h = new HMACSHA3_512(); } // Noncompliant + public void TestMacTripleDes() { var h = new MACTripleDES(); } // Noncompliant } diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetKMACTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetKMACTestFile.cs new file mode 100644 index 000000000..8442d3180 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetKMACTestFile.cs @@ -0,0 +1,7 @@ +using System.Security.Cryptography; +public class DotNetKMACTest { + public void TestKmac128() { byte[] key = new byte[16]; var m = new Kmac128(key); } // Noncompliant + public void TestKmac256() { byte[] key = new byte[32]; var m = new Kmac256(key); } // Noncompliant + public void TestKmacXof128() { byte[] key = new byte[16]; var m = new KmacXof128(key); } // Noncompliant + public void TestKmacXof256() { byte[] key = new byte[32]; var m = new KmacXof256(key); } // Noncompliant +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetKeyDerivationTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetKeyDerivationTestFile.cs new file mode 100644 index 000000000..534e2bfe1 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetKeyDerivationTestFile.cs @@ -0,0 +1,83 @@ +/* + * Comprehensive test file for System.Security.Cryptography KDF-family detection rules + * (DotNetKeyDerivation.java), excluding Rfc2898DeriveBytes (covered separately in + * DotNetRfc2898DeriveBytesTestFile.cs). + * + * Covers: + * - HKDF (static-only class: Extract, Expand, DeriveKey) + * - SP800108HmacCounterKdf (constructor + instance DeriveKey, and the static DeriveBytes + * one-shot overload) + * - PasswordDeriveBytes (constructor + instance GetBytes / CryptDeriveKey) + */ + +using System.Security.Cryptography; + +public class DotNetKeyDerivationTest +{ + // ------------------------------------------------------------------------- + // Section 1: HKDF (static-only, no instance) + // ------------------------------------------------------------------------- + + public void TestHkdfExtract() + { + byte[] ikm = new byte[32]; + byte[] salt = new byte[16]; + byte[] prk = HKDF.Extract(HashAlgorithmName.SHA256, ikm, salt); + } + + public void TestHkdfExpand() + { + byte[] prk = new byte[32]; + byte[] info = new byte[8]; + byte[] okm = HKDF.Expand(HashAlgorithmName.SHA256, prk, 32, info); + } + + public void TestHkdfDeriveKey() + { + byte[] ikm = new byte[32]; + byte[] salt = new byte[16]; + byte[] info = new byte[8]; + byte[] key = HKDF.DeriveKey(HashAlgorithmName.SHA256, ikm, 32, salt, info); + } + + // ------------------------------------------------------------------------- + // Section 2: SP800108HmacCounterKdf + // ------------------------------------------------------------------------- + + public void TestSp800108CtorAndDeriveKey() + { + byte[] key = new byte[32]; + var kdf = new SP800108HmacCounterKdf(key, HashAlgorithmName.SHA256); + byte[] label = new byte[8]; + byte[] context = new byte[8]; + byte[] derived = kdf.DeriveKey(label, context, 32); + } + + public void TestSp800108StaticDeriveBytes() + { + byte[] key = new byte[32]; + byte[] label = new byte[8]; + byte[] context = new byte[8]; + byte[] derived = SP800108HmacCounterKdf.DeriveBytes( + key, HashAlgorithmName.SHA256, label, context, 32); + } + + // ------------------------------------------------------------------------- + // Section 3: PasswordDeriveBytes + // ------------------------------------------------------------------------- + + public void TestPasswordDeriveBytesGetBytes() + { + byte[] salt = new byte[16]; + var pdb = new PasswordDeriveBytes("password", salt); + byte[] derived = pdb.GetBytes(16); + } + + public void TestPasswordDeriveBytesCryptDeriveKey() + { + byte[] salt = new byte[16]; + var pdb = new PasswordDeriveBytes("password", salt, "SHA1", 100); + byte[] iv = new byte[8]; + byte[] key = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, iv); + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetLegacyFormattersTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetLegacyFormattersTestFile.cs new file mode 100644 index 000000000..774c2978d --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetLegacyFormattersTestFile.cs @@ -0,0 +1,138 @@ +/* + * Test file for the legacy Formatter/Deformatter and mask-generation detection rules + * (DotNetLegacyFormatters.java). + * + * Covers all nine classes: + * - DSASignatureFormatter / DSASignatureDeformatter + * - RSAPKCS1SignatureFormatter / RSAPKCS1SignatureDeformatter + * - RSAOAEPKeyExchangeFormatter / RSAOAEPKeyExchangeDeformatter + * - RSAPKCS1KeyExchangeFormatter / RSAPKCS1KeyExchangeDeformatter + * - PKCS1MaskGenerationMethod + * + * Note: the parameterless constructor overload is used throughout (rather than passing a + * DSA/RSA instance from DSA.Create()/RSA.Create()) so that each test method exercises exactly + * this file's rules in isolation, without also triggering DotNetDSA.java's/DotNetRSA.java's own + * DSA.Create()/RSA.Create() detection rules. This also directly demonstrates the documented + * SetKey gap: the Formatter/Deformatter is detected purely from its own class name, independent + * of whether (or how) a real key was ever wired up via SetKey. + */ + +using System.Security.Cryptography; + +public class DotNetLegacyFormattersTest +{ + // ------------------------------------------------------------------------- + // DSASignatureFormatter / DSASignatureDeformatter + // ------------------------------------------------------------------------- + + public void TestDsaSignatureFormatterCreateSignature() + { + var formatter = new DSASignatureFormatter(); // Noncompliant + byte[] hash = new byte[20]; + byte[] signature = formatter.CreateSignature(hash); + } + + public void TestDsaSignatureFormatterSetHashAlgorithm() + { + var formatter = new DSASignatureFormatter(); + formatter.SetHashAlgorithm("SHA1"); + } + + public void TestDsaSignatureDeformatterVerifySignature() + { + var deformatter = new DSASignatureDeformatter(); + byte[] hash = new byte[20]; + byte[] signature = new byte[40]; + bool valid = deformatter.VerifySignature(hash, signature); + } + + // ------------------------------------------------------------------------- + // RSAPKCS1SignatureFormatter / RSAPKCS1SignatureDeformatter + // ------------------------------------------------------------------------- + + public void TestRsaPkcs1SignatureFormatterCreateSignature() + { + var formatter = new RSAPKCS1SignatureFormatter(); + byte[] hash = new byte[32]; + byte[] signature = formatter.CreateSignature(hash); + } + + public void TestRsaPkcs1SignatureFormatterSetHashAlgorithm() + { + var formatter = new RSAPKCS1SignatureFormatter(); + formatter.SetHashAlgorithm("SHA256"); + } + + public void TestRsaPkcs1SignatureDeformatterVerifySignature() + { + var deformatter = new RSAPKCS1SignatureDeformatter(); + byte[] hash = new byte[32]; + byte[] signature = new byte[256]; + bool valid = deformatter.VerifySignature(hash, signature); + } + + // ------------------------------------------------------------------------- + // RSAOAEPKeyExchangeFormatter / RSAOAEPKeyExchangeDeformatter + // ------------------------------------------------------------------------- + + public void TestRsaOaepKeyExchangeFormatterCreateKeyExchange1() + { + var formatter = new RSAOAEPKeyExchangeFormatter(); + byte[] key = new byte[32]; + byte[] encryptedKey = formatter.CreateKeyExchange(key); + } + + public void TestRsaOaepKeyExchangeFormatterCreateKeyExchange2() + { + var formatter = new RSAOAEPKeyExchangeFormatter(); + byte[] key = new byte[32]; + byte[] encryptedKey = formatter.CreateKeyExchange(key, typeof(Aes)); + } + + public void TestRsaOaepKeyExchangeDeformatterDecryptKeyExchange() + { + var deformatter = new RSAOAEPKeyExchangeDeformatter(); + byte[] encryptedKey = new byte[256]; + byte[] key = deformatter.DecryptKeyExchange(encryptedKey); + } + + // ------------------------------------------------------------------------- + // RSAPKCS1KeyExchangeFormatter / RSAPKCS1KeyExchangeDeformatter + // ------------------------------------------------------------------------- + + public void TestRsaPkcs1KeyExchangeFormatterCreateKeyExchange1() + { + var formatter = new RSAPKCS1KeyExchangeFormatter(); + byte[] key = new byte[32]; + byte[] encryptedKey = formatter.CreateKeyExchange(key); + } + + public void TestRsaPkcs1KeyExchangeFormatterCreateKeyExchange2() + { + var formatter = new RSAPKCS1KeyExchangeFormatter(); + byte[] key = new byte[32]; + byte[] encryptedKey = formatter.CreateKeyExchange(key, typeof(Aes)); + } + + public void TestRsaPkcs1KeyExchangeDeformatterDecryptKeyExchange() + { + var deformatter = new RSAPKCS1KeyExchangeDeformatter(); + byte[] encryptedKey = new byte[256]; + byte[] key = deformatter.DecryptKeyExchange(encryptedKey); + } + + // ------------------------------------------------------------------------- + // PKCS1MaskGenerationMethod + // ------------------------------------------------------------------------- + + public void TestPkcs1MaskGenerationMethodCreate() + { + var mgf = new PKCS1MaskGenerationMethod(); + } + + public void TestPkcs1MaskGenerationMethodSetHashName() + { + var mgf = new PKCS1MaskGenerationMethod(); + mgf.HashName = "SHA256"; + } +} 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 000000000..3a7145948 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetMLDsaTestFile.cs @@ -0,0 +1,266 @@ +/* + * Comprehensive test file for System.Security.Cryptography ML-DSA (MLDsa, MLDsaCng, + * MLDsaOpenSsl) and Composite ML-DSA (CompositeMLDsa, CompositeMLDsaCng) detection rules. + * + * ML-DSA (FIPS 204) is documented for the net-10.0/net-11.0 monikers, with no [Experimental] + * attribute on the plain MLDsa/MLDsaCng/MLDsaOpenSsl surface. Composite ML-DSA additionally + * carries [Experimental("SYSLIB5006")] at the class level (still a preview API). See + * DotNetMLDsa.java's class javadoc for the full, verified explanation of what is/isn't modeled + * here and why (in particular the Composite ML-DSA modeling compromise). + */ + +using System.Security.Cryptography; + +public class DotNetMLDsaTest +{ + // ------------------------------------------------------------------------- + // Section 1: MLDsa algorithm-parameterized creation (parameter set captured from the + // MLDsaAlgorithm argument) + // ------------------------------------------------------------------------- + + public void TestGenerateKey44() + { + var dsa = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa44); + } + + public void TestGenerateKey65() + { + var dsa = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65); + } + + public void TestGenerateKey87() + { + var dsa = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa87); + } + + public void TestImportMLDsaPrivateKey() + { + byte[] privateKeyBytes = new byte[64]; + var dsa = MLDsa.ImportMLDsaPrivateKey(MLDsaAlgorithm.MLDsa65, privateKeyBytes); + } + + public void TestImportMLDsaPrivateSeed() + { + byte[] seedBytes = new byte[32]; + var dsa = MLDsa.ImportMLDsaPrivateSeed(MLDsaAlgorithm.MLDsa44, seedBytes); + } + + public void TestImportMLDsaPublicKey() + { + byte[] publicKeyBytes = new byte[64]; + var dsa = MLDsa.ImportMLDsaPublicKey(MLDsaAlgorithm.MLDsa87, publicKeyBytes); + } + + // ------------------------------------------------------------------------- + // Section 2: MLDsa structural imports (no MLDsaAlgorithm argument — the parameter set is + // embedded in the encoded key material / PEM text and cannot be recovered) + // ------------------------------------------------------------------------- + + public void TestImportPkcs8PrivateKey() + { + byte[] pkcs8Bytes = new byte[64]; + var dsa = MLDsa.ImportPkcs8PrivateKey(pkcs8Bytes); + } + + public void TestImportSubjectPublicKeyInfo() + { + byte[] spkiBytes = new byte[64]; + var dsa = MLDsa.ImportSubjectPublicKeyInfo(spkiBytes); + } + + public void TestImportFromPem() + { + string pem = "-----BEGIN PRIVATE KEY-----"; + var dsa = MLDsa.ImportFromPem(pem); + } + + public void TestImportEncryptedPkcs8PrivateKey() + { + byte[] passwordBytes = new byte[16]; + byte[] encryptedBytes = new byte[64]; + var dsa = MLDsa.ImportEncryptedPkcs8PrivateKey(passwordBytes, encryptedBytes); + } + + public void TestImportFromEncryptedPem() + { + string pem = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; + string password = "hunter2"; + var dsa = MLDsa.ImportFromEncryptedPem(pem, password); + } + + // ------------------------------------------------------------------------- + // Section 3: MLDsa native-interop constructors (no MLDsaAlgorithm argument — wrap an + // already-existing native key handle) + // ------------------------------------------------------------------------- + + public void TestMLDsaCng() + { + CngKey cngKey = null; + var dsa = new MLDsaCng(cngKey); + } + + public void TestMLDsaOpenSsl() + { + SafeEvpPKeyHandle handle = null; + var dsa = new MLDsaOpenSsl(handle); + } + + // ------------------------------------------------------------------------- + // Section 4: MLDsa Sign / Verify operations + // ------------------------------------------------------------------------- + + public void TestSignData() + { + var dsa = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65); + byte[] data = new byte[32]; + byte[] context = new byte[0]; + byte[] signature = dsa.SignData(data, context); + } + + public void TestVerifyData() + { + var dsa = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65); + byte[] data = new byte[32]; + byte[] signature = new byte[3309]; + byte[] context = new byte[0]; + bool ok = dsa.VerifyData(data, signature, context); + } + + public void TestSignMu() + { + var dsa = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65); + byte[] mu = new byte[64]; + byte[] signature = dsa.SignMu(mu); + } + + public void TestVerifyMu() + { + var dsa = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65); + byte[] mu = new byte[64]; + byte[] signature = new byte[3309]; + bool ok = dsa.VerifyMu(mu, signature); + } + + public void TestSignPreHash() + { + var dsa = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65); + byte[] hash = new byte[64]; + string hashOid = "2.16.840.1.101.3.4.2.3"; + byte[] context = new byte[0]; + byte[] signature = dsa.SignPreHash(hash, hashOid, context); + } + + public void TestVerifyPreHash() + { + var dsa = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65); + byte[] hash = new byte[64]; + byte[] signature = new byte[3309]; + string hashOid = "2.16.840.1.101.3.4.2.3"; + byte[] context = new byte[0]; + bool ok = dsa.VerifyPreHash(hash, signature, hashOid, context); + } + + // ------------------------------------------------------------------------- + // Section 5: CompositeMLDsa algorithm-parameterized creation (parameter set captured + // verbatim from the CompositeMLDsaAlgorithm argument, e.g. "MLDsa44WithECDsaP256") + // ------------------------------------------------------------------------- + + public void TestCompositeGenerateKey() + { + var dsa = CompositeMLDsa.GenerateKey(CompositeMLDsaAlgorithm.MLDsa44WithECDsaP256); + } + + public void TestCompositeImportPrivateKey() + { + byte[] privateKeyBytes = new byte[64]; + var dsa = CompositeMLDsa.ImportCompositeMLDsaPrivateKey( + CompositeMLDsaAlgorithm.MLDsa65WithRSA3072Pss, privateKeyBytes); + } + + public void TestCompositeImportPublicKey() + { + byte[] publicKeyBytes = new byte[64]; + var dsa = CompositeMLDsa.ImportCompositeMLDsaPublicKey( + CompositeMLDsaAlgorithm.MLDsa87WithEd448, publicKeyBytes); + } + + // ------------------------------------------------------------------------- + // Section 6: CompositeMLDsa structural imports (no CompositeMLDsaAlgorithm argument) + // ------------------------------------------------------------------------- + + public void TestCompositeImportPkcs8PrivateKey() + { + byte[] pkcs8Bytes = new byte[64]; + var dsa = CompositeMLDsa.ImportPkcs8PrivateKey(pkcs8Bytes); + } + + public void TestCompositeImportSubjectPublicKeyInfo() + { + byte[] spkiBytes = new byte[64]; + var dsa = CompositeMLDsa.ImportSubjectPublicKeyInfo(spkiBytes); + } + + public void TestCompositeImportFromPem() + { + string pem = "-----BEGIN PRIVATE KEY-----"; + var dsa = CompositeMLDsa.ImportFromPem(pem); + } + + public void TestCompositeImportEncryptedPkcs8PrivateKey() + { + byte[] passwordBytes = new byte[16]; + byte[] encryptedBytes = new byte[64]; + var dsa = CompositeMLDsa.ImportEncryptedPkcs8PrivateKey(passwordBytes, encryptedBytes); + } + + public void TestCompositeImportFromEncryptedPem() + { + string pem = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; + string password = "hunter2"; + var dsa = CompositeMLDsa.ImportFromEncryptedPem(pem, password); + } + + // ------------------------------------------------------------------------- + // Section 7: CompositeMLDsaCng native-interop constructor + // ------------------------------------------------------------------------- + + public void TestCompositeMLDsaCng() + { + CngKey cngKey = null; + var dsa = new CompositeMLDsaCng(cngKey); + } + + // ------------------------------------------------------------------------- + // Section 8: CompositeMLDsa Sign / Verify operations + // ------------------------------------------------------------------------- + + public void TestCompositeSignData() + { + var dsa = CompositeMLDsa.GenerateKey(CompositeMLDsaAlgorithm.MLDsa44WithECDsaP256); + byte[] data = new byte[32]; + byte[] context = new byte[0]; + byte[] signature = dsa.SignData(data, context); + } + + public void TestCompositeVerifyData() + { + var dsa = CompositeMLDsa.GenerateKey(CompositeMLDsaAlgorithm.MLDsa44WithECDsaP256); + byte[] data = new byte[32]; + byte[] signature = new byte[2400]; + byte[] context = new byte[0]; + bool ok = dsa.VerifyData(data, signature, context); + } + + // ------------------------------------------------------------------------- + // Section 9: Combined usage pattern (generation + sign + verify) + // ------------------------------------------------------------------------- + + public void TestFullFlow() + { + var dsa = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa87); + byte[] data = new byte[32]; + byte[] context = new byte[0]; + byte[] signature = dsa.SignData(data, context); + bool ok = dsa.VerifyData(data, signature, context); + } +} 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 000000000..8c27143fc --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetMLKemTestFile.cs @@ -0,0 +1,138 @@ +/* + * Comprehensive test file for System.Security.Cryptography ML-KEM (MLKem, MLKemCng, + * MLKemOpenSsl) detection rules. + * + * ML-KEM (FIPS 203) is documented for the net-10.0/net-11.0 monikers — still an evolving + * preview API (see the [Experimental("SYSLIB5006")] attribute on some overloads in the + * official reference). See DotNetMLKem.java's class javadoc for the full, verified + * explanation of what is/isn't modeled here and why. + */ + +using System.Security.Cryptography; + +public class DotNetMLKemTest +{ + // ------------------------------------------------------------------------- + // Section 1: Algorithm-parameterized creation (parameter set captured from the + // MLKemAlgorithm argument) + // ------------------------------------------------------------------------- + + public void TestGenerateKey512() + { + var kem = MLKem.GenerateKey(MLKemAlgorithm.MLKem512); + } + + public void TestGenerateKey768() + { + var kem = MLKem.GenerateKey(MLKemAlgorithm.MLKem768); + } + + public void TestGenerateKey1024() + { + var kem = MLKem.GenerateKey(MLKemAlgorithm.MLKem1024); + } + + public void TestImportDecapsulationKey() + { + byte[] decapsulationKeyBytes = new byte[64]; + var kem = MLKem.ImportDecapsulationKey(MLKemAlgorithm.MLKem768, decapsulationKeyBytes); + } + + public void TestImportEncapsulationKey() + { + byte[] encapsulationKeyBytes = new byte[64]; + var kem = MLKem.ImportEncapsulationKey(MLKemAlgorithm.MLKem768, encapsulationKeyBytes); + } + + public void TestImportPrivateSeed() + { + byte[] seedBytes = new byte[64]; + var kem = MLKem.ImportPrivateSeed(MLKemAlgorithm.MLKem512, seedBytes); + } + + // ------------------------------------------------------------------------- + // Section 2: Structural imports (no MLKemAlgorithm argument — the parameter set is + // embedded in the encoded key material / PEM text and cannot be recovered) + // ------------------------------------------------------------------------- + + public void TestImportPkcs8PrivateKey() + { + byte[] pkcs8Bytes = new byte[64]; + var kem = MLKem.ImportPkcs8PrivateKey(pkcs8Bytes); + } + + public void TestImportSubjectPublicKeyInfo() + { + byte[] spkiBytes = new byte[64]; + var kem = MLKem.ImportSubjectPublicKeyInfo(spkiBytes); + } + + public void TestImportFromPem() + { + string pem = "-----BEGIN PRIVATE KEY-----"; + var kem = MLKem.ImportFromPem(pem); + } + + public void TestImportEncryptedPkcs8PrivateKey() + { + byte[] passwordBytes = new byte[16]; + byte[] encryptedBytes = new byte[64]; + var kem = MLKem.ImportEncryptedPkcs8PrivateKey(passwordBytes, encryptedBytes); + } + + public void TestImportFromEncryptedPem() + { + string pem = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; + string password = "hunter2"; + var kem = MLKem.ImportFromEncryptedPem(pem, password); + } + + // ------------------------------------------------------------------------- + // Section 3: Native-interop constructors (no MLKemAlgorithm argument — wrap an + // already-existing native key handle) + // ------------------------------------------------------------------------- + + public void TestMLKemCng() + { + CngKey cngKey = null; + var kem = new MLKemCng(cngKey); + } + + public void TestMLKemOpenSsl() + { + SafeEvpPKeyHandle handle = null; + var kem = new MLKemOpenSsl(handle); + } + + // ------------------------------------------------------------------------- + // Section 4: Encapsulate / Decapsulate operations + // ------------------------------------------------------------------------- + + public void TestEncapsulate() + { + var kem = MLKem.GenerateKey(MLKemAlgorithm.MLKem768); + byte[] ciphertext; + byte[] sharedSecret; + kem.Encapsulate(out ciphertext, out sharedSecret); + } + + public void TestDecapsulate() + { + var kem = MLKem.GenerateKey(MLKemAlgorithm.MLKem768); + byte[] ciphertext = new byte[1088]; + byte[] sharedSecret = kem.Decapsulate(ciphertext); + } + + // ------------------------------------------------------------------------- + // Section 5: Combined usage pattern (generation + encapsulate + decapsulate) + // ------------------------------------------------------------------------- + + public void TestFullFlow() + { + var kem = MLKem.GenerateKey(MLKemAlgorithm.MLKem1024); + byte[] ciphertext; + byte[] sharedSecret; + kem.Encapsulate(out ciphertext, out sharedSecret); + byte[] decapsulated = kem.Decapsulate(ciphertext); + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetProtectedDataTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetProtectedDataTestFile.cs new file mode 100644 index 000000000..1c7b47e40 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetProtectedDataTestFile.cs @@ -0,0 +1,84 @@ +/* + * Comprehensive test file for the DPAPI (Windows Data Protection API) detection rules + * (DotNetProtectedData.java). + * + * Covers: + * - ProtectedData.Protect(...) / Unprotect(...) / TryProtect(...) / TryUnprotect(...) + * - ProtectedMemory.Protect(...) / Unprotect(...) + * - DpapiDataProtector constructor + instance Protect(byte[]) / Unprotect(byte[]) + */ + +using System.Security.Cryptography; + +public class DotNetProtectedDataTest +{ + // ------------------------------------------------------------------------- + // Section 1: ProtectedData + // ------------------------------------------------------------------------- + + public void TestProtectedDataProtect() + { + byte[] secret = new byte[16]; + byte[] entropy = new byte[8]; + byte[] encrypted = ProtectedData.Protect(secret, entropy, DataProtectionScope.CurrentUser); + } + + public void TestProtectedDataUnprotect() + { + byte[] encrypted = new byte[32]; + byte[] entropy = new byte[8]; + byte[] secret = ProtectedData.Unprotect(encrypted, entropy, DataProtectionScope.CurrentUser); + } + + public void TestProtectedDataTryProtect() + { + byte[] secret = new byte[16]; + byte[] entropy = new byte[8]; + byte[] destination = new byte[64]; + int bytesWritten; + ProtectedData.TryProtect(secret, DataProtectionScope.LocalMachine, destination, out bytesWritten, entropy); + } + + public void TestProtectedDataTryUnprotect() + { + byte[] encrypted = new byte[32]; + byte[] entropy = new byte[8]; + byte[] destination = new byte[32]; + int bytesWritten; + ProtectedData.TryUnprotect(encrypted, DataProtectionScope.LocalMachine, destination, out bytesWritten, entropy); + } + + // ------------------------------------------------------------------------- + // Section 2: ProtectedMemory + // ------------------------------------------------------------------------- + + public void TestProtectedMemoryProtect() + { + byte[] secret = new byte[16]; + ProtectedMemory.Protect(secret, MemoryProtectionScope.SameProcess); + } + + public void TestProtectedMemoryUnprotect() + { + byte[] secret = new byte[16]; + ProtectedMemory.Unprotect(secret, MemoryProtectionScope.SameProcess); + } + + // ------------------------------------------------------------------------- + // Section 3: DpapiDataProtector + // ------------------------------------------------------------------------- + + public void TestDpapiDataProtectorProtect() + { + DpapiDataProtector protector = new DpapiDataProtector("MyApp", "Giftcard", "1234"); + byte[] secret = new byte[16]; + byte[] encrypted = protector.Protect(secret); + } + + public void TestDpapiDataProtectorUnprotect() + { + DpapiDataProtector protector = new DpapiDataProtector("MyApp", "Giftcard", "1234"); + byte[] encrypted = new byte[32]; + byte[] secret = protector.Unprotect(encrypted); + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetRC2ComprehensiveTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetRC2ComprehensiveTestFile.cs new file mode 100644 index 000000000..4d42dd1a9 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetRC2ComprehensiveTestFile.cs @@ -0,0 +1,353 @@ +/* + * Comprehensive test file for System.Security.Cryptography RC2 detection rules. + * + * Covers both RC2-related classes and their complete API surface: + * - RC2 (abstract base) + * - RC2CryptoServiceProvider (derived from RC2) + * + * Like DES, RC2 has no CNG-backed subclass and no AEAD variant, so there are + * fewer constructor scenarios in Section 1, and no AEAD sections. Unlike DES, + * RC2 additionally exposes an EffectiveKeySize property (Section 2), which is + * reused as a KeySize detection (see DotNetRC2.java). + * + * Architecture note: all methods inherited from SymmetricAlgorithm (EncryptCbc, + * CreateEncryptor, etc.) are covered once here. The detection engine tracks the + * variable and fires the same depending rules for every concrete RC2 subclass. + */ + +using System.Security.Cryptography; + +public class DotNetRC2ComprehensiveTest +{ + // ------------------------------------------------------------------------- + // Section 1: Factory methods / constructors + // ------------------------------------------------------------------------- + + public void TestRc2Create() + { + var rc2 = RC2.Create(); + } + + public void TestRc2CreateNamed() + { + var rc2 = RC2.Create("RC2"); + } + + public void TestRc2Csp() + { + var rc2 = new RC2CryptoServiceProvider(); + } + + // ------------------------------------------------------------------------- + // Section 2: Property setters (via assignment → synthetic set_X invocations) + // ------------------------------------------------------------------------- + + public void TestPropertyModeCBC() + { + var rc2 = RC2.Create(); + rc2.Mode = CipherMode.CBC; + } + + public void TestPropertyModeECB() + { + var rc2 = RC2.Create(); + rc2.Mode = CipherMode.ECB; + } + + public void TestPropertyModeCFB() + { + var rc2 = RC2.Create(); + rc2.Mode = CipherMode.CFB; + } + + public void TestPropertyModeOFB() + { + var rc2 = RC2.Create(); + rc2.Mode = CipherMode.OFB; + } + + public void TestPropertyModeCTS() + { + var rc2 = RC2.Create(); + rc2.Mode = CipherMode.CTS; + } + + public void TestPropertyKeySize() + { + var rc2 = RC2.Create(); + rc2.KeySize = 128; + } + + public void TestPropertyEffectiveKeySize() + { + var rc2 = RC2.Create(); + rc2.EffectiveKeySize = 64; + } + + public void TestPropertyPaddingPKCS7() + { + var rc2 = RC2.Create(); + rc2.Padding = PaddingMode.PKCS7; + } + + public void TestPropertyPaddingNone() + { + var rc2 = RC2.Create(); + rc2.Padding = PaddingMode.None; + } + + public void TestPropertyPaddingZeros() + { + var rc2 = RC2.Create(); + rc2.Padding = PaddingMode.Zeros; + } + + public void TestPropertyPaddingANSIX923() + { + var rc2 = RC2.Create(); + rc2.Padding = PaddingMode.ANSIX923; + } + + public void TestPropertyFeedbackSize() + { + var rc2 = RC2.Create(); + rc2.FeedbackSize = 8; + } + + public void TestPropertyIV() + { + var rc2 = RC2.Create(); + rc2.IV = new byte[8]; + } + + public void TestPropertyKey() + { + var rc2 = RC2.Create(); + rc2.Key = new byte[16]; + } + + // ------------------------------------------------------------------------- + // Section 3: CreateEncryptor / CreateDecryptor + // ------------------------------------------------------------------------- + + public void TestCreateEncryptorNoArgs() + { + var rc2 = RC2.Create(); + var encryptor = rc2.CreateEncryptor(); + } + + public void TestCreateEncryptorWithArgs() + { + var rc2 = RC2.Create(); + byte[] key = new byte[16]; + byte[] iv = new byte[8]; + var encryptor = rc2.CreateEncryptor(key, iv); + } + + public void TestCreateDecryptorNoArgs() + { + var rc2 = RC2.Create(); + var decryptor = rc2.CreateDecryptor(); + } + + public void TestCreateDecryptorWithArgs() + { + var rc2 = RC2.Create(); + byte[] key = new byte[16]; + byte[] iv = new byte[8]; + var decryptor = rc2.CreateDecryptor(key, iv); + } + + // ------------------------------------------------------------------------- + // Section 4: Direct mode-specific encrypt methods + // ------------------------------------------------------------------------- + + public void TestEncryptCbc() + { + var rc2 = RC2.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = rc2.EncryptCbc(plaintext, iv, PaddingMode.PKCS7); + } + + public void TestEncryptEcb() + { + var rc2 = RC2.Create(); + byte[] plaintext = new byte[16]; + byte[] ciphertext = rc2.EncryptEcb(plaintext, PaddingMode.None); + } + + public void TestEncryptCfb() + { + var rc2 = RC2.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = rc2.EncryptCfb(plaintext, iv, PaddingMode.None, 8); + } + + // ------------------------------------------------------------------------- + // Section 5: Direct mode-specific decrypt methods + // ------------------------------------------------------------------------- + + public void TestDecryptCbc() + { + var rc2 = RC2.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] plaintext = rc2.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); + } + + public void TestDecryptEcb() + { + var rc2 = RC2.Create(); + byte[] ciphertext = new byte[16]; + byte[] plaintext = rc2.DecryptEcb(ciphertext, PaddingMode.None); + } + + public void TestDecryptCfb() + { + var rc2 = RC2.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] plaintext = rc2.DecryptCfb(ciphertext, iv, PaddingMode.None, 8); + } + + // ------------------------------------------------------------------------- + // Section 6: Try* variants + // ------------------------------------------------------------------------- + + public void TestTryEncryptCbc() + { + var rc2 = RC2.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[24]; + int bytesWritten; + rc2.TryEncryptCbc(plaintext, iv, destination, out bytesWritten, PaddingMode.PKCS7); + } + + public void TestTryDecryptCbc() + { + var rc2 = RC2.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[16]; + int bytesWritten; + rc2.TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, PaddingMode.PKCS7); + } + + public void TestTryEncryptEcb() + { + var rc2 = RC2.Create(); + byte[] plaintext = new byte[16]; + byte[] destination = new byte[24]; + int bytesWritten; + rc2.TryEncryptEcb(plaintext, destination, PaddingMode.None, out bytesWritten); + } + + public void TestTryDecryptEcb() + { + var rc2 = RC2.Create(); + byte[] ciphertext = new byte[16]; + byte[] destination = new byte[16]; + int bytesWritten; + rc2.TryDecryptEcb(ciphertext, destination, PaddingMode.None, out bytesWritten); + } + + public void TestTryEncryptCfb() + { + var rc2 = RC2.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[24]; + int bytesWritten; + rc2.TryEncryptCfb(plaintext, iv, destination, out bytesWritten, PaddingMode.None, 8); + } + + public void TestTryDecryptCfb() + { + var rc2 = RC2.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[16]; + int bytesWritten; + rc2.TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, PaddingMode.None, 8); + } + + // ------------------------------------------------------------------------- + // Section 7: Key/IV generation + // ------------------------------------------------------------------------- + + public void TestGenerateKey() + { + var rc2 = RC2.Create(); + rc2.GenerateKey(); + } + + public void TestGenerateIV() + { + var rc2 = RC2.Create(); + rc2.GenerateIV(); + } + + // ------------------------------------------------------------------------- + // Section 8: Combined usage patterns (real-world scenarios) + // Demonstrates that depending rules fire correctly for both derived classes. + // ------------------------------------------------------------------------- + + public void TestRc2CbcFullFlow() + { + var rc2 = RC2.Create(); + rc2.Mode = CipherMode.CBC; + rc2.Padding = PaddingMode.PKCS7; + var encryptor = rc2.CreateEncryptor(); + } + + public void TestRc2CspEncryptCbc() + { + var rc2 = new RC2CryptoServiceProvider(); + rc2.Mode = CipherMode.CBC; + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = rc2.EncryptCbc(plaintext, iv, PaddingMode.PKCS7); + } + + public void TestRc2CspDecryptCbc() + { + var rc2 = new RC2CryptoServiceProvider(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] plaintext = rc2.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); + } + + public void TestRc2CfbFeedback() + { + var rc2 = RC2.Create(); + rc2.Mode = CipherMode.CFB; + rc2.FeedbackSize = 8; + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = rc2.EncryptCfb(plaintext, iv, PaddingMode.None, 8); + } + + public void TestRc2CbcWithEncryptorOverload() + { + var rc2 = RC2.Create(); + byte[] key = new byte[16]; + byte[] iv = new byte[8]; + var encryptor = rc2.CreateEncryptor(key, iv); + } + + public void TestRc2EcbEncrypt() + { + var rc2 = new RC2CryptoServiceProvider(); + byte[] plaintext = new byte[16]; + byte[] ciphertext = rc2.EncryptEcb(plaintext, PaddingMode.None); + } + + public void TestRc2CspEffectiveKeySize() + { + var rc2 = new RC2CryptoServiceProvider(); + rc2.EffectiveKeySize = 40; + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetRSATestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetRSATestFile.cs index 147848228..4c4a90d2c 100755 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetRSATestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetRSATestFile.cs @@ -1,4 +1,182 @@ +/* + * Comprehensive test file for System.Security.Cryptography RSA detection rules. + * + * Covers all four RSA-related classes and their complete operational API surface: + * - RSA (abstract base) + * - RSACng, RSACryptoServiceProvider, RSAOpenSsl (derived from RSA) + * + * Architecture note: all methods inherited from RSA / AsymmetricAlgorithm (KeySize, + * Encrypt, Decrypt, SignData, VerifyData, SignHash, VerifyHash, Try* variants, etc.) + * are covered once here. The detection engine tracks the variable and fires the same + * depending rules for every concrete RSA subclass. + */ + using System.Security.Cryptography; -public class DotNetRSATest { - public void TestRsaCreate() { var rsa = RSA.Create(); } // Noncompliant + +public class DotNetRSATest +{ + // ------------------------------------------------------------------------- + // Section 1: Factory methods / constructors + // ------------------------------------------------------------------------- + + public void TestRsaCreate() + { + var rsa = RSA.Create(); // Noncompliant + } + + public void TestRsaCreateWithKeySize() + { + var rsa = RSA.Create(2048); + } + + public void TestRsaCsp() + { + var rsa = new RSACryptoServiceProvider(); + } + + public void TestRsaCng() + { + var rsa = new RSACng(); + } + + public void TestRsaOpenSsl() + { + var rsa = new RSAOpenSsl(); + } + + // ------------------------------------------------------------------------- + // Section 2: Property setters (via assignment → synthetic set_X invocations) + // ------------------------------------------------------------------------- + + public void TestPropertyKeySize2048() + { + var rsa = RSA.Create(); + rsa.KeySize = 2048; + } + + public void TestPropertyKeySize4096() + { + var rsa = RSA.Create(); + rsa.KeySize = 4096; + } + + // ------------------------------------------------------------------------- + // Section 3: Encrypt / Decrypt + // ------------------------------------------------------------------------- + + public void TestEncrypt() + { + var rsa = RSA.Create(); + byte[] data = new byte[32]; + byte[] ciphertext = rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256); + } + + public void TestDecrypt() + { + var rsa = RSA.Create(); + byte[] ciphertext = new byte[256]; + byte[] plaintext = rsa.Decrypt(ciphertext, RSAEncryptionPadding.OaepSHA256); + } + + public void TestTryEncrypt() + { + var rsa = RSA.Create(); + byte[] data = new byte[32]; + byte[] destination = new byte[256]; + int bytesWritten; + rsa.TryEncrypt(data, destination, RSAEncryptionPadding.Pkcs1, out bytesWritten); + } + + public void TestTryDecrypt() + { + var rsa = RSA.Create(); + byte[] ciphertext = new byte[256]; + byte[] destination = new byte[32]; + int bytesWritten; + rsa.TryDecrypt(ciphertext, destination, RSAEncryptionPadding.Pkcs1, out bytesWritten); + } + + // ------------------------------------------------------------------------- + // Section 4: SignData / TrySignData / VerifyData + // ------------------------------------------------------------------------- + + public void TestSignData() + { + var rsa = RSA.Create(); + byte[] data = new byte[64]; + byte[] signature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } + + public void TestTrySignData() + { + var rsa = RSA.Create(); + byte[] data = new byte[64]; + byte[] destination = new byte[256]; + int bytesWritten; + rsa.TrySignData(data, destination, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1, out bytesWritten); + } + + public void TestVerifyData() + { + var rsa = RSA.Create(); + byte[] data = new byte[64]; + byte[] signature = new byte[256]; + bool valid = rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } + + // ------------------------------------------------------------------------- + // Section 5: SignHash / TrySignHash / VerifyHash + // ------------------------------------------------------------------------- + + public void TestSignHash() + { + var rsa = RSA.Create(); + byte[] hash = new byte[32]; + byte[] signature = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } + + public void TestTrySignHash() + { + var rsa = RSA.Create(); + byte[] hash = new byte[32]; + byte[] destination = new byte[256]; + int bytesWritten; + rsa.TrySignHash(hash, destination, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1, out bytesWritten); + } + + public void TestVerifyHash() + { + var rsa = RSA.Create(); + byte[] hash = new byte[32]; + byte[] signature = new byte[256]; + bool valid = rsa.VerifyHash(hash, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } + + // ------------------------------------------------------------------------- + // Section 6: Combined usage patterns (real-world scenarios) + // Demonstrates that depending rules fire correctly for ALL derived classes. + // ------------------------------------------------------------------------- + + public void TestRsaCngFullFlow() + { + var rsa = new RSACng(); + rsa.KeySize = 3072; + byte[] data = new byte[32]; + byte[] ciphertext = rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256); + } + + public void TestRsaCspSignFlow() + { + var rsa = new RSACryptoServiceProvider(); + byte[] data = new byte[64]; + byte[] signature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } + + public void TestRsaOpenSslVerifyFlow() + { + var rsa = new RSAOpenSsl(); + byte[] data = new byte[64]; + byte[] signature = new byte[256]; + bool valid = rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } } diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetRandomNumberGeneratorTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetRandomNumberGeneratorTestFile.cs new file mode 100644 index 000000000..13ac8c7b0 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetRandomNumberGeneratorTestFile.cs @@ -0,0 +1,120 @@ +/* + * Comprehensive test file for RandomNumberGenerator / RNGCryptoServiceProvider detection rules + * (DotNetRandomNumberGenerator.java). + * + * Covers: + * - RandomNumberGenerator.Create() / Create(string) + instance GetBytes/GetNonZeroBytes + * - RandomNumberGenerator's static-only methods: Fill, GetBytes(int)/(Span), + * GetHexString, GetInt32, GetItems, GetNonZeroBytes(Span), GetString, Shuffle + * - RNGCryptoServiceProvider constructor overloads + instance GetBytes/GetNonZeroBytes + */ + +using System.Security.Cryptography; + +public class DotNetRandomNumberGeneratorTest +{ + // ------------------------------------------------------------------------- + // Section 1: RandomNumberGenerator.Create() / Create(string) + instance operations + // ------------------------------------------------------------------------- + + public void TestCreateAndGetBytes() + { + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + byte[] data = new byte[32]; + rng.GetBytes(data); + } + + public void TestCreateAndGetNonZeroBytes() + { + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + byte[] data = new byte[32]; + rng.GetNonZeroBytes(data); + } + + public void TestCreateNamed() + { + RandomNumberGenerator rng = RandomNumberGenerator.Create("RandomNumberGenerator"); + byte[] data = new byte[32]; + rng.GetBytes(data); + } + + // ------------------------------------------------------------------------- + // Section 2: RandomNumberGenerator static-only methods + // ------------------------------------------------------------------------- + + public void TestStaticFill() + { + byte[] data = new byte[32]; + RandomNumberGenerator.Fill(data); + } + + public void TestStaticGetBytesCount() + { + byte[] data = RandomNumberGenerator.GetBytes(32); + } + + public void TestStaticGetHexString() + { + string hex = RandomNumberGenerator.GetHexString(16); + } + + public void TestStaticGetInt32() + { + int value = RandomNumberGenerator.GetInt32(100); + } + + public void TestStaticGetInt32Range() + { + int value = RandomNumberGenerator.GetInt32(1, 100); + } + + public void TestStaticGetItems() + { + int[] choices = new int[] { 1, 2, 3, 4, 5 }; + int[] items = RandomNumberGenerator.GetItems(choices, 3); + } + + public void TestStaticGetNonZeroBytes() + { + byte[] data = new byte[32]; + RandomNumberGenerator.GetNonZeroBytes(data); + } + + public void TestStaticGetString() + { + string alphabet = "abcdefghijklmnopqrstuvwxyz"; + string result = RandomNumberGenerator.GetString(alphabet, 10); + } + + public void TestStaticShuffle() + { + int[] values = new int[] { 1, 2, 3, 4, 5 }; + RandomNumberGenerator.Shuffle(values); + } + + // ------------------------------------------------------------------------- + // Section 3: RNGCryptoServiceProvider + // ------------------------------------------------------------------------- + + public void TestRngCspGetBytes() + { + RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(); + byte[] data = new byte[32]; + rngCsp.GetBytes(data); + } + + public void TestRngCspGetNonZeroBytes() + { + RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(); + byte[] data = new byte[32]; + rngCsp.GetNonZeroBytes(data); + } + + public void TestRngCspWithSeed() + { + byte[] seed = new byte[32]; + RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(seed); + byte[] data = new byte[32]; + rngCsp.GetBytes(data); + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetSHA3TestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetSHA3TestFile.cs new file mode 100644 index 000000000..457709ce5 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetSHA3TestFile.cs @@ -0,0 +1,8 @@ +using System.Security.Cryptography; +public class DotNetSHA3Test { + public void TestSha3_256Create() { var h = SHA3_256.Create(); } // Noncompliant + public void TestSha3_384Create() { var h = SHA3_384.Create(); } // Noncompliant + public void TestSha3_512Create() { var h = SHA3_512.Create(); } // Noncompliant + public void TestShake128() { var h = new Shake128(); } // Noncompliant + public void TestShake256() { var h = new Shake256(); } // Noncompliant +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetSHATestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetSHATestFile.cs index 9ab3b53cf..44065051f 100755 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetSHATestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetSHATestFile.cs @@ -5,4 +5,24 @@ public class DotNetSHATest { public void TestSha384Create() { var h = SHA384.Create(); } // Noncompliant public void TestSha512Create() { var h = SHA512.Create(); } // Noncompliant public void TestMd5Create() { var h = MD5.Create(); } // Noncompliant + + public void TestMd5CreateNamed() { var h = MD5.Create("MD5"); } // Noncompliant + public void TestSha1CreateNamed() { var h = SHA1.Create("SHA1"); } // Noncompliant + public void TestSha256CreateNamed() { var h = SHA256.Create("SHA256"); } // Noncompliant + public void TestSha384CreateNamed() { var h = SHA384.Create("SHA384"); } // Noncompliant + public void TestSha512CreateNamed() { var h = SHA512.Create("SHA512"); } // Noncompliant + + public void TestMd5Cng() { var h = new MD5Cng(); } // Noncompliant + public void TestSha1Cng() { var h = new SHA1Cng(); } // Noncompliant + public void TestSha1Csp() { var h = new SHA1CryptoServiceProvider(); } // Noncompliant + public void TestSha256Cng() { var h = new SHA256Cng(); } // Noncompliant + public void TestSha256Csp() { var h = new SHA256CryptoServiceProvider(); }// Noncompliant + public void TestSha384Cng() { var h = new SHA384Cng(); } // Noncompliant + public void TestSha384Csp() { var h = new SHA384CryptoServiceProvider(); }// Noncompliant + public void TestSha512Cng() { var h = new SHA512Cng(); } // Noncompliant + public void TestSha512Csp() { var h = new SHA512CryptoServiceProvider(); }// Noncompliant + + public void TestRipemd160Create() { var h = RIPEMD160.Create(); } // Noncompliant + public void TestRipemd160CreateNamed() { var h = RIPEMD160.Create("RIPEMD160"); } // Noncompliant + public void TestRipemd160Managed() { var h = new RIPEMD160Managed(); } // Noncompliant } diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetSlhDsaTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetSlhDsaTestFile.cs new file mode 100644 index 000000000..29bf6e05f --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetSlhDsaTestFile.cs @@ -0,0 +1,152 @@ +/* + * Comprehensive test file for System.Security.Cryptography SLH-DSA (SlhDsa, SlhDsaCng, + * SlhDsaOpenSsl) detection rules. + * + * SLH-DSA (FIPS 205, formerly SPHINCS+) is documented for the net-10.0/net-11.0 monikers, with + * the whole SlhDsa surface (base class, both derived classes, and SlhDsaAlgorithm) carrying a + * class-level [Experimental("SYSLIB5006")] attribute (still a preview API, unlike the now-stable + * plain ML-DSA/ML-KEM surface). See DotNetSlhDsa.java's class javadoc for the full, verified + * explanation of what is/isn't modeled here and why, and how it differs from ML-DSA (no + * ImportSlhDsaPrivateSeed, no SignMu/VerifyMu, no Composite variant, different parameter set + * names). + */ + +using System.Security.Cryptography; + +public class DotNetSlhDsaTest +{ + // ------------------------------------------------------------------------- + // Section 1: SlhDsa algorithm-parameterized creation (parameter set captured from the + // SlhDsaAlgorithm argument) + // ------------------------------------------------------------------------- + + public void TestGenerateKeySha2_128s() + { + var dsa = SlhDsa.GenerateKey(SlhDsaAlgorithm.SlhDsaSha2_128s); + } + + public void TestGenerateKeyShake256f() + { + var dsa = SlhDsa.GenerateKey(SlhDsaAlgorithm.SlhDsaShake256f); + } + + public void TestImportSlhDsaPrivateKey() + { + byte[] privateKeyBytes = new byte[64]; + var dsa = SlhDsa.ImportSlhDsaPrivateKey(SlhDsaAlgorithm.SlhDsaSha2_192f, privateKeyBytes); + } + + public void TestImportSlhDsaPublicKey() + { + byte[] publicKeyBytes = new byte[32]; + var dsa = SlhDsa.ImportSlhDsaPublicKey(SlhDsaAlgorithm.SlhDsaShake128s, publicKeyBytes); + } + + // ------------------------------------------------------------------------- + // Section 2: SlhDsa structural imports (no SlhDsaAlgorithm argument — the parameter set is + // embedded in the encoded key material / PEM text and cannot be recovered) + // ------------------------------------------------------------------------- + + public void TestImportPkcs8PrivateKey() + { + byte[] pkcs8Bytes = new byte[64]; + var dsa = SlhDsa.ImportPkcs8PrivateKey(pkcs8Bytes); + } + + public void TestImportSubjectPublicKeyInfo() + { + byte[] spkiBytes = new byte[64]; + var dsa = SlhDsa.ImportSubjectPublicKeyInfo(spkiBytes); + } + + public void TestImportFromPem() + { + string pem = "-----BEGIN PRIVATE KEY-----"; + var dsa = SlhDsa.ImportFromPem(pem); + } + + public void TestImportEncryptedPkcs8PrivateKey() + { + byte[] passwordBytes = new byte[16]; + byte[] encryptedBytes = new byte[64]; + var dsa = SlhDsa.ImportEncryptedPkcs8PrivateKey(passwordBytes, encryptedBytes); + } + + public void TestImportFromEncryptedPem() + { + string pem = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; + string password = "hunter2"; + var dsa = SlhDsa.ImportFromEncryptedPem(pem, password); + } + + // ------------------------------------------------------------------------- + // Section 3: SlhDsa native-interop constructors (no SlhDsaAlgorithm argument — wrap an + // already-existing native key handle) + // ------------------------------------------------------------------------- + + public void TestSlhDsaCng() + { + CngKey cngKey = null; + var dsa = new SlhDsaCng(cngKey); + } + + public void TestSlhDsaOpenSsl() + { + SafeEvpPKeyHandle handle = null; + var dsa = new SlhDsaOpenSsl(handle); + } + + // ------------------------------------------------------------------------- + // Section 4: SlhDsa Sign / Verify operations (no SignMu/VerifyMu — confirmed absent from + // the official SlhDsa reference) + // ------------------------------------------------------------------------- + + public void TestSignData() + { + var dsa = SlhDsa.GenerateKey(SlhDsaAlgorithm.SlhDsaSha2_128s); + byte[] data = new byte[32]; + byte[] context = new byte[0]; + byte[] signature = dsa.SignData(data, context); + } + + public void TestVerifyData() + { + var dsa = SlhDsa.GenerateKey(SlhDsaAlgorithm.SlhDsaSha2_128s); + byte[] data = new byte[32]; + byte[] signature = new byte[7856]; + byte[] context = new byte[0]; + bool ok = dsa.VerifyData(data, signature, context); + } + + public void TestSignPreHash() + { + var dsa = SlhDsa.GenerateKey(SlhDsaAlgorithm.SlhDsaSha2_128s); + byte[] hash = new byte[32]; + string hashOid = "2.16.840.1.101.3.4.2.1"; + byte[] context = new byte[0]; + byte[] signature = dsa.SignPreHash(hash, hashOid, context); + } + + public void TestVerifyPreHash() + { + var dsa = SlhDsa.GenerateKey(SlhDsaAlgorithm.SlhDsaSha2_128s); + byte[] hash = new byte[32]; + byte[] signature = new byte[7856]; + string hashOid = "2.16.840.1.101.3.4.2.1"; + byte[] context = new byte[0]; + bool ok = dsa.VerifyPreHash(hash, signature, hashOid, context); + } + + // ------------------------------------------------------------------------- + // Section 5: combined usage pattern (generation + sign + verify) + // ------------------------------------------------------------------------- + + public void TestFullFlow() + { + var dsa = SlhDsa.GenerateKey(SlhDsaAlgorithm.SlhDsaShake256f); + byte[] data = new byte[32]; + byte[] context = new byte[0]; + byte[] signature = dsa.SignData(data, context); + bool ok = dsa.VerifyData(data, signature, context); + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetTripleDESComprehensiveTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetTripleDESComprehensiveTestFile.cs new file mode 100644 index 000000000..68585e759 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetTripleDESComprehensiveTestFile.cs @@ -0,0 +1,359 @@ +/* + * Comprehensive test file for System.Security.Cryptography Triple DES (3DES) detection rules. + * + * Covers all three TripleDES-related classes and their complete API surface: + * - TripleDES (abstract base) + * - TripleDESCryptoServiceProvider (derived from TripleDES, legacy CAPI) + * - TripleDESCng (derived from TripleDES, CNG-backed) + * + * Architecture note: all methods inherited from SymmetricAlgorithm (EncryptCbc, + * CreateEncryptor, etc.) are covered once here. The detection engine tracks the + * variable and fires the same depending rules for every concrete TripleDES subclass. + * Unlike RC2, TripleDES exposes no extra properties beyond those inherited from + * SymmetricAlgorithm, so Section 2 mirrors DotNetDESComprehensiveTestFile.cs. Unlike + * DES/RC2 (which have no CNG-backed subclass), TripleDES has TripleDESCng, mirroring + * AesCng in DotNetAESComprehensiveTestFile.cs. + */ + +using System.Security.Cryptography; + +public class DotNetTripleDESComprehensiveTest +{ + // ------------------------------------------------------------------------- + // Section 1: Factory methods / constructors + // ------------------------------------------------------------------------- + + public void TestTripleDesCreate() + { + var tdes = TripleDES.Create(); + } + + public void TestTripleDesCreateNamed() + { + var tdes = TripleDES.Create("TripleDES"); + } + + public void TestTripleDesCsp() + { + var tdes = new TripleDESCryptoServiceProvider(); + } + + public void TestTripleDesCng() + { + var tdes = new TripleDESCng(); + } + + public void TestTripleDesCngNamed() + { + var tdes = new TripleDESCng("myKey"); + } + + // ------------------------------------------------------------------------- + // Section 2: Property setters (via assignment → synthetic set_X invocations) + // ------------------------------------------------------------------------- + + public void TestPropertyModeCBC() + { + var tdes = TripleDES.Create(); + tdes.Mode = CipherMode.CBC; + } + + public void TestPropertyModeECB() + { + var tdes = TripleDES.Create(); + tdes.Mode = CipherMode.ECB; + } + + public void TestPropertyModeCFB() + { + var tdes = TripleDES.Create(); + tdes.Mode = CipherMode.CFB; + } + + public void TestPropertyModeOFB() + { + var tdes = TripleDES.Create(); + tdes.Mode = CipherMode.OFB; + } + + public void TestPropertyModeCTS() + { + var tdes = TripleDES.Create(); + tdes.Mode = CipherMode.CTS; + } + + public void TestPropertyKeySize() + { + var tdes = TripleDES.Create(); + tdes.KeySize = 192; + } + + public void TestPropertyPaddingPKCS7() + { + var tdes = TripleDES.Create(); + tdes.Padding = PaddingMode.PKCS7; + } + + public void TestPropertyPaddingNone() + { + var tdes = TripleDES.Create(); + tdes.Padding = PaddingMode.None; + } + + public void TestPropertyPaddingZeros() + { + var tdes = TripleDES.Create(); + tdes.Padding = PaddingMode.Zeros; + } + + public void TestPropertyPaddingANSIX923() + { + var tdes = TripleDES.Create(); + tdes.Padding = PaddingMode.ANSIX923; + } + + public void TestPropertyFeedbackSize() + { + var tdes = TripleDES.Create(); + tdes.FeedbackSize = 8; + } + + public void TestPropertyIV() + { + var tdes = TripleDES.Create(); + tdes.IV = new byte[8]; + } + + public void TestPropertyKey() + { + var tdes = TripleDES.Create(); + tdes.Key = new byte[24]; + } + + // ------------------------------------------------------------------------- + // Section 3: CreateEncryptor / CreateDecryptor + // ------------------------------------------------------------------------- + + public void TestCreateEncryptorNoArgs() + { + var tdes = TripleDES.Create(); + var encryptor = tdes.CreateEncryptor(); + } + + public void TestCreateEncryptorWithArgs() + { + var tdes = TripleDES.Create(); + byte[] key = new byte[24]; + byte[] iv = new byte[8]; + var encryptor = tdes.CreateEncryptor(key, iv); + } + + public void TestCreateDecryptorNoArgs() + { + var tdes = TripleDES.Create(); + var decryptor = tdes.CreateDecryptor(); + } + + public void TestCreateDecryptorWithArgs() + { + var tdes = TripleDES.Create(); + byte[] key = new byte[24]; + byte[] iv = new byte[8]; + var decryptor = tdes.CreateDecryptor(key, iv); + } + + // ------------------------------------------------------------------------- + // Section 4: Direct mode-specific encrypt methods + // ------------------------------------------------------------------------- + + public void TestEncryptCbc() + { + var tdes = TripleDES.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = tdes.EncryptCbc(plaintext, iv, PaddingMode.PKCS7); + } + + public void TestEncryptEcb() + { + var tdes = TripleDES.Create(); + byte[] plaintext = new byte[16]; + byte[] ciphertext = tdes.EncryptEcb(plaintext, PaddingMode.None); + } + + public void TestEncryptCfb() + { + var tdes = TripleDES.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = tdes.EncryptCfb(plaintext, iv, PaddingMode.None, 8); + } + + // ------------------------------------------------------------------------- + // Section 5: Direct mode-specific decrypt methods + // ------------------------------------------------------------------------- + + public void TestDecryptCbc() + { + var tdes = TripleDES.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] plaintext = tdes.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); + } + + public void TestDecryptEcb() + { + var tdes = TripleDES.Create(); + byte[] ciphertext = new byte[16]; + byte[] plaintext = tdes.DecryptEcb(ciphertext, PaddingMode.None); + } + + public void TestDecryptCfb() + { + var tdes = TripleDES.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] plaintext = tdes.DecryptCfb(ciphertext, iv, PaddingMode.None, 8); + } + + // ------------------------------------------------------------------------- + // Section 6: Try* variants + // ------------------------------------------------------------------------- + + public void TestTryEncryptCbc() + { + var tdes = TripleDES.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[24]; + int bytesWritten; + tdes.TryEncryptCbc(plaintext, iv, destination, out bytesWritten, PaddingMode.PKCS7); + } + + public void TestTryDecryptCbc() + { + var tdes = TripleDES.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[16]; + int bytesWritten; + tdes.TryDecryptCbc(ciphertext, iv, destination, out bytesWritten, PaddingMode.PKCS7); + } + + public void TestTryEncryptEcb() + { + var tdes = TripleDES.Create(); + byte[] plaintext = new byte[16]; + byte[] destination = new byte[24]; + int bytesWritten; + tdes.TryEncryptEcb(plaintext, destination, PaddingMode.None, out bytesWritten); + } + + public void TestTryDecryptEcb() + { + var tdes = TripleDES.Create(); + byte[] ciphertext = new byte[16]; + byte[] destination = new byte[16]; + int bytesWritten; + tdes.TryDecryptEcb(ciphertext, destination, PaddingMode.None, out bytesWritten); + } + + public void TestTryEncryptCfb() + { + var tdes = TripleDES.Create(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[24]; + int bytesWritten; + tdes.TryEncryptCfb(plaintext, iv, destination, out bytesWritten, PaddingMode.None, 8); + } + + public void TestTryDecryptCfb() + { + var tdes = TripleDES.Create(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] destination = new byte[16]; + int bytesWritten; + tdes.TryDecryptCfb(ciphertext, iv, destination, out bytesWritten, PaddingMode.None, 8); + } + + // ------------------------------------------------------------------------- + // Section 7: Key/IV generation + // ------------------------------------------------------------------------- + + public void TestGenerateKey() + { + var tdes = TripleDES.Create(); + tdes.GenerateKey(); + } + + public void TestGenerateIV() + { + var tdes = TripleDES.Create(); + tdes.GenerateIV(); + } + + // ------------------------------------------------------------------------- + // Section 8: Combined usage patterns (real-world scenarios) + // Demonstrates that depending rules fire correctly for all derived classes. + // ------------------------------------------------------------------------- + + public void TestTripleDesCbcFullFlow() + { + var tdes = TripleDES.Create(); + tdes.Mode = CipherMode.CBC; + tdes.Padding = PaddingMode.PKCS7; + var encryptor = tdes.CreateEncryptor(); + } + + public void TestTripleDesCspEncryptCbc() + { + var tdes = new TripleDESCryptoServiceProvider(); + tdes.Mode = CipherMode.CBC; + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = tdes.EncryptCbc(plaintext, iv, PaddingMode.PKCS7); + } + + public void TestTripleDesCspDecryptCbc() + { + var tdes = new TripleDESCryptoServiceProvider(); + byte[] ciphertext = new byte[16]; + byte[] iv = new byte[8]; + byte[] plaintext = tdes.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); + } + + public void TestTripleDesCfbFeedback() + { + var tdes = TripleDES.Create(); + tdes.Mode = CipherMode.CFB; + tdes.FeedbackSize = 8; + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = tdes.EncryptCfb(plaintext, iv, PaddingMode.None, 8); + } + + public void TestTripleDesCbcWithEncryptorOverload() + { + var tdes = TripleDES.Create(); + byte[] key = new byte[24]; + byte[] iv = new byte[8]; + var encryptor = tdes.CreateEncryptor(key, iv); + } + + public void TestTripleDesEcbEncrypt() + { + var tdes = new TripleDESCryptoServiceProvider(); + byte[] plaintext = new byte[16]; + byte[] ciphertext = tdes.EncryptEcb(plaintext, PaddingMode.None); + } + + public void TestTripleDesCngEncryptCbc() + { + var tdes = new TripleDESCng(); + byte[] plaintext = new byte[16]; + byte[] iv = new byte[8]; + byte[] ciphertext = tdes.EncryptCbc(plaintext, iv, PaddingMode.PKCS7); + } +} diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetX25519DiffieHellmanTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetX25519DiffieHellmanTestFile.cs new file mode 100644 index 000000000..2b6ca6791 --- /dev/null +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetX25519DiffieHellmanTestFile.cs @@ -0,0 +1,85 @@ +/* + * Comprehensive test file for System.Security.Cryptography X25519DiffieHellman (X25519) + * detection rules. + * + * Covers all three X25519DiffieHellman-related classes and their complete verified operational + * API surface: + * - X25519DiffieHellman (abstract base) — entry point is the static GenerateKey() factory + * (there is no Create() method, unlike ECDiffieHellman) + * - X25519DiffieHellmanCng, X25519DiffieHellmanOpenSsl (derived from X25519DiffieHellman) + * + * Architecture note: DeriveRawSecretAgreement is the only key-agreement operation that exists on + * X25519DiffieHellman (verified against the official Microsoft Learn API reference — no + * DeriveKeyMaterial/DeriveKeyFromHash/DeriveKeyFromHmac/DeriveKeyTls, and no settable KeySize + * property, unlike ECDiffieHellman). See DotNetX25519DiffieHellman.java class javadoc for the + * full, verified rationale. + * + * Known gap: reading/exporting the public key (e.g. ExportPublicKey()) cannot be meaningfully + * modeled as a depending rule (see DotNetX25519DiffieHellman.java class javadoc) — no test + * attempts it. + */ + +using System.Security.Cryptography; + +public class DotNetX25519DiffieHellmanTest +{ + // ------------------------------------------------------------------------- + // Section 1: Factory method / constructors + // ------------------------------------------------------------------------- + + public void TestX25519GenerateKey() + { + var x25519 = X25519DiffieHellman.GenerateKey(); + } + + public void TestX25519Cng() + { + CngKey cngKey = null; + var x25519 = new X25519DiffieHellmanCng(cngKey); + } + + public void TestX25519OpenSsl() + { + SafeEvpPKeyHandle handle = null; + var x25519 = new X25519DiffieHellmanOpenSsl(handle); + } + + // ------------------------------------------------------------------------- + // Section 2: DeriveRawSecretAgreement operation (all overloads collapse to one rule) + // ------------------------------------------------------------------------- + + public void TestDeriveRawSecretAgreementByteArray() + { + var x25519 = X25519DiffieHellman.GenerateKey(); + byte[] otherPartyPublicKey = new byte[32]; + byte[] secret = x25519.DeriveRawSecretAgreement(otherPartyPublicKey); + } + + public void TestDeriveRawSecretAgreementOtherParty() + { + var x25519 = X25519DiffieHellman.GenerateKey(); + var otherParty = X25519DiffieHellman.GenerateKey(); + byte[] secret = x25519.DeriveRawSecretAgreement(otherParty); + } + + // ------------------------------------------------------------------------- + // Section 3: Combined usage patterns (real-world scenarios) + // Demonstrates that the depending rule fires correctly for ALL derived classes. + // ------------------------------------------------------------------------- + + public void TestX25519CngDeriveFlow() + { + CngKey cngKey = null; + var x25519 = new X25519DiffieHellmanCng(cngKey); + byte[] otherPartyPublicKey = new byte[32]; + byte[] secret = x25519.DeriveRawSecretAgreement(otherPartyPublicKey); + } + + public void TestX25519OpenSslDeriveFlow() + { + SafeEvpPKeyHandle handle = null; + var x25519 = new X25519DiffieHellmanOpenSsl(handle); + byte[] otherPartyPublicKey = new byte[32]; + byte[] secret = x25519.DeriveRawSecretAgreement(otherPartyPublicKey); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAlgorithmFactoryTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAlgorithmFactoryTest.java new file mode 100644 index 000000000..707631a9a --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAlgorithmFactoryTest.java @@ -0,0 +1,235 @@ +/* + * 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.Algorithm; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.context.CipherContext; +import com.ibm.engine.model.context.DigestContext; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.context.MacContext; +import com.ibm.mapper.model.BlockCipher; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Test for the generic, string-based {@code Create(string)} factory methods declared directly on + * the abstract base classes of {@code System.Security.Cryptography} ({@code + * DotNetAlgorithmFactory.java}): {@code SymmetricAlgorithm.Create(string)}, {@code + * HashAlgorithm.Create(string)}, {@code KeyedHashAlgorithm.Create(string)}, {@code + * HMAC.Create(string)}, {@code AsymmetricAlgorithm.Create(string)}. + * + *

Finding mapping (one finding per test method in {@code DotNetAlgorithmFactoryTestFile.cs}): + * + *

+ *  0 TestSymmetricAlgorithmCreateAes        → CipherContext, Algorithm "AES"
+ *  1 TestSymmetricAlgorithmCreateRc2        → CipherContext, Algorithm "RC2"
+ *  2 TestSymmetricAlgorithmCreateTripleDes  → CipherContext, Algorithm "3DES"
+ *  3 TestSymmetricAlgorithmCreateDes        → CipherContext, Algorithm "DES"
+ *  4 TestSymmetricAlgorithmCreateAesWithEncryptorAndDecryptor → CipherContext, Algorithm "AES",
+ *      plus two child findings (CipherAction "ENCRYPT" from CreateEncryptor(key, iv) and
+ *      CipherAction "DECRYPT" from CreateDecryptor(key, iv)) — exercises the depending rules now
+ *      attached to SYMMETRIC_ALGORITHM_CREATE.
+ *  5 TestHashAlgorithmCreateSha256          → DigestContext, Algorithm "SHA256"
+ *  6 TestHashAlgorithmCreateShaHyphenated   → DigestContext, Algorithm "SHA-512"
+ *  7 TestHashAlgorithmCreateMd5             → DigestContext, Algorithm "MD5"
+ *  8 TestHashAlgorithmCreateShaBare         → DigestContext, Algorithm "SHA"
+ *  9 TestKeyedHashAlgorithmCreateHmacSha256 → MacContext, Algorithm "HMACSHA256"
+ * 10 TestKeyedHashAlgorithmCreateMacTripleDes → MacContext, Algorithm "MACTripleDES"
+ * 11 TestHmacCreateHmacSha1                 → MacContext, Algorithm "HMACSHA1"
+ * 12 TestHmacCreateFullyQualified           → MacContext, Algorithm
+ *      "System.Security.Cryptography.HMACSHA256"
+ * 13 TestAsymmetricAlgorithmCreateRsa       → KeyContext, Algorithm "RSA"
+ * 14 TestAsymmetricAlgorithmCreateDsa       → KeyContext, Algorithm "DSA"
+ * 15 TestAsymmetricAlgorithmCreateEcdsa     → KeyContext, Algorithm "ECDsa"
+ * 16 TestAsymmetricAlgorithmCreateEcdh      → KeyContext, Algorithm "ECDH"
+ * 
+ */ +class DotNetAlgorithmFactoryTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetAlgorithmFactoryTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue value = detectionStore.getDetectionValues().get(0); + assertThat(value).isInstanceOf(Algorithm.class); + + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + + switch (findingId) { + case 0 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(CipherContext.class); + assertThat(value.asString()).isEqualTo("AES"); + assertThat(node.asString()).isEqualTo("AES"); + } + case 1 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(CipherContext.class); + assertThat(value.asString()).isEqualTo("RC2"); + assertThat(node.asString()).isEqualTo("RC2"); + } + case 2 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(CipherContext.class); + assertThat(value.asString()).isEqualTo("3DES"); + assertThat(node.asString()).isEqualTo("DESede"); + } + case 3 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(CipherContext.class); + assertThat(value.asString()).isEqualTo("DES"); + assertThat(node.asString()).isEqualTo("DES-56"); + } + case 4 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(CipherContext.class); + assertThat(value.asString()).isEqualTo("AES"); + assertThat(node.asString()).isEqualTo("AES"); + assertThat(node.getKind()).isEqualTo(BlockCipher.class); + assertThat(node.getChildren().get(Encrypt.class)).isNotNull(); + assertThat(node.getChildren().get(Decrypt.class)).isNotNull(); + + // detectionStore.getChildren() has one entry per depending rule attached to + // SYMMETRIC_ALGORITHM_CREATE (28: 4 property setters + 24 cipher ops), most of + // them empty for this call site; only the two that actually matched + // (CreateEncryptor/CreateDecryptor) carry a detection value. + List> + populatedChildren = + detectionStore.getChildren().stream() + .filter(child -> !child.getDetectionValues().isEmpty()) + .toList(); + assertThat(populatedChildren).hasSize(2); + Set childActions = + populatedChildren.stream() + .map( + child -> { + assertThat(child.getDetectionValues()).hasSize(1); + IValue childValue = + child.getDetectionValues().get(0); + assertThat(childValue).isInstanceOf(CipherAction.class); + return childValue.asString(); + }) + .collect(Collectors.toSet()); + assertThat(childActions).containsExactlyInAnyOrder("ENCRYPT", "DECRYPT"); + } + case 5 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(DigestContext.class); + assertThat(value.asString()).isEqualTo("SHA256"); + assertThat(node.asString()).isEqualTo("SHA-256"); + } + case 6 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(DigestContext.class); + assertThat(value.asString()).isEqualTo("SHA-512"); + assertThat(node.asString()).isEqualTo("SHA-512"); + } + case 7 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(DigestContext.class); + assertThat(value.asString()).isEqualTo("MD5"); + assertThat(node.asString()).isEqualTo("MD5"); + } + case 8 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(DigestContext.class); + assertThat(value.asString()).isEqualTo("SHA"); + assertThat(node.asString()).isEqualTo("SHA-1"); + } + case 9 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(MacContext.class); + assertThat(value.asString()).isEqualTo("HMACSHA256"); + assertThat(node.asString()).isEqualTo("HMAC-SHA-256"); + } + case 10 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(MacContext.class); + assertThat(value.asString()).isEqualTo("MACTripleDES"); + assertThat(node.asString()).isEqualTo("DESede"); + } + case 11 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(MacContext.class); + assertThat(value.asString()).isEqualTo("HMACSHA1"); + assertThat(node.asString()).isEqualTo("HMAC-SHA-1"); + } + case 12 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(MacContext.class); + assertThat(value.asString()).isEqualTo("System.Security.Cryptography.HMACSHA256"); + assertThat(node.asString()).isEqualTo("HMAC-SHA-256"); + } + case 13 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat(value.asString()).isEqualTo("RSA"); + assertThat(node.asString()).isEqualTo("RSA"); + } + case 14 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat(value.asString()).isEqualTo("DSA"); + assertThat(node.asString()).isEqualTo("DSA"); + } + case 15 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat(value.asString()).isEqualTo("ECDsa"); + assertThat(node.asString()).isEqualTo("ECDSA"); + } + case 16 -> { + assertThat(detectionStore.getDetectionValueContext()) + .isInstanceOf(KeyContext.class); + assertThat(value.asString()).isEqualTo("ECDH"); + assertThat(node.asString()).isEqualTo("ECDH"); + } + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305Test.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305Test.java new file mode 100644 index 000000000..2ab00400a --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305Test.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.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.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.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Test for {@link DotNetChaCha20Poly1305} detection rules. + * + *

Finding mapping (one finding per test method in DotNetChaCha20Poly1305TestFile.cs): + * + *

+ * 0  TestChaCha20Poly1305Ctor          → ChaCha20-Poly1305
+ * 1  TestChaCha20Poly1305Encrypt       → ChaCha20-Poly1305 + Encrypt
+ * 2  TestChaCha20Poly1305EncryptNoAad  → ChaCha20-Poly1305 + Encrypt
+ * 3  TestChaCha20Poly1305Decrypt       → ChaCha20-Poly1305 + Decrypt
+ * 4  TestChaCha20Poly1305DecryptNoAad  → ChaCha20-Poly1305 + Decrypt
+ * 5  TestChaCha20Poly1305FullFlow      → ChaCha20-Poly1305 + Encrypt
+ * 
+ */ +class DotNetChaCha20Poly1305Test extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetChaCha20Poly1305TestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + // Every top-level finding must be ChaCha20-Poly1305 + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("CHACHA20-POLY1305"); + + switch (findingId) { + case 0 -> { + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).asString()).isEqualTo("ChaCha20-Poly1305"); + } + case 1, 2, 5 -> assertEncryptFindings(detectionStore, nodes); + case 3, 4 -> assertDecryptFindings(detectionStore, nodes); + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + private void assertEncryptFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes) { + + DetectionStore encryptStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(encryptStore).isNotNull(); + assertThat(encryptStore.getDetectionValues()).hasSize(1); + assertThat(encryptStore.getDetectionValues().get(0).asString()).isEqualTo("ENCRYPT"); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).asString()).isEqualTo("ChaCha20-Poly1305"); + assertThat(nodes.get(0).getChildren().get(Encrypt.class)).isNotNull(); + } + + private void assertDecryptFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes) { + + DetectionStore decryptStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(decryptStore).isNotNull(); + assertThat(decryptStore.getDetectionValues()).hasSize(1); + assertThat(decryptStore.getDetectionValues().get(0).asString()).isEqualTo("DECRYPT"); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).asString()).isEqualTo("ChaCha20-Poly1305"); + assertThat(nodes.get(0).getChildren().get(Decrypt.class)).isNotNull(); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDiffieHellmanTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDiffieHellmanTest.java index 8b88a4d3a..676cfb813 100755 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDiffieHellmanTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDiffieHellmanTest.java @@ -31,13 +31,57 @@ import com.ibm.engine.model.context.KeyContext; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.KeyLength; import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Generate; +import com.ibm.mapper.model.functionality.KeyDerivation; import com.ibm.plugin.CSharpVerifier; import com.ibm.plugin.TestBase; import java.util.List; import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; +/** + * Comprehensive test for all ECDH (ECDiffieHellman) related detection rules + * (DotNetECDiffieHellman.java). + * + *

Covers all three ECDiffieHellman-related classes and their complete operational API surface: + * + *

    + *
  • ECDiffieHellman (abstract base) + *
  • ECDiffieHellmanCng, ECDiffieHellmanOpenSsl (derived from ECDiffieHellman) + *
+ * + *

Note: every top-level finding translates to a {@link com.ibm.mapper.model.algorithms.ECDH} + * node ({@code node.asString()} == "ECDH"), whose kind is {@link KeyAgreement}, and which always + * carries an {@link Oid} child ("1.3.132.1.12") added unconditionally by the {@code ECDH} algorithm + * model constructor. + * + *

Finding mapping (one finding per test method in DotNetECDiffieHellmanTestFile.cs): + * + *

+ * Section 1 – factory methods / constructors (findings 0–3):
+ *   0 TestECDHCreate             → ECDH
+ *   1 TestECDHCreateWithCurve    → ECDH
+ *   2 TestECDHCng                → ECDH
+ *   3 TestECDHOpenSsl            → ECDH
+ *
+ * Section 2 – property KeySize setters (findings 4–5):
+ *   4 TestPropertyKeySize256     → ECDH, KeyLength child "256"
+ *   5 TestPropertyKeySize384     → ECDH, KeyLength child "384"
+ *
+ * Section 3 – key-derivation operations (findings 6–10):
+ *   6  TestDeriveKeyMaterial          → ECDH + KeyDerivation child
+ *   7  TestDeriveKeyFromHash          → ECDH + KeyDerivation child
+ *   8  TestDeriveKeyFromHmac          → ECDH + KeyDerivation child
+ *   9  TestDeriveKeyTls               → ECDH + KeyDerivation child
+ *   10 TestDeriveRawSecretAgreement   → ECDH + Generate child
+ *
+ * Section 4 – combined usage patterns (findings 11–12):
+ *   11 TestECDHCngFullFlow        → ECDH + KeyDerivation (+ KeyLength child "384")
+ *   12 TestECDHOpenSslDeriveFlow  → ECDH + KeyDerivation
+ * 
+ */ class DotNetECDiffieHellmanTest extends TestBase { @Test @@ -52,11 +96,13 @@ public void asserts( DetectionStore detectionStore, @Nonnull List nodes) { - assertThat(detectionStore.getDetectionValues()).hasSize(1); + + // Every top-level finding must be ECDH assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(ValueAction.class); - assertThat(value0.asString()).isEqualTo("ECDH"); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("ECDH"); assertThat(nodes).hasSize(1); INode node = nodes.get(0); @@ -66,5 +112,72 @@ public void asserts( INode oid = node.getChildren().get(Oid.class); assertThat(oid).isNotNull(); assertThat(oid.asString()).isEqualTo("1.3.132.1.12"); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: simple constructors — only ECDH, no extra children + // ----------------------------------------------------------------- + case 0, 1, 2, 3 -> { + // node.asString() already asserted to be "ECDH" above + } + + // ----------------------------------------------------------------- + // Section 2: property KeySize setters + // ----------------------------------------------------------------- + case 4 -> assertKeySize(node, "256"); + case 5 -> assertKeySize(node, "384"); + + // ----------------------------------------------------------------- + // Section 3: key-derivation operations + // ----------------------------------------------------------------- + case 6, 7, 8, 9 -> assertKeyDerivation(detectionStore, node); + case 10 -> assertRawSecretAgreement(detectionStore, node); + + // ----------------------------------------------------------------- + // Section 4: combined usage patterns + // ----------------------------------------------------------------- + case 11 -> { + assertKeyDerivation(detectionStore, node); + assertThat(node.getChildren().get(KeyLength.class)).isNotNull(); + assertThat(node.getChildren().get(KeyLength.class).asString()).isEqualTo("384"); + } + case 12 -> assertKeyDerivation(detectionStore, node); + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertKeySize(@Nonnull INode node, @Nonnull String expectedKeySize) { + assertThat(node.getChildren().get(KeyLength.class)).isNotNull(); + assertThat(node.getChildren().get(KeyLength.class).asString()).isEqualTo(expectedKeySize); + } + + private void assertKeyDerivation( + @Nonnull DetectionStore store, + @Nonnull INode node) { + + DetectionStore deriveStore = + getStoreOfValueType(ValueAction.class, store.getChildren()); + assertThat(deriveStore).isNotNull(); + assertThat(deriveStore.getDetectionValues()).hasSize(1); + + assertThat(node.getChildren().get(KeyDerivation.class)).isNotNull(); + } + + private void assertRawSecretAgreement( + @Nonnull DetectionStore store, + @Nonnull INode node) { + + DetectionStore deriveStore = + getStoreOfValueType(ValueAction.class, store.getChildren()); + assertThat(deriveStore).isNotNull(); + assertThat(deriveStore.getDetectionValues()).hasSize(1); + + assertThat(node.getChildren().get(Generate.class)).isNotNull(); } } diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDsaTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDsaTest.java index f7337a1c3..6ca1e6f07 100755 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDsaTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetECDsaTest.java @@ -27,16 +27,67 @@ 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.SignatureAction; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.KeyContext; import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.mapper.model.functionality.Verify; import com.ibm.plugin.CSharpVerifier; import com.ibm.plugin.TestBase; import java.util.List; import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; +/** + * Comprehensive test for all ECDSA-related detection rules (DotNetECDsa.java). + * + *

Covers all three ECDsa-related classes and their complete operational API surface: + * + *

    + *
  • ECDsa (abstract base) + *
  • ECDsaCng, ECDsaOpenSsl (derived from ECDsa) + *
+ * + *

Note: {@code ECDSA.asString()} (see {@code mapper/model/algorithms/ECDSA.java}) only appends + * an {@code EllipticCurve} or {@code MessageDigest} suffix, matching the CycloneDX cryptography + * registry pattern {@code ECDSA[-{ellipticCurve}][-{hashAlgorithm}]} (which has no {@code + * keyLength} placeholder, unlike RSA/DSA). Therefore a detected {@code KeySize} property still + * surfaces as a {@link KeyLength} child node, but does not change {@code node.asString()}. + * + *

Finding mapping (one finding per test method in DotNetECDsaTestFile.cs): + * + *

+ * Section 1 – factory methods / constructors (findings 0–6):
+ *   0 TestECDsaCreate             → ECDSA
+ *   1 TestECDsaCreateWithCurve    → ECDSA
+ *   2 TestECDsaCng                → ECDSA
+ *   3 TestECDsaOpenSsl            → ECDSA
+ *   4 TestECDsaCngWithKey         → ECDSA (new ECDsaCng(CngKey))
+ *   5 TestECDsaCngWithCurve       → ECDSA (new ECDsaCng(ECCurve))
+ *   6 TestECDsaCngWithKeySize     → ECDSA (new ECDsaCng(int))
+ *
+ * Section 2 – property KeySize setters (findings 7–8):
+ *   7 TestPropertyKeySize256      → ECDSA, KeyLength child "256"
+ *   8 TestPropertyKeySize384      → ECDSA, KeyLength child "384"
+ *
+ * Section 3 – SignData / TrySignData / VerifyData (findings 9–11):
+ *   9  TestSignData      → ECDSA + Sign
+ *   10 TestTrySignData   → ECDSA + Sign
+ *   11 TestVerifyData    → ECDSA + Verify
+ *
+ * Section 4 – SignHash / TrySignHash / VerifyHash (findings 12–14):
+ *   12 TestSignHash     → ECDSA + Sign
+ *   13 TestTrySignHash  → ECDSA + Sign
+ *   14 TestVerifyHash   → ECDSA + Verify
+ *
+ * Section 5 – combined usage patterns (findings 15–16):
+ *   15 TestECDsaCngFullFlow        → ECDSA + Sign (+ KeyLength child "384")
+ *   16 TestECDsaOpenSslVerifyFlow  → ECDSA + Verify
+ * 
+ */ class DotNetECDsaTest extends TestBase { @Test @@ -51,18 +102,92 @@ public void asserts( DetectionStore detectionStore, @Nonnull List nodes) { - assertThat(detectionStore.getDetectionValues()).hasSize(1); + + // Every top-level finding must be ECDSA assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(ValueAction.class); - assertThat(value0.asString()).isEqualTo("ECDSA"); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("ECDSA"); - /* - * Translation - */ assertThat(nodes).hasSize(1); INode node = nodes.get(0); assertThat(node.getKind()).isEqualTo(Signature.class); assertThat(node.asString()).isEqualTo("ECDSA"); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: simple constructors — only ECDSA, no children fired + // ----------------------------------------------------------------- + case 0, 1, 2, 3, 4, 5, 6 -> { + // node.asString() already asserted to be "ECDSA" above + } + + // ----------------------------------------------------------------- + // Section 2: property KeySize setters + // ----------------------------------------------------------------- + case 7 -> assertKeySize(node, "256"); + case 8 -> assertKeySize(node, "384"); + + // ----------------------------------------------------------------- + // Section 3: SignData / TrySignData / VerifyData + // ----------------------------------------------------------------- + case 9, 10 -> assertSign(detectionStore, node); + case 11 -> assertVerify(detectionStore, node); + + // ----------------------------------------------------------------- + // Section 4: SignHash / TrySignHash / VerifyHash + // ----------------------------------------------------------------- + case 12, 13 -> assertSign(detectionStore, node); + case 14 -> assertVerify(detectionStore, node); + + // ----------------------------------------------------------------- + // Section 5: combined usage patterns + // ----------------------------------------------------------------- + case 15 -> { + assertSign(detectionStore, node); + assertThat(node.getChildren().get(KeyLength.class)).isNotNull(); + assertThat(node.getChildren().get(KeyLength.class).asString()).isEqualTo("384"); + } + case 16 -> assertVerify(detectionStore, node); + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertKeySize(@Nonnull INode node, @Nonnull String expectedKeySize) { + assertThat(node.getChildren().get(KeyLength.class)).isNotNull(); + assertThat(node.getChildren().get(KeyLength.class).asString()).isEqualTo(expectedKeySize); + } + + private void assertSign( + @Nonnull DetectionStore store, + @Nonnull INode node) { + + DetectionStore signStore = + getStoreOfValueType(SignatureAction.class, store.getChildren()); + assertThat(signStore).isNotNull(); + assertThat(signStore.getDetectionValues()).hasSize(1); + assertThat(signStore.getDetectionValues().get(0).asString()).isEqualTo("SIGN"); + + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + + private void assertVerify( + @Nonnull DetectionStore store, + @Nonnull INode node) { + + DetectionStore verifyStore = + getStoreOfValueType(SignatureAction.class, store.getChildren()); + assertThat(verifyStore).isNotNull(); + assertThat(verifyStore.getDetectionValues()).hasSize(1); + assertThat(verifyStore.getDetectionValues().get(0).asString()).isEqualTo("VERIFY"); + + assertThat(node.getChildren().get(Verify.class)).isNotNull(); } } diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetHMACTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetHMACTest.java index 261e1dfc6..09b10e7fd 100755 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetHMACTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetHMACTest.java @@ -29,6 +29,7 @@ import com.ibm.engine.model.IValue; import com.ibm.engine.model.ValueAction; 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; @@ -39,6 +40,26 @@ import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; +/** + * Tests for {@link DotNetHMAC}. + * + *

findingId → test method → expected translated node ({@code asString()}), in source order of + * {@code DotNetHMACTestFile.cs}: + * + *

    + *
  • 0 → {@code TestHmacSha1} → {@code HMAC-SHA-1} + *
  • 1 → {@code TestHmacSha256} → {@code HMAC-SHA-256} + *
  • 2 → {@code TestHmacSha384} → {@code HMAC-SHA-384} + *
  • 3 → {@code TestHmacSha512} → {@code HMAC-SHA-512} + *
  • 4 → {@code TestHmacMd5} → {@code HMAC-MD5} + *
  • 5 → {@code TestHmacRipemd160} → {@code HMAC-RIPEMD} (digest child: {@code RIPEMD-160}) + *
  • 6 → {@code TestHmacSha3_256} → {@code HMAC-SHA3-256} + *
  • 7 → {@code TestHmacSha3_384} → {@code HMAC-SHA3-384} + *
  • 8 → {@code TestHmacSha3_512} → {@code HMAC-SHA3-512} + *
  • 9 → {@code TestMacTripleDes} → {@code DESede} (not an HMAC(digest) node; see {@link + * DotNetHMAC} javadoc for why {@code MACTripleDES} is modeled as DESede-as-Mac) + *
+ */ class DotNetHMACTest extends TestBase { @Test @@ -120,6 +141,55 @@ public void asserts( assertThat(digestSize).isNotNull(); assertThat(digestSize.asString()).isEqualTo("128"); } + case 5 -> { + assertThat(value0.asString()).isEqualTo("HMACRIPEMD160"); + assertThat(node.asString()).isEqualTo("HMAC-RIPEMD"); + INode digest = node.getChildren().get(MessageDigest.class); + assertThat(digest).isNotNull(); + assertThat(digest.asString()).isEqualTo("RIPEMD-160"); + INode digestSize = digest.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("160"); + } + case 6 -> { + assertThat(value0.asString()).isEqualTo("HMACSHA3_256"); + assertThat(node.asString()).isEqualTo("HMAC-SHA3-256"); + INode digest = node.getChildren().get(MessageDigest.class); + assertThat(digest).isNotNull(); + assertThat(digest.asString()).isEqualTo("SHA3-256"); + INode digestSize = digest.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("256"); + } + case 7 -> { + assertThat(value0.asString()).isEqualTo("HMACSHA3_384"); + assertThat(node.asString()).isEqualTo("HMAC-SHA3-384"); + INode digest = node.getChildren().get(MessageDigest.class); + assertThat(digest).isNotNull(); + assertThat(digest.asString()).isEqualTo("SHA3-384"); + INode digestSize = digest.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("384"); + } + case 8 -> { + assertThat(value0.asString()).isEqualTo("HMACSHA3_512"); + assertThat(node.asString()).isEqualTo("HMAC-SHA3-512"); + INode digest = node.getChildren().get(MessageDigest.class); + assertThat(digest).isNotNull(); + assertThat(digest.asString()).isEqualTo("SHA3-512"); + INode digestSize = digest.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("512"); + } + case 9 -> { + // MACTripleDES is not HMAC-based: it translates to the DESede algorithm + // reinterpreted "as" a Mac (see DotNetHMAC javadoc), not to an HMAC(digest) node. + assertThat(value0.asString()).isEqualTo("MACTRIPLEDES"); + assertThat(node.asString()).isEqualTo("DESede"); + INode blockSize = node.getChildren().get(BlockSize.class); + assertThat(blockSize).isNotNull(); + assertThat(blockSize.asString()).isEqualTo("64"); + } default -> throw new IllegalStateException("Unexpected findingId: " + findingId); } } diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKMACTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKMACTest.java new file mode 100644 index 000000000..47ee60304 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKMACTest.java @@ -0,0 +1,106 @@ +/* + * 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.MacContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Mac; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link DotNetKMAC}. + * + *

findingId → test method → expected translated node ({@code asString()}), in source order of + * {@code DotNetKMACTestFile.cs}: + * + *

    + *
  • 0 → {@code TestKmac128} → {@code KMAC128} + *
  • 1 → {@code TestKmac256} → {@code KMAC256} + *
  • 2 → {@code TestKmacXof128} → {@code KMAC128} (raw detected value is {@code KMACXOF128}; the + * translated node collapses to the same {@code KMAC} model as {@code Kmac128} — see {@link + * DotNetKMAC} javadoc "Known modeling gap" section) + *
  • 3 → {@code TestKmacXof256} → {@code KMAC256} (raw detected value is {@code KMACXOF256}; + * same collapse as above, for the 256-bit pair) + *
+ */ +class DotNetKMACTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetKMACTestFile.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(MacContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(ValueAction.class); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(Mac.class); + + switch (findingId) { + case 0 -> { + assertThat(value0.asString()).isEqualTo("KMAC128"); + assertThat(node.asString()).isEqualTo("KMAC128"); + } + case 1 -> { + assertThat(value0.asString()).isEqualTo("KMAC256"); + assertThat(node.asString()).isEqualTo("KMAC256"); + } + case 2 -> { + assertThat(value0.asString()).isEqualTo("KMACXOF128"); + assertThat(node.asString()).isEqualTo("KMAC128"); + } + case 3 -> { + assertThat(value0.asString()).isEqualTo("KMACXOF256"); + assertThat(node.asString()).isEqualTo("KMAC256"); + } + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivationTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivationTest.java new file mode 100644 index 000000000..9405bfe4d --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivationTest.java @@ -0,0 +1,156 @@ +/* + * 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.KeyDerivationFunction; +import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; +import com.ibm.mapper.model.functionality.KeyDerivation; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all KDF-family detection rules (DotNetKeyDerivation.java), excluding + * {@code Rfc2898DeriveBytes} (covered separately by {@code DotNetRfc2898DeriveBytesTest}). + * + *

Covers: + * + *

    + *
  • {@code HKDF} — static-only class ({@code Extract}, {@code Expand}, {@code DeriveKey}) + *
  • {@code SP800108HmacCounterKdf} — constructor + instance {@code DeriveKey}, and the static + * one-shot {@code DeriveBytes} overload + *
  • {@code PasswordDeriveBytes} — constructor + instance {@code GetBytes} / {@code + * CryptDeriveKey} + *
+ * + *

Node strings below were captured from an actual test run's debug log ({@code + * target/node-tree.log}), not guessed: + * + *

+ * Finding mapping (one finding per test method in DotNetKeyDerivationTestFile.cs):
+ *
+ * 0 TestHkdfExtract                     → KeyDerivationFunction "HKDF"
+ * 1 TestHkdfExpand                      → KeyDerivationFunction "HKDF"
+ * 2 TestHkdfDeriveKey                   → KeyDerivationFunction "HKDF"
+ * 3 TestSp800108CtorAndDeriveKey        → KeyDerivationFunction "SP800_108_CounterKDF"
+ *                                          + KeyDerivation "KEYDERIVATION" child
+ * 4 TestSp800108StaticDeriveBytes       → KeyDerivationFunction "SP800_108_CounterKDF"
+ *                                          (no children — static one-shot call)
+ * 5 TestPasswordDeriveBytesGetBytes     → PasswordBasedKeyDerivationFunction "PBKDF1"
+ *                                          + KeyDerivation "KEYDERIVATION" child
+ * 6 TestPasswordDeriveBytesCryptDeriveKey → PasswordBasedKeyDerivationFunction "PBKDF1"
+ *                                          + KeyDerivation "KEYDERIVATION" child
+ * 
+ */ +class DotNetKeyDerivationTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetKeyDerivationTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + + switch (findingId) { + // ----------------------------------------------------------------- + // Section 1: HKDF static methods — no depending rules, no children + // ----------------------------------------------------------------- + case 0, 1, 2 -> { + assertThat(primary.asString()).isEqualTo("HKDF"); + assertThat(node.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(node.asString()).isEqualTo("HKDF"); + assertThat(node.getChildren()).isEmpty(); + } + + // ----------------------------------------------------------------- + // Section 2: SP800108HmacCounterKdf + // ----------------------------------------------------------------- + case 3 -> { + assertThat(primary.asString()).isEqualTo("SP800108"); + assertThat(node.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(node.asString()).isEqualTo("SP800_108_CounterKDF"); + assertKeyDerivationChild(detectionStore, node); + } + case 4 -> { + assertThat(primary.asString()).isEqualTo("SP800108"); + assertThat(node.getKind()).isEqualTo(KeyDerivationFunction.class); + assertThat(node.asString()).isEqualTo("SP800_108_CounterKDF"); + assertThat(node.getChildren()).isEmpty(); + } + + // ----------------------------------------------------------------- + // Section 3: PasswordDeriveBytes + // ----------------------------------------------------------------- + case 5, 6 -> { + assertThat(primary.asString()).isEqualTo("PBKDF1"); + assertThat(node.getKind()).isEqualTo(PasswordBasedKeyDerivationFunction.class); + assertThat(node.asString()).isEqualTo("PBKDF1"); + assertKeyDerivationChild(detectionStore, node); + } + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertKeyDerivationChild( + @Nonnull DetectionStore store, + @Nonnull INode node) { + + DetectionStore deriveStore = + getStoreOfValueType(ValueAction.class, store.getChildren()); + assertThat(deriveStore).isNotNull(); + assertThat(deriveStore.getDetectionValues()).hasSize(1); + + assertThat(node.getChildren().get(KeyDerivation.class)).isNotNull(); + assertThat(node.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetLegacyFormattersTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetLegacyFormattersTest.java new file mode 100644 index 000000000..4b3c18e92 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetLegacyFormattersTest.java @@ -0,0 +1,264 @@ +/* + * 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.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MaskGenerationFunction; +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.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Test for the legacy Formatter/Deformatter and mask-generation detection rules + * (DotNetLegacyFormatters.java). + * + *

Finding mapping (one finding per test method in DotNetLegacyFormattersTestFile.cs; the + * parameterless constructor overload is used throughout, so — unlike DotNetRSATest/DotNetDSATest — + * no extra findings from DSA.Create()/RSA.Create() are interleaved): + * + *

+ * 0  TestDsaSignatureFormatterCreateSignature          → DSA + Sign
+ * 1  TestDsaSignatureFormatterSetHashAlgorithm         → DSA-SHA-1 + MessageDigest(SHA-1)
+ * 2  TestDsaSignatureDeformatterVerifySignature        → DSA + Verify
+ * 3  TestRsaPkcs1SignatureFormatterCreateSignature     → RSA + Sign
+ * 4  TestRsaPkcs1SignatureFormatterSetHashAlgorithm    → RSA + MessageDigest(SHA-256)
+ * 5  TestRsaPkcs1SignatureDeformatterVerifySignature   → RSA + Verify
+ * 6  TestRsaOaepKeyExchangeFormatterCreateKeyExchange1 → RSA-OAEP + Encrypt + Padding(OAEP)
+ * 7  TestRsaOaepKeyExchangeFormatterCreateKeyExchange2 → RSA-OAEP + Encrypt + Padding(OAEP)
+ * 8  TestRsaOaepKeyExchangeDeformatterDecryptKeyExchange → RSA-OAEP + Decrypt + Padding(OAEP)
+ * 9  TestRsaPkcs1KeyExchangeFormatterCreateKeyExchange1  → RSA + Encrypt + Padding(PKCS1)
+ * 10 TestRsaPkcs1KeyExchangeFormatterCreateKeyExchange2  → RSA + Encrypt + Padding(PKCS1)
+ * 11 TestRsaPkcs1KeyExchangeDeformatterDecryptKeyExchange → RSA + Decrypt + Padding(PKCS1)
+ * 12 TestPkcs1MaskGenerationMethodCreate                → MGF1
+ * 13 TestPkcs1MaskGenerationMethodSetHashName           → MGF1 + MessageDigest(SHA-256)
+ * 
+ * + * These node-string / OID values were captured by first running this test with a no-op {@code + * asserts()} and inspecting {@code target/node-tree.log}, per the project's testing guidelines + * (never guessing {@code asString()} output). + */ +class DotNetLegacyFormattersTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetLegacyFormattersTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + // Every top-level finding in this file is a KeyContext ValueAction (DSA/RSA/MGF1) + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + + switch (findingId) { + case 0 -> { + assertThat(primary.asString()).isEqualTo("DSA"); + assertThat(node.getKind()).isEqualTo(Signature.class); + assertThat(node.asString()).isEqualTo("DSA"); + assertOid(node, "1.2.840.10040.4.1"); + assertSign(detectionStore, node); + } + case 1 -> { + assertThat(primary.asString()).isEqualTo("DSA"); + assertThat(node.getKind()).isEqualTo(Signature.class); + assertThat(node.asString()).isEqualTo("DSA-SHA-1"); + assertOid(node, "1.2.840.10040.4.3"); + assertDigest(node, "SHA-1"); + } + case 2 -> { + assertThat(primary.asString()).isEqualTo("DSA"); + assertThat(node.getKind()).isEqualTo(Signature.class); + assertThat(node.asString()).isEqualTo("DSA"); + assertOid(node, "1.2.840.10040.4.1"); + assertVerify(detectionStore, node); + } + case 3 -> { + assertThat(primary.asString()).isEqualTo("RSA"); + assertThat(node.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(node.asString()).isEqualTo("RSA"); + assertOid(node, "1.2.840.113549.1.1.1"); + assertSign(detectionStore, node); + } + case 4 -> { + assertThat(primary.asString()).isEqualTo("RSA"); + assertThat(node.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(node.asString()).isEqualTo("RSA"); + assertOid(node, "1.2.840.113549.1.1.1"); + assertDigest(node, "SHA-256"); + } + case 5 -> { + assertThat(primary.asString()).isEqualTo("RSA"); + assertThat(node.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(node.asString()).isEqualTo("RSA"); + assertOid(node, "1.2.840.113549.1.1.1"); + assertVerify(detectionStore, node); + } + case 6, 7 -> { + assertThat(primary.asString()).isEqualTo("RSA"); + assertThat(node.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(node.asString()).isEqualTo("RSA-OAEP"); + assertOid(node, "1.2.840.113549.1.1.7"); + assertPadding(node, "OAEP"); + assertEncrypt(detectionStore, node); + } + case 8 -> { + assertThat(primary.asString()).isEqualTo("RSA"); + assertThat(node.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(node.asString()).isEqualTo("RSA-OAEP"); + assertOid(node, "1.2.840.113549.1.1.7"); + assertPadding(node, "OAEP"); + assertDecrypt(detectionStore, node); + } + case 9, 10 -> { + assertThat(primary.asString()).isEqualTo("RSA"); + assertThat(node.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(node.asString()).isEqualTo("RSA"); + assertOid(node, "1.2.840.113549.1.1.1"); + assertPadding(node, "PKCS1"); + assertEncrypt(detectionStore, node); + } + case 11 -> { + assertThat(primary.asString()).isEqualTo("RSA"); + assertThat(node.getKind()).isEqualTo(PublicKeyEncryption.class); + assertThat(node.asString()).isEqualTo("RSA"); + assertOid(node, "1.2.840.113549.1.1.1"); + assertPadding(node, "PKCS1"); + assertDecrypt(detectionStore, node); + } + case 12 -> { + assertThat(primary.asString()).isEqualTo("MGF1"); + assertThat(node.getKind()).isEqualTo(MaskGenerationFunction.class); + assertThat(node.asString()).isEqualTo("MGF1"); + assertOid(node, "1.2.840.113549.1.1.8"); + } + case 13 -> { + assertThat(primary.asString()).isEqualTo("MGF1"); + assertThat(node.getKind()).isEqualTo(MaskGenerationFunction.class); + assertThat(node.asString()).isEqualTo("MGF1"); + assertOid(node, "1.2.840.113549.1.1.8"); + assertDigest(node, "SHA-256"); + } + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertOid(@Nonnull INode node, @Nonnull String expectedOid) { + INode oid = node.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.asString()).isEqualTo(expectedOid); + } + + private void assertDigest(@Nonnull INode node, @Nonnull String expectedDigest) { + INode digest = node.getChildren().get(MessageDigest.class); + assertThat(digest).isNotNull(); + assertThat(digest.asString()).isEqualTo(expectedDigest); + } + + private void assertPadding(@Nonnull INode node, @Nonnull String expectedPadding) { + INode padding = node.getChildren().get(Padding.class); + assertThat(padding).isNotNull(); + assertThat(padding.asString()).isEqualTo(expectedPadding); + } + + private void assertSign( + @Nonnull DetectionStore store, + @Nonnull INode node) { + DetectionStore signStore = + getStoreOfValueType(SignatureAction.class, store.getChildren()); + assertThat(signStore).isNotNull(); + assertThat(signStore.getDetectionValues()).hasSize(1); + assertThat(signStore.getDetectionValues().get(0).asString()).isEqualTo("SIGN"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + + private void assertVerify( + @Nonnull DetectionStore store, + @Nonnull INode node) { + DetectionStore verifyStore = + getStoreOfValueType(SignatureAction.class, store.getChildren()); + assertThat(verifyStore).isNotNull(); + assertThat(verifyStore.getDetectionValues()).hasSize(1); + assertThat(verifyStore.getDetectionValues().get(0).asString()).isEqualTo("VERIFY"); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); + } + + private void assertEncrypt( + @Nonnull DetectionStore store, + @Nonnull INode node) { + // The CipherAction and the constant Padding value (see DotNetLegacyFormatters's + // CREATE_KEY_EXCHANGE_* rules) are captured by the same rule/level, so this child store's + // detection values contains both — unlike DotNetRSA's plain Encrypt/Decrypt rules. + DetectionStore encryptStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(encryptStore).isNotNull(); + assertThat(encryptStore.getDetectionValues()) + .anyMatch(value -> "ENCRYPT".equals(value.asString())); + assertThat(node.getChildren().get(Encrypt.class)).isNotNull(); + } + + private void assertDecrypt( + @Nonnull DetectionStore store, + @Nonnull INode node) { + DetectionStore decryptStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(decryptStore).isNotNull(); + assertThat(decryptStore.getDetectionValues()) + .anyMatch(value -> "DECRYPT".equals(value.asString())); + assertThat(node.getChildren().get(Decrypt.class)).isNotNull(); + } +} 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 000000000..c0cc117e5 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLDsaTest.java @@ -0,0 +1,274 @@ +/* + * 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.ParameterIdentifier; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all ML-DSA and Composite ML-DSA related detection rules + * (DotNetMLDsa.java). + * + *

Covers the {@code MLDsa} abstract base (all of its static factory methods), the {@code + * MLDsaCng}/{@code MLDsaOpenSsl} native-interop derived classes, the {@code SignData}/{@code + * VerifyData}/{@code SignMu}/{@code VerifyMu}/{@code SignPreHash}/{@code VerifyPreHash} instance + * operations, and the {@code CompositeMLDsa}/{@code CompositeMLDsaCng} counterparts. + * + *

Every top-level finding translates to a {@link com.ibm.mapper.model.algorithms.MLDSA} node + * (kind {@link Signature}). When the {@code MLDsaAlgorithm} parameter set is captured, {@code + * node.asString()} is {@code "ML-DSA-44"}/{@code "ML-DSA-65"}/{@code "ML-DSA-87"}, the node carries + * a {@link ParameterSetIdentifier} child and — added by the enricher, confirmed from the actual + * debug output — an {@link Oid} child. For {@code CompositeMLDsa}, the full {@code + * CompositeMLDsaAlgorithm} name (e.g. {@code "MLDsa44WithECDsaP256"}) is captured verbatim as the + * {@link ParameterSetIdentifier} (see DotNetMLDsa's class javadoc for why), which the enricher does + * not recognize, so it falls back to the generic NIST signature base OID. + * + *

Finding mapping (one finding per test method in DotNetMLDsaTestFile.cs; verified against the + * actual {@code target/node-tree.log} output of a debug run, not guessed): + * + *

+ * Section 1 – MLDsa algorithm-parameterized creation (findings 0–5):
+ *   0 TestGenerateKey44            → ML-DSA-44
+ *   1 TestGenerateKey65            → ML-DSA-65
+ *   2 TestGenerateKey87            → ML-DSA-87
+ *   3 TestImportMLDsaPrivateKey    → ML-DSA-65
+ *   4 TestImportMLDsaPrivateSeed   → ML-DSA-44
+ *   5 TestImportMLDsaPublicKey     → ML-DSA-87
+ *
+ * Section 2 – MLDsa structural imports, no parameter set (findings 6–10):
+ *   6  TestImportPkcs8PrivateKey          → ML-DSA (generic)
+ *   7  TestImportSubjectPublicKeyInfo     → ML-DSA (generic)
+ *   8  TestImportFromPem                  → ML-DSA (generic)
+ *   9  TestImportEncryptedPkcs8PrivateKey → ML-DSA (generic)
+ *   10 TestImportFromEncryptedPem         → ML-DSA (generic)
+ *
+ * Section 3 – MLDsa native-interop constructors, no parameter set (findings 11–12):
+ *   11 TestMLDsaCng      → ML-DSA (generic)
+ *   12 TestMLDsaOpenSsl  → ML-DSA (generic)
+ *
+ * Section 4 – MLDsa Sign / Verify operations (findings 13–18):
+ *   13 TestSignData      → ML-DSA-65, Sign child
+ *   14 TestVerifyData    → ML-DSA-65, Verify child
+ *   15 TestSignMu        → ML-DSA-65, Sign child
+ *   16 TestVerifyMu      → ML-DSA-65, Verify child
+ *   17 TestSignPreHash   → ML-DSA-65, Sign child
+ *   18 TestVerifyPreHash → ML-DSA-65, Verify child
+ *
+ * Section 5 – CompositeMLDsa algorithm-parameterized creation (findings 19–21):
+ *   19 TestCompositeGenerateKey       → ML-DSA-MLDsa44WithECDsaP256
+ *   20 TestCompositeImportPrivateKey  → ML-DSA-MLDsa65WithRSA3072Pss
+ *   21 TestCompositeImportPublicKey   → ML-DSA-MLDsa87WithEd448
+ *
+ * Section 6 – CompositeMLDsa structural imports, no parameter set (findings 22–26):
+ *   22 TestCompositeImportPkcs8PrivateKey          → ML-DSA (generic)
+ *   23 TestCompositeImportSubjectPublicKeyInfo     → ML-DSA (generic)
+ *   24 TestCompositeImportFromPem                  → ML-DSA (generic)
+ *   25 TestCompositeImportEncryptedPkcs8PrivateKey → ML-DSA (generic)
+ *   26 TestCompositeImportFromEncryptedPem         → ML-DSA (generic)
+ *
+ * Section 7 – CompositeMLDsaCng native-interop constructor, no parameter set (finding 27):
+ *   27 TestCompositeMLDsaCng → ML-DSA (generic)
+ *
+ * Section 8 – CompositeMLDsa Sign / Verify operations (findings 28–29):
+ *   28 TestCompositeSignData   → ML-DSA-MLDsa44WithECDsaP256, Sign child
+ *   29 TestCompositeVerifyData → ML-DSA-MLDsa44WithECDsaP256, Verify child
+ *
+ * Section 9 – combined usage pattern (finding 30):
+ *   30 TestFullFlow → ML-DSA-87, Sign child, Verify child
+ * 
+ */ +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) { + + // Every top-level finding must be ML-DSA + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("ML-DSA"); + + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(Signature.class); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: MLDsa algorithm-parameterized creation + // ----------------------------------------------------------------- + case 0 -> assertParameterSet(detectionStore, node, "MLDsa44", "44"); + case 1 -> assertParameterSet(detectionStore, node, "MLDsa65", "65"); + case 2 -> assertParameterSet(detectionStore, node, "MLDsa87", "87"); + case 3 -> assertParameterSet(detectionStore, node, "MLDsa65", "65"); + case 4 -> assertParameterSet(detectionStore, node, "MLDsa44", "44"); + case 5 -> assertParameterSet(detectionStore, node, "MLDsa87", "87"); + + // ----------------------------------------------------------------- + // Section 2 & 3: generic ML-DSA, no parameter set + // ----------------------------------------------------------------- + case 6, 7, 8, 9, 10, 11, 12 -> assertGeneric(node); + + // ----------------------------------------------------------------- + // Section 4: MLDsa Sign / Verify operations + // ----------------------------------------------------------------- + case 13 -> { + assertParameterSet(detectionStore, node, "MLDsa65", "65"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + case 14 -> { + assertParameterSet(detectionStore, node, "MLDsa65", "65"); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); + } + case 15 -> { + assertParameterSet(detectionStore, node, "MLDsa65", "65"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + case 16 -> { + assertParameterSet(detectionStore, node, "MLDsa65", "65"); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); + } + case 17 -> { + assertParameterSet(detectionStore, node, "MLDsa65", "65"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + case 18 -> { + assertParameterSet(detectionStore, node, "MLDsa65", "65"); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); + } + + // ----------------------------------------------------------------- + // Section 5: CompositeMLDsa algorithm-parameterized creation + // ----------------------------------------------------------------- + case 19 -> assertCompositeParameterSet(detectionStore, node, "MLDsa44WithECDsaP256"); + case 20 -> assertCompositeParameterSet(detectionStore, node, "MLDsa65WithRSA3072Pss"); + case 21 -> assertCompositeParameterSet(detectionStore, node, "MLDsa87WithEd448"); + + // ----------------------------------------------------------------- + // Section 6 & 7: generic ML-DSA, no parameter set (CompositeMLDsa) + // ----------------------------------------------------------------- + case 22, 23, 24, 25, 26, 27 -> assertGeneric(node); + + // ----------------------------------------------------------------- + // Section 8: CompositeMLDsa Sign / Verify operations + // ----------------------------------------------------------------- + case 28 -> { + assertCompositeParameterSet(detectionStore, node, "MLDsa44WithECDsaP256"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + case 29 -> { + assertCompositeParameterSet(detectionStore, node, "MLDsa44WithECDsaP256"); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); + } + + // ----------------------------------------------------------------- + // Section 9: combined usage pattern + // ----------------------------------------------------------------- + case 30 -> { + assertParameterSet(detectionStore, node, "MLDsa87", "87"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); + } + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertParameterSet( + @Nonnull DetectionStore store, + @Nonnull INode node, + @Nonnull String expectedRawIdentifier, + @Nonnull String expectedParameterSet) { + + DetectionStore + parameterIdentifierStore = + getStoreOfValueType(ParameterIdentifier.class, store.getChildren()); + assertThat(parameterIdentifierStore).isNotNull(); + assertThat(parameterIdentifierStore.getDetectionValues()).hasSize(1); + assertThat(parameterIdentifierStore.getDetectionValues().get(0).asString()) + .isEqualTo(expectedRawIdentifier); + + assertThat(node.asString()).isEqualTo("ML-DSA-" + expectedParameterSet); + + INode parameterSetIdentifier = node.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.asString()).isEqualTo(expectedParameterSet); + } + + private void assertCompositeParameterSet( + @Nonnull DetectionStore store, + @Nonnull INode node, + @Nonnull String expectedCompositeIdentifier) { + + DetectionStore + parameterIdentifierStore = + getStoreOfValueType(ParameterIdentifier.class, store.getChildren()); + assertThat(parameterIdentifierStore).isNotNull(); + assertThat(parameterIdentifierStore.getDetectionValues()).hasSize(1); + assertThat(parameterIdentifierStore.getDetectionValues().get(0).asString()) + .isEqualTo(expectedCompositeIdentifier); + + assertThat(node.asString()).isEqualTo("ML-DSA-" + expectedCompositeIdentifier); + + INode parameterSetIdentifier = node.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.asString()).isEqualTo(expectedCompositeIdentifier); + } + + private void assertGeneric(@Nonnull INode node) { + assertThat(node.asString()).isEqualTo("ML-DSA"); + assertThat(node.getChildren().get(ParameterSetIdentifier.class)).isNull(); + } +} 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 000000000..32c9027f1 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetMLKemTest.java @@ -0,0 +1,193 @@ +/* + * 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.ParameterIdentifier; +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.mapper.model.Oid; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.functionality.Decapsulate; +import com.ibm.mapper.model.functionality.Encapsulate; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all ML-KEM related detection rules (DotNetMLKem.java). + * + *

Covers the {@code MLKem} abstract base (all of its static factory methods), and the {@code + * MLKemCng}/{@code MLKemOpenSsl} native-interop derived classes, plus the {@code + * Encapsulate}/{@code Decapsulate} instance operations shared by all three. + * + *

Every top-level finding translates to a {@link com.ibm.mapper.model.algorithms.MLKEM} node + * (kind {@link KeyEncapsulationMechanism}). When the {@code MLKemAlgorithm} parameter set is + * captured, {@code node.asString()} is {@code "ML-KEM-512"}/{@code "ML-KEM-768"}/{@code + * "ML-KEM-1024"}, the node carries a {@link ParameterSetIdentifier} child ("512"/"768"/"1024") and + * — added by the enricher, confirmed from the actual debug output — an {@link Oid} child ({@code + * "2.16.840.1.101.3.4.4.1"}/{@code ".2"}/{@code ".3"} respectively). Otherwise {@code + * node.asString()} is the generic {@code "ML-KEM"}, with neither child. + * + *

Finding mapping (one finding per test method in DotNetMLKemTestFile.cs; verified against the + * actual {@code target/node-tree.log} output of a debug run, not guessed): + * + *

+ * Section 1 – algorithm-parameterized creation (findings 0–5):
+ *   0 TestGenerateKey512            → ML-KEM-512  (ParameterSetIdentifier "512", Oid ".4.1")
+ *   1 TestGenerateKey768            → ML-KEM-768  (ParameterSetIdentifier "768", Oid ".4.2")
+ *   2 TestGenerateKey1024           → ML-KEM-1024 (ParameterSetIdentifier "1024", Oid ".4.3")
+ *   3 TestImportDecapsulationKey    → ML-KEM-768
+ *   4 TestImportEncapsulationKey    → ML-KEM-768
+ *   5 TestImportPrivateSeed         → ML-KEM-512
+ *
+ * Section 2 – structural imports, no parameter set (findings 6–10):
+ *   6  TestImportPkcs8PrivateKey          → ML-KEM (generic)
+ *   7  TestImportSubjectPublicKeyInfo     → ML-KEM (generic)
+ *   8  TestImportFromPem                  → ML-KEM (generic)
+ *   9  TestImportEncryptedPkcs8PrivateKey → ML-KEM (generic)
+ *   10 TestImportFromEncryptedPem         → ML-KEM (generic)
+ *
+ * Section 3 – native-interop constructors, no parameter set (findings 11–12):
+ *   11 TestMLKemCng      → ML-KEM (generic)
+ *   12 TestMLKemOpenSsl  → ML-KEM (generic)
+ *
+ * Section 4 – Encapsulate / Decapsulate operations (findings 13–14):
+ *   13 TestEncapsulate → ML-KEM-768, Encapsulate child
+ *   14 TestDecapsulate → ML-KEM-768, Decapsulate child
+ *
+ * Section 5 – combined usage pattern (finding 15):
+ *   15 TestFullFlow → ML-KEM-1024, Encapsulate child, Decapsulate child
+ * 
+ */ +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) { + + // Every top-level finding must be ML-KEM + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("ML-KEM"); + + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(KeyEncapsulationMechanism.class); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: algorithm-parameterized creation + // ----------------------------------------------------------------- + case 0 -> assertParameterSet(detectionStore, node, "512", "2.16.840.1.101.3.4.4.1"); + case 1 -> assertParameterSet(detectionStore, node, "768", "2.16.840.1.101.3.4.4.2"); + case 2 -> assertParameterSet(detectionStore, node, "1024", "2.16.840.1.101.3.4.4.3"); + case 3 -> assertParameterSet(detectionStore, node, "768", "2.16.840.1.101.3.4.4.2"); + case 4 -> assertParameterSet(detectionStore, node, "768", "2.16.840.1.101.3.4.4.2"); + case 5 -> assertParameterSet(detectionStore, node, "512", "2.16.840.1.101.3.4.4.1"); + + // ----------------------------------------------------------------- + // Section 2 & 3: generic ML-KEM, no parameter set + // ----------------------------------------------------------------- + case 6, 7, 8, 9, 10, 11, 12 -> assertGeneric(node); + + // ----------------------------------------------------------------- + // Section 4: Encapsulate / Decapsulate operations + // ----------------------------------------------------------------- + case 13 -> { + assertParameterSet(detectionStore, node, "768", "2.16.840.1.101.3.4.4.2"); + assertThat(node.getChildren().get(Encapsulate.class)).isNotNull(); + } + case 14 -> { + assertParameterSet(detectionStore, node, "768", "2.16.840.1.101.3.4.4.2"); + assertThat(node.getChildren().get(Decapsulate.class)).isNotNull(); + } + + // ----------------------------------------------------------------- + // Section 5: combined usage pattern + // ----------------------------------------------------------------- + case 15 -> { + assertParameterSet(detectionStore, node, "1024", "2.16.840.1.101.3.4.4.3"); + assertThat(node.getChildren().get(Encapsulate.class)).isNotNull(); + assertThat(node.getChildren().get(Decapsulate.class)).isNotNull(); + } + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertParameterSet( + @Nonnull DetectionStore store, + @Nonnull INode node, + @Nonnull String expectedParameterSet, + @Nonnull String expectedOid) { + + DetectionStore + parameterIdentifierStore = + getStoreOfValueType(ParameterIdentifier.class, store.getChildren()); + assertThat(parameterIdentifierStore).isNotNull(); + assertThat(parameterIdentifierStore.getDetectionValues()).hasSize(1); + assertThat(parameterIdentifierStore.getDetectionValues().get(0).asString()) + .isEqualTo("MLKem" + expectedParameterSet); + + assertThat(node.asString()).isEqualTo("ML-KEM-" + expectedParameterSet); + + INode parameterSetIdentifier = node.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.asString()).isEqualTo(expectedParameterSet); + + INode oid = node.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.asString()).isEqualTo(expectedOid); + } + + private void assertGeneric(@Nonnull INode node) { + assertThat(node.asString()).isEqualTo("ML-KEM"); + assertThat(node.getChildren().get(ParameterSetIdentifier.class)).isNull(); + assertThat(node.getChildren().get(Oid.class)).isNull(); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetProtectedDataTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetProtectedDataTest.java new file mode 100644 index 000000000..752f61a79 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetProtectedDataTest.java @@ -0,0 +1,155 @@ +/* + * 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.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.Cipher; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all DPAPI detection rules ({@code DotNetProtectedData.java}). + * + *

Covers: + * + *

    + *
  • {@code ProtectedData.Protect}/{@code Unprotect}/{@code TryProtect}/{@code TryUnprotect} + *
  • {@code ProtectedMemory.Protect}/{@code Unprotect} + *
  • {@code DpapiDataProtector} constructor + instance {@code Protect(byte[])}/{@code + * Unprotect(byte[])} + *
+ * + *

Node strings below were captured from an actual test run's debug log ({@code + * target/node-tree.log} / stdout), not guessed: + * + *

+ * Finding mapping (one finding per test method in DotNetProtectedDataTestFile.cs):
+ *
+ * 0 TestProtectedDataProtect        → detected ValueAction "DPAPI_PROTECT"   → Cipher "DPAPI" + Encrypt "ENCRYPT" child
+ * 1 TestProtectedDataUnprotect      → detected ValueAction "DPAPI_UNPROTECT" → Cipher "DPAPI" + Decrypt "DECRYPT" child
+ * 2 TestProtectedDataTryProtect     → detected ValueAction "DPAPI_PROTECT"   → Cipher "DPAPI" + Encrypt "ENCRYPT" child
+ * 3 TestProtectedDataTryUnprotect   → detected ValueAction "DPAPI_UNPROTECT" → Cipher "DPAPI" + Decrypt "DECRYPT" child
+ * 4 TestProtectedMemoryProtect      → detected ValueAction "DPAPI_PROTECT"   → Cipher "DPAPI" + Encrypt "ENCRYPT" child
+ * 5 TestProtectedMemoryUnprotect    → detected ValueAction "DPAPI_UNPROTECT" → Cipher "DPAPI" + Decrypt "DECRYPT" child
+ * 6 TestDpapiDataProtectorProtect   → detected ValueAction "DPAPI" + child CipherAction ENCRYPT → Cipher "DPAPI" + Encrypt "ENCRYPT" child
+ * 7 TestDpapiDataProtectorUnprotect → detected ValueAction "DPAPI" + child CipherAction DECRYPT → Cipher "DPAPI" + Decrypt "DECRYPT" child
+ * 
+ */ +class DotNetProtectedDataTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetProtectedDataTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(Cipher.class); + assertThat(node.asString()).isEqualTo("DPAPI"); + + switch (findingId) { + // ----------------------------------------------------------------- + // Section 1 & 2: ProtectedData / ProtectedMemory static one-shot calls — identity + // and action are captured together as a single merged ValueAction. + // ----------------------------------------------------------------- + case 0, 2, 4 -> { + assertThat(primary.asString()).isEqualTo("DPAPI_PROTECT"); + INode encrypt = node.getChildren().get(Encrypt.class); + assertThat(encrypt).isNotNull(); + assertThat(encrypt.asString()).isEqualTo("ENCRYPT"); + } + case 1, 3, 5 -> { + assertThat(primary.asString()).isEqualTo("DPAPI_UNPROTECT"); + INode decrypt = node.getChildren().get(Decrypt.class); + assertThat(decrypt).isNotNull(); + assertThat(decrypt.asString()).isEqualTo("DECRYPT"); + } + + // ----------------------------------------------------------------- + // Section 3: DpapiDataProtector — real tracked instance, so identity ("DPAPI") is + // captured at the constructor and the action (CipherAction) is a depending-rule + // child of it. + // ----------------------------------------------------------------- + case 6 -> { + assertThat(primary.asString()).isEqualTo("DPAPI"); + assertCipherActionChild(detectionStore, node, CipherAction.Action.ENCRYPT); + INode encrypt = node.getChildren().get(Encrypt.class); + assertThat(encrypt).isNotNull(); + assertThat(encrypt.asString()).isEqualTo("ENCRYPT"); + } + case 7 -> { + assertThat(primary.asString()).isEqualTo("DPAPI"); + assertCipherActionChild(detectionStore, node, CipherAction.Action.DECRYPT); + INode decrypt = node.getChildren().get(Decrypt.class); + assertThat(decrypt).isNotNull(); + assertThat(decrypt.asString()).isEqualTo("DECRYPT"); + } + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertCipherActionChild( + @Nonnull DetectionStore store, + @Nonnull INode node, + @Nonnull CipherAction.Action expectedAction) { + + DetectionStore childStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(childStore).isNotNull(); + assertThat(childStore.getDetectionValues()).hasSize(1); + IValue childValue = childStore.getDetectionValues().get(0); + assertThat(childValue).isInstanceOf(CipherAction.class); + assertThat(((CipherAction) childValue).getAction()).isEqualTo(expectedAction); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRC2ComprehensiveTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRC2ComprehensiveTest.java new file mode 100644 index 000000000..224e2bfaf --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRC2ComprehensiveTest.java @@ -0,0 +1,417 @@ +/* + * 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.BlockSize; +import com.ibm.engine.model.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.Mode; +import com.ibm.engine.model.Padding; +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.KeyLength; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.Generate; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all RC2-related detection rules (DotNetRC2.java). + * + *

Covers both RC2-related classes and their complete API surface: + * + *

    + *
  • RC2 (abstract base) + *
  • RC2CryptoServiceProvider (derived from RC2) + *
+ * + *

Like DES, RC2 has no CNG-backed subclass and no AEAD variant, so there is no equivalent to + * Section 8/9 of DotNetAESComprehensiveTest (AesGcm/AesCcm). Unlike DES, RC2 additionally exposes + * an {@code EffectiveKeySize} property, which is reused as a {@code KeySize} detection (see + * DotNetRC2.java) and is covered by an extra test method in Section 2 (property setters). + * + *

Finding mapping (one finding per test method in DotNetRC2ComprehensiveTestFile.cs): + * + *

+ * Section 1 – factory methods / constructors (findings 0–2):
+ *   0  TestRc2Create                  → RC2
+ *   1  TestRc2CreateNamed             → RC2
+ *   2  TestRc2Csp                     → RC2
+ *
+ * Section 2 – property setters (findings 3–16):
+ *   3  TestPropertyModeCBC            → RC2-CBC
+ *   4  TestPropertyModeECB            → RC2-ECB
+ *   5  TestPropertyModeCFB            → RC2-CFB
+ *   6  TestPropertyModeOFB            → RC2-OFB
+ *   7  TestPropertyModeCTS            → RC2-CTS
+ *   8  TestPropertyKeySize            → RC2-128
+ *   9  TestPropertyEffectiveKeySize   → RC2-64 (reused as KeySize, see DotNetRC2.java)
+ *   10 TestPropertyPaddingPKCS7       → RC2 (padding never rendered in asString)
+ *   11 TestPropertyPaddingNone        → RC2 (padding never rendered in asString)
+ *   12 TestPropertyPaddingZeros       → RC2 (padding never rendered in asString)
+ *   13 TestPropertyPaddingANSIX923    → RC2 (padding never rendered in asString)
+ *   14 TestPropertyFeedbackSize       → RC2 (BlockSize never rendered in asString)
+ *   15 TestPropertyIV                 → RC2 (no IV rule)
+ *   16 TestPropertyKey                → RC2 (no Key rule)
+ *
+ * Section 3 – CreateEncryptor/CreateDecryptor (findings 17–20):
+ *   17 TestCreateEncryptorNoArgs      → RC2 + Encrypt
+ *   18 TestCreateEncryptorWithArgs    → RC2 + Encrypt
+ *   19 TestCreateDecryptorNoArgs      → RC2 + Decrypt
+ *   20 TestCreateDecryptorWithArgs    → RC2 + Decrypt
+ *
+ * Section 4 – direct encrypt (findings 21–23):
+ *   21 TestEncryptCbc                 → RC2-CBC
+ *   22 TestEncryptEcb                 → RC2-ECB
+ *   23 TestEncryptCfb                 → RC2-CFB
+ *
+ * Section 5 – direct decrypt (findings 24–26):
+ *   24 TestDecryptCbc                 → RC2-CBC
+ *   25 TestDecryptEcb                 → RC2-ECB
+ *   26 TestDecryptCfb                 → RC2-CFB
+ *
+ * Section 6 – Try* variants (findings 27–32):
+ *   27 TestTryEncryptCbc              → RC2-CBC
+ *   28 TestTryDecryptCbc              → RC2-CBC
+ *   29 TestTryEncryptEcb              → RC2-ECB
+ *   30 TestTryDecryptEcb              → RC2-ECB
+ *   31 TestTryEncryptCfb              → RC2-CFB
+ *   32 TestTryDecryptCfb              → RC2-CFB
+ *
+ * Section 7 – key/IV generation (findings 33–34):
+ *   33 TestGenerateKey                → RC2 + KeyGeneration
+ *   34 TestGenerateIV                 → RC2 + Generate
+ *
+ * Section 8 – combined usage patterns (findings 35–41):
+ *   35 TestRc2CbcFullFlow             → RC2-CBC + Encrypt
+ *   36 TestRc2CspEncryptCbc           → RC2-CBC
+ *   37 TestRc2CspDecryptCbc           → RC2-CBC
+ *   38 TestRc2CfbFeedback             → RC2-CFB
+ *   39 TestRc2CbcWithEncryptorOverload → RC2 + Encrypt
+ *   40 TestRc2EcbEncrypt              → RC2-ECB
+ *   41 TestRc2CspEffectiveKeySize     → RC2-40
+ * 
+ * + *

NOTE: unlike DES, {@code RC2}'s abstract-base constructor {@code RC2(DetectionLocation)} does + * not seed a default {@code KeyLength} or {@code BlockSize} (see {@code RC2.java}) — + * {@code RC2.asString()} uses {@code composeName(true, true, false)} just like {@code DES}, but + * with nothing to render unless a KeySize/EffectiveKeySize/Mode is actually detected. So the plain + * "RC2" string appears whenever no such property was set, and Padding never contributes to the + * string even when a Padding value is detected as a child. Verified against actual test-run debug + * output ({@code target/node-tree.log}) rather than guessed. + */ +class DotNetRC2ComprehensiveTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetRC2ComprehensiveTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + // Every top-level finding must be RC2 + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("RC2"); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: simple constructors — only RC2, no children fired + // ----------------------------------------------------------------- + case 0, 1, 2 -> { + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("RC2"); + } + + // ----------------------------------------------------------------- + // Section 2a: property Mode setters + // ----------------------------------------------------------------- + case 3 -> assertModeFindings(detectionStore, nodes, "CBC", "RC2-CBC"); + case 4 -> assertModeFindings(detectionStore, nodes, "ECB", "RC2-ECB"); + case 5 -> assertModeFindings(detectionStore, nodes, "CFB", "RC2-CFB"); + case 6 -> assertModeFindings(detectionStore, nodes, "OFB", "RC2-OFB"); + case 7 -> assertModeFindings(detectionStore, nodes, "CTS", "RC2-CTS"); + + // ----------------------------------------------------------------- + // Section 2b: property KeySize / EffectiveKeySize setters — both + // reuse KeySizeFactory (see DotNetRC2.java), so both render as + // - in the node string via composeName's KeyLength branch. + // ----------------------------------------------------------------- + case 8 -> assertKeySizeFindings(detectionStore, nodes, "128", "RC2-128"); + case 9 -> assertKeySizeFindings(detectionStore, nodes, "64", "RC2-64"); + + // ----------------------------------------------------------------- + // Section 2c: property Padding setters — Padding is tracked as a + // child node but RC2.asString() uses composeName(true, true, false), + // so padding never contributes to the rendered string. + // ----------------------------------------------------------------- + case 10 -> assertPaddingFindings(detectionStore, nodes, "PKCS7", "RC2"); + case 11 -> assertPaddingFindings(detectionStore, nodes, "None", "RC2"); + case 12 -> assertPaddingFindings(detectionStore, nodes, "Zeros", "RC2"); + case 13 -> assertPaddingFindings(detectionStore, nodes, "ANSIX923", "RC2"); + + // ----------------------------------------------------------------- + // Section 2d: FeedbackSize setter — BlockSize(8) detected but never + // contributes to asString() (composeName has no BlockSize branch) + // ----------------------------------------------------------------- + case 14 -> { + DetectionStore fbStore = + getStoreOfValueType(BlockSize.class, detectionStore.getChildren()); + assertThat(fbStore).isNotNull(); + assertThat(fbStore.getDetectionValues()).hasSize(1); + assertThat(fbStore.getDetectionValues().get(0).asString()).isEqualTo("8"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("RC2"); + } + + // ----------------------------------------------------------------- + // Section 2e: IV and Key setters — no detection rules for these + // ----------------------------------------------------------------- + case 15, 16 -> { + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("RC2"); + } + + // ----------------------------------------------------------------- + // Section 3: CreateEncryptor / CreateDecryptor + // ----------------------------------------------------------------- + case 17, 18 -> assertEncryptFindings(detectionStore, nodes, "RC2"); + case 19, 20 -> assertDecryptFindings(detectionStore, nodes, "RC2"); + + // ----------------------------------------------------------------- + // Section 4: direct mode-specific encrypt + // ----------------------------------------------------------------- + case 21 -> assertModePaddingFindings(detectionStore, nodes, "CBC", "PKCS7", "RC2-CBC"); + case 22 -> assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "RC2-ECB"); + case 23 -> assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "RC2-CFB"); + + // ----------------------------------------------------------------- + // Section 5: direct mode-specific decrypt + // ----------------------------------------------------------------- + case 24 -> assertModePaddingFindings(detectionStore, nodes, "CBC", "PKCS7", "RC2-CBC"); + case 25 -> assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "RC2-ECB"); + case 26 -> assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "RC2-CFB"); + + // ----------------------------------------------------------------- + // Section 6: Try* variants + // ----------------------------------------------------------------- + case 27, 28 -> + assertModePaddingFindings(detectionStore, nodes, "CBC", "PKCS7", "RC2-CBC"); + case 29, 30 -> + assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "RC2-ECB"); + case 31, 32 -> + assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "RC2-CFB"); + + // ----------------------------------------------------------------- + // Section 7: GenerateKey / GenerateIV + // ----------------------------------------------------------------- + case 33 -> { + // rc2.GenerateKey() → KeyGeneration functionality node + DetectionStore + genKeyStore = + getStoreOfValueType( + ValueAction.class, detectionStore.getChildren()); + assertThat(genKeyStore).isNotNull(); + assertThat(genKeyStore.getDetectionValues().get(0).asString()) + .isEqualTo("GenerateKey"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).getChildren().get(KeyGeneration.class)).isNotNull(); + } + case 34 -> { + // rc2.GenerateIV() → Generate functionality node + DetectionStore + genIvStore = + getStoreOfValueType( + ValueAction.class, detectionStore.getChildren()); + assertThat(genIvStore).isNotNull(); + assertThat(genIvStore.getDetectionValues().get(0).asString()) + .isEqualTo("GenerateIV"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).getChildren().get(Generate.class)).isNotNull(); + } + + // ----------------------------------------------------------------- + // Section 8: combined usage patterns + // ----------------------------------------------------------------- + case 35 -> { + // TestRc2CbcFullFlow: Mode=CBC, Padding=PKCS7 (unrendered), CreateEncryptor + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(BlockCipher.class); + assertThat(node.asString()).isEqualTo("RC2-CBC"); + assertThat(node.getChildren().get(com.ibm.mapper.model.Mode.class)).isNotNull(); + assertThat(node.getChildren().get(Encrypt.class)).isNotNull(); + } + case 36 -> assertModePaddingFindings(detectionStore, nodes, "CBC", "PKCS7", "RC2-CBC"); + case 37 -> assertModePaddingFindings(detectionStore, nodes, "CBC", "PKCS7", "RC2-CBC"); + case 38 -> assertModePaddingFindings(detectionStore, nodes, "CFB", "None", "RC2-CFB"); + case 39 -> assertEncryptFindings(detectionStore, nodes, "RC2"); + case 40 -> assertModePaddingFindings(detectionStore, nodes, "ECB", "None", "RC2-ECB"); + case 41 -> assertKeySizeFindings(detectionStore, nodes, "40", "RC2-40"); + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertModeFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedMode, + @Nonnull String expectedNodeString) { + + DetectionStore modeStore = + getStoreOfValueType(Mode.class, store.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues()).hasSize(1); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo(expectedMode); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(com.ibm.mapper.model.Mode.class)).isNotNull(); + } + + private void assertKeySizeFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedKeySize, + @Nonnull String expectedNodeString) { + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, store.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues()).hasSize(1); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo(expectedKeySize); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(KeyLength.class)).isNotNull(); + assertThat(nodes.get(0).getChildren().get(KeyLength.class).asString()) + .isEqualTo(expectedKeySize); + } + + private void assertPaddingFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedPadding, + @Nonnull String expectedNodeString) { + + DetectionStore paddingStore = + getStoreOfValueType(Padding.class, store.getChildren()); + assertThat(paddingStore).isNotNull(); + assertThat(paddingStore.getDetectionValues()).hasSize(1); + assertThat(paddingStore.getDetectionValues().get(0).asString()).isEqualTo(expectedPadding); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + } + + private void assertEncryptFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedNodeString) { + + DetectionStore encryptStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(encryptStore).isNotNull(); + assertThat(encryptStore.getDetectionValues()).hasSize(1); + assertThat(encryptStore.getDetectionValues().get(0).asString()).isEqualTo("ENCRYPT"); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(Encrypt.class)).isNotNull(); + } + + private void assertDecryptFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedNodeString) { + + DetectionStore decryptStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(decryptStore).isNotNull(); + assertThat(decryptStore.getDetectionValues()).hasSize(1); + assertThat(decryptStore.getDetectionValues().get(0).asString()).isEqualTo("DECRYPT"); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(Decrypt.class)).isNotNull(); + } + + /** + * Asserts mode+padding findings using the translated node tree. Direct-mode methods + * (EncryptCbc, TryDecryptEcb, etc.) place Mode and Padding in the same child detection store, + * so we validate via the final node string rather than per-store inspection. + */ + private void assertModePaddingFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedMode, + @Nonnull String expectedPadding, + @Nonnull String expectedNodeString) { + + DetectionStore modeStore = + getStoreOfValueType(Mode.class, store.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues()) + .anySatisfy(v -> assertThat(v.asString()).isEqualTo(expectedMode)); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRSATest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRSATest.java index 5336bc3e1..c623bce63 100755 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRSATest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRSATest.java @@ -26,18 +26,71 @@ 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.CipherAction; import com.ibm.engine.model.IValue; +import com.ibm.engine.model.SignatureAction; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.KeyContext; import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyLength; import com.ibm.mapper.model.Oid; import com.ibm.mapper.model.PublicKeyEncryption; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.mapper.model.functionality.Verify; import com.ibm.plugin.CSharpVerifier; import com.ibm.plugin.TestBase; import java.util.List; import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; +/** + * Comprehensive test for all RSA-related detection rules (DotNetRSA.java). + * + *

Covers all four RSA-related classes and their complete operational API surface: + * + *

    + *
  • RSA (abstract base) + *
  • RSACng, RSACryptoServiceProvider, RSAOpenSsl (derived from RSA) + *
+ * + *

Finding mapping (one finding per test method in DotNetRSATestFile.cs): + * + *

+ * Section 1 – factory methods / constructors (findings 0–4):
+ *   0 TestRsaCreate              → RSA
+ *   1 TestRsaCreateWithKeySize   → RSA
+ *   2 TestRsaCsp                 → RSA
+ *   3 TestRsaCng                 → RSA
+ *   4 TestRsaOpenSsl             → RSA
+ *
+ * Section 2 – property KeySize setters (findings 5–6):
+ *   5 TestPropertyKeySize2048    → RSA-2048
+ *   6 TestPropertyKeySize4096    → RSA-4096
+ *
+ * Section 3 – Encrypt / Decrypt (findings 7–10):
+ *   7  TestEncrypt      → RSA + Encrypt
+ *   8  TestDecrypt      → RSA + Decrypt
+ *   9  TestTryEncrypt   → RSA + Encrypt
+ *   10 TestTryDecrypt   → RSA + Decrypt
+ *
+ * Section 4 – SignData / TrySignData / VerifyData (findings 11–13):
+ *   11 TestSignData     → RSA + Sign
+ *   12 TestTrySignData  → RSA + Sign
+ *   13 TestVerifyData   → RSA + Verify
+ *
+ * Section 5 – SignHash / TrySignHash / VerifyHash (findings 14–16):
+ *   14 TestSignHash     → RSA + Sign
+ *   15 TestTrySignHash  → RSA + Sign
+ *   16 TestVerifyHash   → RSA + Verify
+ *
+ * Section 6 – combined usage patterns (findings 17–19):
+ *   17 TestRsaCngFullFlow        → RSA-3072 + Encrypt
+ *   18 TestRsaCspSignFlow        → RSA + Sign
+ *   19 TestRsaOpenSslVerifyFlow  → RSA + Verify
+ * 
+ */ class DotNetRSATest extends TestBase { @Test @@ -52,24 +105,136 @@ public void asserts( DetectionStore detectionStore, @Nonnull List nodes) { - assertThat(detectionStore).isNotNull(); - assertThat(detectionStore.getDetectionValues()).hasSize(1); + + // Every top-level finding must be RSA assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(ValueAction.class); - assertThat(value0.asString()).isEqualTo("RSA"); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("RSA"); - /* - * Translation - */ assertThat(nodes).hasSize(1); INode node = nodes.get(0); assertThat(node.getKind()).isEqualTo(PublicKeyEncryption.class); - assertThat(node.asString()).isEqualTo("RSA"); - // RSA carries its OID as a child node + // RSA always carries its OID as a child node INode oid = node.getChildren().get(Oid.class); assertThat(oid).isNotNull(); assertThat(oid.asString()).isEqualTo("1.2.840.113549.1.1.1"); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: simple constructors — only RSA, no children fired + // ----------------------------------------------------------------- + case 0, 1, 2, 3, 4 -> assertThat(node.asString()).isEqualTo("RSA"); + + // ----------------------------------------------------------------- + // Section 2: property KeySize setters + // ----------------------------------------------------------------- + case 5 -> assertKeySize(detectionStore, node, "2048"); + case 6 -> assertKeySize(detectionStore, node, "4096"); + + // ----------------------------------------------------------------- + // Section 3: Encrypt / Decrypt / TryEncrypt / TryDecrypt + // ----------------------------------------------------------------- + case 7, 9 -> assertEncrypt(detectionStore, node, "RSA"); + case 8, 10 -> assertDecrypt(detectionStore, node, "RSA"); + + // ----------------------------------------------------------------- + // Section 4: SignData / TrySignData / VerifyData + // ----------------------------------------------------------------- + case 11, 12 -> assertSign(detectionStore, node, "RSA"); + case 13 -> assertVerify(detectionStore, node, "RSA"); + + // ----------------------------------------------------------------- + // Section 5: SignHash / TrySignHash / VerifyHash + // ----------------------------------------------------------------- + case 14, 15 -> assertSign(detectionStore, node, "RSA"); + case 16 -> assertVerify(detectionStore, node, "RSA"); + + // ----------------------------------------------------------------- + // Section 6: combined usage patterns + // ----------------------------------------------------------------- + case 17 -> assertEncrypt(detectionStore, node, "RSA-3072"); + case 18 -> assertSign(detectionStore, node, "RSA"); + case 19 -> assertVerify(detectionStore, node, "RSA"); + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertKeySize( + @Nonnull DetectionStore store, + @Nonnull INode node, + @Nonnull String expectedKeySize) { + + assertThat(node.asString()).isEqualTo("RSA-" + expectedKeySize); + assertThat(node.getChildren().get(KeyLength.class)).isNotNull(); + assertThat(node.getChildren().get(KeyLength.class).asString()).isEqualTo(expectedKeySize); + } + + private void assertEncrypt( + @Nonnull DetectionStore store, + @Nonnull INode node, + @Nonnull String expectedNodeString) { + + DetectionStore encryptStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(encryptStore).isNotNull(); + assertThat(encryptStore.getDetectionValues()).hasSize(1); + assertThat(encryptStore.getDetectionValues().get(0).asString()).isEqualTo("ENCRYPT"); + + assertThat(node.asString()).isEqualTo(expectedNodeString); + assertThat(node.getChildren().get(Encrypt.class)).isNotNull(); + } + + private void assertDecrypt( + @Nonnull DetectionStore store, + @Nonnull INode node, + @Nonnull String expectedNodeString) { + + DetectionStore decryptStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(decryptStore).isNotNull(); + assertThat(decryptStore.getDetectionValues()).hasSize(1); + assertThat(decryptStore.getDetectionValues().get(0).asString()).isEqualTo("DECRYPT"); + + assertThat(node.asString()).isEqualTo(expectedNodeString); + assertThat(node.getChildren().get(Decrypt.class)).isNotNull(); + } + + private void assertSign( + @Nonnull DetectionStore store, + @Nonnull INode node, + @Nonnull String expectedNodeString) { + + DetectionStore signStore = + getStoreOfValueType(SignatureAction.class, store.getChildren()); + assertThat(signStore).isNotNull(); + assertThat(signStore.getDetectionValues()).hasSize(1); + assertThat(signStore.getDetectionValues().get(0).asString()).isEqualTo("SIGN"); + + assertThat(node.asString()).isEqualTo(expectedNodeString); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + + private void assertVerify( + @Nonnull DetectionStore store, + @Nonnull INode node, + @Nonnull String expectedNodeString) { + + DetectionStore verifyStore = + getStoreOfValueType(SignatureAction.class, store.getChildren()); + assertThat(verifyStore).isNotNull(); + assertThat(verifyStore.getDetectionValues()).hasSize(1); + assertThat(verifyStore.getDetectionValues().get(0).asString()).isEqualTo("VERIFY"); + + assertThat(node.asString()).isEqualTo(expectedNodeString); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); } } diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGeneratorTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGeneratorTest.java new file mode 100644 index 000000000..4a7a86c60 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGeneratorTest.java @@ -0,0 +1,144 @@ +/* + * 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.PRNGContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.PseudorandomNumberGenerator; +import com.ibm.mapper.model.functionality.Generate; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all {@code RandomNumberGenerator} / {@code RNGCryptoServiceProvider} + * detection rules ({@code DotNetRandomNumberGenerator.java}). + * + *

Covers: + * + *

    + *
  • {@code RandomNumberGenerator.Create()} / {@code Create(string)} + instance {@code + * GetBytes}/{@code GetNonZeroBytes} + *
  • {@code RandomNumberGenerator}'s static-only methods: {@code Fill}, {@code GetBytes(int)}, + * {@code GetHexString}, {@code GetInt32} (both overloads), {@code GetItems}, {@code + * GetNonZeroBytes(Span)}, {@code GetString}, {@code Shuffle} + *
  • {@code RNGCryptoServiceProvider} constructor overloads + instance {@code GetBytes}/{@code + * GetNonZeroBytes} + *
+ * + *

Node strings below were captured from an actual test run's debug log ({@code + * target/node-tree.log}), not guessed: + * + *

+ * Finding mapping (one finding per test method in DotNetRandomNumberGeneratorTestFile.cs):
+ *
+ *  0 TestCreateAndGetBytes         → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child
+ *  1 TestCreateAndGetNonZeroBytes  → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child
+ *  2 TestCreateNamed               → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child
+ *  3 TestStaticFill                → PseudorandomNumberGenerator "NATIVEPRNG" (no children)
+ *  4 TestStaticGetBytesCount       → PseudorandomNumberGenerator "NATIVEPRNG" (no children)
+ *  5 TestStaticGetHexString        → PseudorandomNumberGenerator "NATIVEPRNG" (no children)
+ *  6 TestStaticGetInt32            → PseudorandomNumberGenerator "NATIVEPRNG" (no children)
+ *  7 TestStaticGetInt32Range       → PseudorandomNumberGenerator "NATIVEPRNG" (no children)
+ *  8 TestStaticGetItems            → PseudorandomNumberGenerator "NATIVEPRNG" (no children)
+ *  9 TestStaticGetNonZeroBytes     → PseudorandomNumberGenerator "NATIVEPRNG" (no children)
+ * 10 TestStaticGetString           → PseudorandomNumberGenerator "NATIVEPRNG" (no children)
+ * 11 TestStaticShuffle             → PseudorandomNumberGenerator "NATIVEPRNG" (no children)
+ * 12 TestRngCspGetBytes            → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child
+ * 13 TestRngCspGetNonZeroBytes     → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child
+ * 14 TestRngCspWithSeed            → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child
+ * 
+ */ +class DotNetRandomNumberGeneratorTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify( + "rules/detection/dotnet/DotNetRandomNumberGeneratorTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(PRNGContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("NATIVEPRNG"); + + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(PseudorandomNumberGenerator.class); + assertThat(node.asString()).isEqualTo("NATIVEPRNG"); + + switch (findingId) { + // ----------------------------------------------------------------- + // Section 1: RandomNumberGenerator.Create()/Create(string) + instance operations + // ----------------------------------------------------------------- + case 0, 1, 2 -> assertGenerateChild(detectionStore, node); + + // ----------------------------------------------------------------- + // Section 2: RandomNumberGenerator static-only methods — self-contained, + // no depending rules, no children. + // ----------------------------------------------------------------- + case 3, 4, 5, 6, 7, 8, 9, 10, 11 -> assertThat(node.getChildren()).isEmpty(); + + // ----------------------------------------------------------------- + // Section 3: RNGCryptoServiceProvider + // ----------------------------------------------------------------- + case 12, 13, 14 -> assertGenerateChild(detectionStore, node); + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertGenerateChild( + @Nonnull DetectionStore store, + @Nonnull INode node) { + + DetectionStore childStore = + getStoreOfValueType(ValueAction.class, store.getChildren()); + assertThat(childStore).isNotNull(); + assertThat(childStore.getDetectionValues()).hasSize(1); + + assertThat(node.getChildren().get(Generate.class)).isNotNull(); + assertThat(node.getChildren().get(Generate.class).asString()).isEqualTo("GENERATE"); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA3Test.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA3Test.java new file mode 100644 index 000000000..bc4cb7204 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHA3Test.java @@ -0,0 +1,127 @@ +/* + * 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.DigestContext; +import com.ibm.mapper.model.DigestSize; +import com.ibm.mapper.model.ExtendableOutputFunction; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link DotNetSHA3}, covering: + * + *
    + *
  • findingId 0 → {@code TestSha3_256Create} → {@code SHA3-256} + *
  • findingId 1 → {@code TestSha3_384Create} → {@code SHA3-384} + *
  • findingId 2 → {@code TestSha3_512Create} → {@code SHA3-512} + *
  • findingId 3 → {@code TestShake128} → {@code SHAKE128} + *
  • findingId 4 → {@code TestShake256} → {@code SHAKE256} + *
+ */ +class DotNetSHA3Test extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetSHA3TestFile.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(DigestContext.class); + IValue value0 = detectionStore.getDetectionValues().get(0); + assertThat(value0).isInstanceOf(ValueAction.class); + + /* + * Translation + */ + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + + switch (findingId) { + case 0 -> { + assertThat(value0.asString()).isEqualTo("SHA3_256"); + assertThat(node.getKind()).isEqualTo(MessageDigest.class); + assertThat(node.asString()).isEqualTo("SHA3-256"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("256"); + } + case 1 -> { + assertThat(value0.asString()).isEqualTo("SHA3_384"); + assertThat(node.getKind()).isEqualTo(MessageDigest.class); + assertThat(node.asString()).isEqualTo("SHA3-384"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("384"); + } + case 2 -> { + assertThat(value0.asString()).isEqualTo("SHA3_512"); + assertThat(node.getKind()).isEqualTo(MessageDigest.class); + assertThat(node.asString()).isEqualTo("SHA3-512"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("512"); + } + case 3 -> { + assertThat(value0.asString()).isEqualTo("Shake128"); + assertThat(node.getKind()).isEqualTo(ExtendableOutputFunction.class); + assertThat(node.asString()).isEqualTo("SHAKE128"); + INode parameterSetIdentifier = node.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.asString()).isEqualTo("128"); + } + case 4 -> { + assertThat(value0.asString()).isEqualTo("Shake256"); + assertThat(node.getKind()).isEqualTo(ExtendableOutputFunction.class); + assertThat(node.asString()).isEqualTo("SHAKE256"); + INode parameterSetIdentifier = node.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.asString()).isEqualTo("256"); + } + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHATest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHATest.java index 7f787d40d..ce2e05530 100755 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHATest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHATest.java @@ -104,6 +104,91 @@ public void asserts( assertThat(digestSize).isNotNull(); assertThat(digestSize.asString()).isEqualTo("128"); } + // MD5.Create("MD5") / SHA*.Create("SHA*") named-factory overloads: same detected + // value and translation as their no-arg Create() counterparts (cases 0-4). + case 5 -> { + assertThat(value0.asString()).isEqualTo("MD5"); + assertThat(node.asString()).isEqualTo("MD5"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("128"); + } + case 6 -> { + assertThat(value0.asString()).isEqualTo("SHA1"); + assertThat(node.asString()).isEqualTo("SHA-1"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("160"); + } + case 7 -> { + assertThat(value0.asString()).isEqualTo("SHA256"); + assertThat(node.asString()).isEqualTo("SHA-256"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("256"); + } + case 8 -> { + assertThat(value0.asString()).isEqualTo("SHA384"); + assertThat(node.asString()).isEqualTo("SHA-384"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("384"); + } + case 9 -> { + assertThat(value0.asString()).isEqualTo("SHA512"); + assertThat(node.asString()).isEqualTo("SHA-512"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("512"); + } + // new MD5Cng(): CNG-backed implementation, same translation as MD5.Create() (case 4). + case 10 -> { + assertThat(value0.asString()).isEqualTo("MD5"); + assertThat(node.asString()).isEqualTo("MD5"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("128"); + } + // new SHA1Cng() / new SHA1CryptoServiceProvider(): same translation as SHA1.Create(). + case 11, 12 -> { + assertThat(value0.asString()).isEqualTo("SHA1"); + assertThat(node.asString()).isEqualTo("SHA-1"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("160"); + } + // new SHA256Cng() / new SHA256CryptoServiceProvider(): same as SHA256.Create(). + case 13, 14 -> { + assertThat(value0.asString()).isEqualTo("SHA256"); + assertThat(node.asString()).isEqualTo("SHA-256"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("256"); + } + // new SHA384Cng() / new SHA384CryptoServiceProvider(): same as SHA384.Create(). + case 15, 16 -> { + assertThat(value0.asString()).isEqualTo("SHA384"); + assertThat(node.asString()).isEqualTo("SHA-384"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("384"); + } + // new SHA512Cng() / new SHA512CryptoServiceProvider(): same as SHA512.Create(). + case 17, 18 -> { + assertThat(value0.asString()).isEqualTo("SHA512"); + assertThat(node.asString()).isEqualTo("SHA-512"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("512"); + } + // RIPEMD160.Create() / RIPEMD160.Create("RIPEMD160") / new RIPEMD160Managed() + case 19, 20, 21 -> { + assertThat(value0.asString()).isEqualTo("RIPEMD160"); + assertThat(node.asString()).isEqualTo("RIPEMD-160"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("160"); + } default -> throw new IllegalStateException("Unexpected findingId: " + findingId); } } diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSlhDsaTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSlhDsaTest.java new file mode 100644 index 000000000..7147de276 --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSlhDsaTest.java @@ -0,0 +1,192 @@ +/* + * 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.ParameterIdentifier; +import com.ibm.engine.model.ValueAction; +import com.ibm.engine.model.context.KeyContext; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.ParameterSetIdentifier; +import com.ibm.mapper.model.Signature; +import com.ibm.mapper.model.functionality.Sign; +import com.ibm.mapper.model.functionality.Verify; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all SLH-DSA related detection rules (DotNetSlhDsa.java). + * + *

Covers the {@code SlhDsa} abstract base (all of its static factory methods), the {@code + * SlhDsaCng}/{@code SlhDsaOpenSsl} native-interop derived classes, and the {@code SignData}/{@code + * VerifyData}/{@code SignPreHash}/{@code VerifyPreHash} instance operations (no {@code SignMu}/ + * {@code VerifyMu} — confirmed absent from the official {@code SlhDsa} reference, see {@code + * DotNetSlhDsa}'s class javadoc). + * + *

Every top-level finding translates to a {@link com.ibm.mapper.model.algorithms.SPHINCSPlus} + * node (kind {@link Signature}, {@code NAME = "SLH-DSA"}). When the {@code SlhDsaAlgorithm} + * parameter set is captured, {@code node.asString()} is e.g. {@code "SLH-DSA-SHA2-128s"} and the + * node carries a {@link ParameterSetIdentifier} child. + * + *

Finding mapping (one finding per test method in DotNetSlhDsaTestFile.cs; verified against the + * actual {@code target/node-tree.log} output of a debug run, not guessed): + * + *

+ * Section 1 – SlhDsa algorithm-parameterized creation (findings 0–3):
+ *   0 TestGenerateKeySha2_128s      → SLH-DSA-SHA2-128s
+ *   1 TestGenerateKeyShake256f      → SLH-DSA-SHAKE-256f
+ *   2 TestImportSlhDsaPrivateKey    → SLH-DSA-SHA2-192f
+ *   3 TestImportSlhDsaPublicKey     → SLH-DSA-SHAKE-128s
+ *
+ * Section 2 – SlhDsa structural imports, no parameter set (findings 4–8):
+ *   4 TestImportPkcs8PrivateKey          → SLH-DSA (generic)
+ *   5 TestImportSubjectPublicKeyInfo     → SLH-DSA (generic)
+ *   6 TestImportFromPem                  → SLH-DSA (generic)
+ *   7 TestImportEncryptedPkcs8PrivateKey → SLH-DSA (generic)
+ *   8 TestImportFromEncryptedPem         → SLH-DSA (generic)
+ *
+ * Section 3 – SlhDsa native-interop constructors, no parameter set (findings 9–10):
+ *   9  TestSlhDsaCng      → SLH-DSA (generic)
+ *   10 TestSlhDsaOpenSsl  → SLH-DSA (generic)
+ *
+ * Section 4 – SlhDsa Sign / Verify operations (findings 11–14):
+ *   11 TestSignData      → SLH-DSA-SHA2-128s, Sign child
+ *   12 TestVerifyData    → SLH-DSA-SHA2-128s, Verify child
+ *   13 TestSignPreHash   → SLH-DSA-SHA2-128s, Sign child
+ *   14 TestVerifyPreHash → SLH-DSA-SHA2-128s, Verify child
+ *
+ * Section 5 – combined usage pattern (finding 15):
+ *   15 TestFullFlow → SLH-DSA-SHAKE-256f, Sign child, Verify child
+ * 
+ */ +class DotNetSlhDsaTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetSlhDsaTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + // Every top-level finding must be SLH-DSA + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("SLH-DSA"); + + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(Signature.class); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: SlhDsa algorithm-parameterized creation + // ----------------------------------------------------------------- + case 0 -> assertParameterSet(detectionStore, node, "SlhDsaSha2_128s", "SHA2-128s"); + case 1 -> assertParameterSet(detectionStore, node, "SlhDsaShake256f", "SHAKE-256f"); + case 2 -> assertParameterSet(detectionStore, node, "SlhDsaSha2_192f", "SHA2-192f"); + case 3 -> assertParameterSet(detectionStore, node, "SlhDsaShake128s", "SHAKE-128s"); + + // ----------------------------------------------------------------- + // Section 2 & 3: generic SLH-DSA, no parameter set + // ----------------------------------------------------------------- + case 4, 5, 6, 7, 8, 9, 10 -> assertGeneric(node); + + // ----------------------------------------------------------------- + // Section 4: SlhDsa Sign / Verify operations + // ----------------------------------------------------------------- + case 11 -> { + assertParameterSet(detectionStore, node, "SlhDsaSha2_128s", "SHA2-128s"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + case 12 -> { + assertParameterSet(detectionStore, node, "SlhDsaSha2_128s", "SHA2-128s"); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); + } + case 13 -> { + assertParameterSet(detectionStore, node, "SlhDsaSha2_128s", "SHA2-128s"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + } + case 14 -> { + assertParameterSet(detectionStore, node, "SlhDsaSha2_128s", "SHA2-128s"); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); + } + + // ----------------------------------------------------------------- + // Section 5: combined usage pattern + // ----------------------------------------------------------------- + case 15 -> { + assertParameterSet(detectionStore, node, "SlhDsaShake256f", "SHAKE-256f"); + assertThat(node.getChildren().get(Sign.class)).isNotNull(); + assertThat(node.getChildren().get(Verify.class)).isNotNull(); + } + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertParameterSet( + @Nonnull DetectionStore store, + @Nonnull INode node, + @Nonnull String expectedRawIdentifier, + @Nonnull String expectedParameterSet) { + + DetectionStore + parameterIdentifierStore = + getStoreOfValueType(ParameterIdentifier.class, store.getChildren()); + assertThat(parameterIdentifierStore).isNotNull(); + assertThat(parameterIdentifierStore.getDetectionValues()).hasSize(1); + assertThat(parameterIdentifierStore.getDetectionValues().get(0).asString()) + .isEqualTo(expectedRawIdentifier); + + assertThat(node.asString()).isEqualTo("SLH-DSA-" + expectedParameterSet); + + INode parameterSetIdentifier = node.getChildren().get(ParameterSetIdentifier.class); + assertThat(parameterSetIdentifier).isNotNull(); + assertThat(parameterSetIdentifier.asString()).isEqualTo(expectedParameterSet); + } + + private void assertGeneric(@Nonnull INode node) { + assertThat(node.asString()).isEqualTo("SLH-DSA"); + assertThat(node.getChildren().get(ParameterSetIdentifier.class)).isNull(); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetTripleDESComprehensiveTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetTripleDESComprehensiveTest.java new file mode 100644 index 000000000..23ee4722b --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetTripleDESComprehensiveTest.java @@ -0,0 +1,438 @@ +/* + * 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.CipherAction; +import com.ibm.engine.model.IValue; +import com.ibm.engine.model.KeySize; +import com.ibm.engine.model.Mode; +import com.ibm.engine.model.Padding; +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.KeyLength; +import com.ibm.mapper.model.functionality.Decrypt; +import com.ibm.mapper.model.functionality.Encrypt; +import com.ibm.mapper.model.functionality.Generate; +import com.ibm.mapper.model.functionality.KeyGeneration; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all Triple DES (3DES)-related detection rules (DotNetTripleDES.java). + * + *

Covers all three TripleDES-related classes and their complete API surface: + * + *

    + *
  • TripleDES (abstract base) + *
  • TripleDESCryptoServiceProvider (derived from TripleDES, legacy CAPI) + *
  • TripleDESCng (derived from TripleDES, CNG-backed) + *
+ * + *

TripleDES exposes no extra properties beyond those inherited from {@code SymmetricAlgorithm} + * (unlike RC2's {@code EffectiveKeySize}), so the depending-rule set mirrors {@code DotNetDES} + * exactly, plus a CNG-backed creation rule (mirroring {@code AesCng} in {@code DotNetAES}). + * + *

Finding mapping (one finding per test method in DotNetTripleDESComprehensiveTestFile.cs): + * + *

+ * Section 1 – factory methods / constructors (findings 0–4):
+ *   0  TestTripleDesCreate            → DESede
+ *   1  TestTripleDesCreateNamed       → DESede
+ *   2  TestTripleDesCsp               → DESede
+ *   3  TestTripleDesCng               → DESede
+ *   4  TestTripleDesCngNamed          → DESede
+ *
+ * Section 2 – property setters (findings 5–17):
+ *   5  TestPropertyModeCBC            → DESede-CBC
+ *   6  TestPropertyModeECB            → DESede-ECB
+ *   7  TestPropertyModeCFB            → DESede-CFB
+ *   8  TestPropertyModeOFB            → DESede-OFB
+ *   9  TestPropertyModeCTS            → DESede-CTS
+ *   10 TestPropertyKeySize            → DESede192 (KeyLength appended with no separator)
+ *   11 TestPropertyPaddingPKCS7       → DESede-PKCS7
+ *   12 TestPropertyPaddingNone        → DESede-None
+ *   13 TestPropertyPaddingZeros       → DESede-Zeros
+ *   14 TestPropertyPaddingANSIX923    → DESede-ANSIX923
+ *   15 TestPropertyFeedbackSize       → DESede (BlockSize never rendered in asString)
+ *   16 TestPropertyIV                 → DESede (no IV rule)
+ *   17 TestPropertyKey                → DESede (no Key rule)
+ *
+ * Section 3 – CreateEncryptor/CreateDecryptor (findings 18–21):
+ *   18 TestCreateEncryptorNoArgs      → DESede + Encrypt
+ *   19 TestCreateEncryptorWithArgs    → DESede + Encrypt
+ *   20 TestCreateDecryptorNoArgs      → DESede + Decrypt
+ *   21 TestCreateDecryptorWithArgs    → DESede + Decrypt
+ *
+ * Section 4 – direct encrypt (findings 22–24):
+ *   22 TestEncryptCbc                 → DESede-CBC-PKCS7
+ *   23 TestEncryptEcb                 → DESede-ECB-None
+ *   24 TestEncryptCfb                 → DESede-CFB-None
+ *
+ * Section 5 – direct decrypt (findings 25–27):
+ *   25 TestDecryptCbc                 → DESede-CBC-PKCS7
+ *   26 TestDecryptEcb                 → DESede-ECB-None
+ *   27 TestDecryptCfb                 → DESede-CFB-None
+ *
+ * Section 6 – Try* variants (findings 28–33):
+ *   28 TestTryEncryptCbc              → DESede-CBC-PKCS7
+ *   29 TestTryDecryptCbc              → DESede-CBC-PKCS7
+ *   30 TestTryEncryptEcb              → DESede-ECB-None
+ *   31 TestTryDecryptEcb              → DESede-ECB-None
+ *   32 TestTryEncryptCfb              → DESede-CFB-None
+ *   33 TestTryDecryptCfb              → DESede-CFB-None
+ *
+ * Section 7 – key/IV generation (findings 34–35):
+ *   34 TestGenerateKey                → DESede + KeyGeneration
+ *   35 TestGenerateIV                 → DESede + Generate
+ *
+ * Section 8 – combined usage patterns (findings 36–42):
+ *   36 TestTripleDesCbcFullFlow           → DESede-CBC-PKCS7 + Encrypt
+ *   37 TestTripleDesCspEncryptCbc         → DESede-CBC-PKCS7
+ *   38 TestTripleDesCspDecryptCbc         → DESede-CBC-PKCS7
+ *   39 TestTripleDesCfbFeedback           → DESede-CFB-None
+ *   40 TestTripleDesCbcWithEncryptorOverload → DESede + Encrypt
+ *   41 TestTripleDesEcbEncrypt            → DESede-ECB-None
+ *   42 TestTripleDesCngEncryptCbc         → DESede-CBC-PKCS7
+ * 
+ * + *

NOTE: unlike {@code RC2} (whose {@code asString()} uses {@code composeName(true, true, false)} + * and never renders padding), {@code DESede.asString()} explicitly appends {@code KeyLength} (no + * separator), then {@code -}, then {@code -} (see {@code DESede.java}). Verified + * against actual test-run debug output ({@code target/node-tree.log}) rather than guessed. + */ +class DotNetTripleDESComprehensiveTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify( + "rules/detection/dotnet/DotNetTripleDESComprehensiveTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + // Every top-level finding must be TRIPLEDES + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("TRIPLEDES"); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: simple constructors — only DESede, no children fired + // ----------------------------------------------------------------- + case 0, 1, 2, 3, 4 -> { + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("DESede"); + } + + // ----------------------------------------------------------------- + // Section 2a: property Mode setters + // ----------------------------------------------------------------- + case 5 -> assertModeFindings(detectionStore, nodes, "CBC", "DESede-CBC"); + case 6 -> assertModeFindings(detectionStore, nodes, "ECB", "DESede-ECB"); + case 7 -> assertModeFindings(detectionStore, nodes, "CFB", "DESede-CFB"); + case 8 -> assertModeFindings(detectionStore, nodes, "OFB", "DESede-OFB"); + case 9 -> assertModeFindings(detectionStore, nodes, "CTS", "DESede-CTS"); + + // ----------------------------------------------------------------- + // Section 2b: property KeySize setter — KeyLength is appended + // directly after the name (no separator) by DESede.asString(). + // ----------------------------------------------------------------- + case 10 -> assertKeySizeFindings(detectionStore, nodes, "192", "DESede192"); + + // ----------------------------------------------------------------- + // Section 2c: property Padding setters — unlike RC2, DESede.asString() + // does render padding as a "-" suffix. + // ----------------------------------------------------------------- + case 11 -> assertPaddingFindings(detectionStore, nodes, "PKCS7", "DESede-PKCS7"); + case 12 -> assertPaddingFindings(detectionStore, nodes, "None", "DESede-None"); + case 13 -> assertPaddingFindings(detectionStore, nodes, "Zeros", "DESede-Zeros"); + case 14 -> assertPaddingFindings(detectionStore, nodes, "ANSIX923", "DESede-ANSIX923"); + + // ----------------------------------------------------------------- + // Section 2d: FeedbackSize setter — BlockSize(8) detected but never + // contributes to asString() (no BlockSize branch in DESede.asString()) + // ----------------------------------------------------------------- + case 15 -> { + DetectionStore fbStore = + getStoreOfValueType( + com.ibm.engine.model.BlockSize.class, detectionStore.getChildren()); + assertThat(fbStore).isNotNull(); + assertThat(fbStore.getDetectionValues()).hasSize(1); + assertThat(fbStore.getDetectionValues().get(0).asString()).isEqualTo("8"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("DESede"); + } + + // ----------------------------------------------------------------- + // Section 2e: IV and Key setters — no detection rules for these + // ----------------------------------------------------------------- + case 16, 17 -> { + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo("DESede"); + } + + // ----------------------------------------------------------------- + // Section 3: CreateEncryptor / CreateDecryptor + // ----------------------------------------------------------------- + case 18, 19 -> assertEncryptFindings(detectionStore, nodes, "DESede"); + case 20, 21 -> assertDecryptFindings(detectionStore, nodes, "DESede"); + + // ----------------------------------------------------------------- + // Section 4: direct mode-specific encrypt + // ----------------------------------------------------------------- + case 22 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "DESede-CBC-PKCS7"); + case 23 -> + assertModePaddingFindings( + detectionStore, nodes, "ECB", "None", "DESede-ECB-None"); + case 24 -> + assertModePaddingFindings( + detectionStore, nodes, "CFB", "None", "DESede-CFB-None"); + + // ----------------------------------------------------------------- + // Section 5: direct mode-specific decrypt + // ----------------------------------------------------------------- + case 25 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "DESede-CBC-PKCS7"); + case 26 -> + assertModePaddingFindings( + detectionStore, nodes, "ECB", "None", "DESede-ECB-None"); + case 27 -> + assertModePaddingFindings( + detectionStore, nodes, "CFB", "None", "DESede-CFB-None"); + + // ----------------------------------------------------------------- + // Section 6: Try* variants + // ----------------------------------------------------------------- + case 28, 29 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "DESede-CBC-PKCS7"); + case 30, 31 -> + assertModePaddingFindings( + detectionStore, nodes, "ECB", "None", "DESede-ECB-None"); + case 32, 33 -> + assertModePaddingFindings( + detectionStore, nodes, "CFB", "None", "DESede-CFB-None"); + + // ----------------------------------------------------------------- + // Section 7: GenerateKey / GenerateIV + // ----------------------------------------------------------------- + case 34 -> { + // tdes.GenerateKey() → KeyGeneration functionality node + DetectionStore + genKeyStore = + getStoreOfValueType( + ValueAction.class, detectionStore.getChildren()); + assertThat(genKeyStore).isNotNull(); + assertThat(genKeyStore.getDetectionValues().get(0).asString()) + .isEqualTo("GenerateKey"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).getChildren().get(KeyGeneration.class)).isNotNull(); + } + case 35 -> { + // tdes.GenerateIV() → Generate functionality node + DetectionStore + genIvStore = + getStoreOfValueType( + ValueAction.class, detectionStore.getChildren()); + assertThat(genIvStore).isNotNull(); + assertThat(genIvStore.getDetectionValues().get(0).asString()) + .isEqualTo("GenerateIV"); + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).getChildren().get(Generate.class)).isNotNull(); + } + + // ----------------------------------------------------------------- + // Section 8: combined usage patterns + // ----------------------------------------------------------------- + case 36 -> { + // TestTripleDesCbcFullFlow: Mode=CBC, Padding=PKCS7, CreateEncryptor + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(BlockCipher.class); + assertThat(node.asString()).isEqualTo("DESede-CBC-PKCS7"); + assertThat(node.getChildren().get(com.ibm.mapper.model.Mode.class)).isNotNull(); + assertThat(node.getChildren().get(Encrypt.class)).isNotNull(); + } + case 37 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "DESede-CBC-PKCS7"); + case 38 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "DESede-CBC-PKCS7"); + case 39 -> + assertModePaddingFindings( + detectionStore, nodes, "CFB", "None", "DESede-CFB-None"); + case 40 -> assertEncryptFindings(detectionStore, nodes, "DESede"); + case 41 -> + assertModePaddingFindings( + detectionStore, nodes, "ECB", "None", "DESede-ECB-None"); + case 42 -> + assertModePaddingFindings( + detectionStore, nodes, "CBC", "PKCS7", "DESede-CBC-PKCS7"); + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertModeFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedMode, + @Nonnull String expectedNodeString) { + + DetectionStore modeStore = + getStoreOfValueType(Mode.class, store.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues()).hasSize(1); + assertThat(modeStore.getDetectionValues().get(0).asString()).isEqualTo(expectedMode); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(com.ibm.mapper.model.Mode.class)).isNotNull(); + } + + private void assertKeySizeFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedKeySize, + @Nonnull String expectedNodeString) { + + DetectionStore keySizeStore = + getStoreOfValueType(KeySize.class, store.getChildren()); + assertThat(keySizeStore).isNotNull(); + assertThat(keySizeStore.getDetectionValues()).hasSize(1); + assertThat(keySizeStore.getDetectionValues().get(0).asString()).isEqualTo(expectedKeySize); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(KeyLength.class)).isNotNull(); + assertThat(nodes.get(0).getChildren().get(KeyLength.class).asString()) + .isEqualTo(expectedKeySize); + } + + private void assertPaddingFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedPadding, + @Nonnull String expectedNodeString) { + + DetectionStore paddingStore = + getStoreOfValueType(Padding.class, store.getChildren()); + assertThat(paddingStore).isNotNull(); + assertThat(paddingStore.getDetectionValues()).hasSize(1); + assertThat(paddingStore.getDetectionValues().get(0).asString()).isEqualTo(expectedPadding); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + } + + private void assertEncryptFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedNodeString) { + + DetectionStore encryptStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(encryptStore).isNotNull(); + assertThat(encryptStore.getDetectionValues()).hasSize(1); + assertThat(encryptStore.getDetectionValues().get(0).asString()).isEqualTo("ENCRYPT"); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(Encrypt.class)).isNotNull(); + } + + private void assertDecryptFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedNodeString) { + + DetectionStore decryptStore = + getStoreOfValueType(CipherAction.class, store.getChildren()); + assertThat(decryptStore).isNotNull(); + assertThat(decryptStore.getDetectionValues()).hasSize(1); + assertThat(decryptStore.getDetectionValues().get(0).asString()).isEqualTo("DECRYPT"); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + assertThat(nodes.get(0).getChildren().get(Decrypt.class)).isNotNull(); + } + + /** + * Asserts mode+padding findings using the translated node tree. Direct-mode methods + * (EncryptCbc, TryDecryptEcb, etc.) place Mode and Padding in the same child detection store, + * so we validate via the final node string rather than per-store inspection. + */ + private void assertModePaddingFindings( + @Nonnull DetectionStore store, + @Nonnull List nodes, + @Nonnull String expectedMode, + @Nonnull String expectedPadding, + @Nonnull String expectedNodeString) { + + DetectionStore modeStore = + getStoreOfValueType(Mode.class, store.getChildren()); + assertThat(modeStore).isNotNull(); + assertThat(modeStore.getDetectionValues()) + .anySatisfy(v -> assertThat(v.asString()).isEqualTo(expectedMode)); + + assertThat(nodes).hasSize(1); + assertThat(nodes.get(0).getKind()).isEqualTo(BlockCipher.class); + assertThat(nodes.get(0).asString()).isEqualTo(expectedNodeString); + } +} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellmanTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellmanTest.java new file mode 100644 index 000000000..c59d91dda --- /dev/null +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellmanTest.java @@ -0,0 +1,152 @@ +/* + * 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.EllipticCurve; +import com.ibm.mapper.model.INode; +import com.ibm.mapper.model.KeyAgreement; +import com.ibm.mapper.model.Oid; +import com.ibm.mapper.model.functionality.Generate; +import com.ibm.plugin.CSharpVerifier; +import com.ibm.plugin.TestBase; +import java.util.List; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for all X25519 (X25519DiffieHellman) related detection rules + * (DotNetX25519DiffieHellman.java). + * + *

Covers all three X25519DiffieHellman-related classes and their complete verified operational + * API surface: + * + *

    + *
  • X25519DiffieHellman (abstract base — entry point is the static {@code GenerateKey()} + * factory, there is no {@code Create()}) + *
  • X25519DiffieHellmanCng, X25519DiffieHellmanOpenSsl (derived from X25519DiffieHellman) + *
+ * + *

Note: every top-level finding translates to a {@link com.ibm.mapper.model.algorithms.X25519} + * node ({@code node.asString()} == "x25519"), whose kind is {@link KeyAgreement}, and which always + * carries a {@link EllipticCurve} child ({@code Curve25519}, {@code asString()} == "Curve25519") + * and an {@link Oid} child ("1.3.101.110") added unconditionally by the {@code X25519} algorithm + * model constructor. + * + *

Finding mapping (one finding per test method in DotNetX25519DiffieHellmanTestFile.cs): + * + *

+ * Section 1 – factory method / constructors (findings 0–2):
+ *   0 TestX25519GenerateKey   → X25519
+ *   1 TestX25519Cng           → X25519
+ *   2 TestX25519OpenSsl       → X25519
+ *
+ * Section 2 – DeriveRawSecretAgreement operation (findings 3–4):
+ *   3 TestDeriveRawSecretAgreementByteArray  → X25519 + Generate child
+ *   4 TestDeriveRawSecretAgreementOtherParty → X25519 + Generate child
+ *
+ * Section 3 – combined usage patterns (findings 5–6):
+ *   5 TestX25519CngDeriveFlow       → X25519 + Generate child
+ *   6 TestX25519OpenSslDeriveFlow   → X25519 + Generate child
+ * 
+ */ +class DotNetX25519DiffieHellmanTest extends TestBase { + + @Test + void test() throws Exception { + CSharpVerifier.verify("rules/detection/dotnet/DotNetX25519DiffieHellmanTestFile.cs", this); + } + + @Override + public void asserts( + int findingId, + @Nonnull + DetectionStore + detectionStore, + @Nonnull List nodes) { + + // Every top-level finding must be X25519 + assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("X25519"); + + assertThat(nodes).hasSize(1); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(KeyAgreement.class); + assertThat(node.asString()).isEqualTo("x25519"); + + INode curve = node.getChildren().get(EllipticCurve.class); + assertThat(curve).isNotNull(); + assertThat(curve.asString()).isEqualTo("Curve25519"); + + INode oid = node.getChildren().get(Oid.class); + assertThat(oid).isNotNull(); + assertThat(oid.asString()).isEqualTo("1.3.101.110"); + + switch (findingId) { + + // ----------------------------------------------------------------- + // Section 1: simple constructors — only X25519, no extra children + // ----------------------------------------------------------------- + case 0, 1, 2 -> { + // node.asString() already asserted to be "x25519" above + } + + // ----------------------------------------------------------------- + // Section 2: DeriveRawSecretAgreement operation + // ----------------------------------------------------------------- + case 3, 4 -> assertRawSecretAgreement(detectionStore, node); + + // ----------------------------------------------------------------- + // Section 3: combined usage patterns + // ----------------------------------------------------------------- + case 5, 6 -> assertRawSecretAgreement(detectionStore, node); + + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + // ------------------------------------------------------------------------- + // Assertion helpers + // ------------------------------------------------------------------------- + + private void assertRawSecretAgreement( + @Nonnull DetectionStore store, + @Nonnull INode node) { + + DetectionStore deriveStore = + getStoreOfValueType(ValueAction.class, store.getChildren()); + assertThat(deriveStore).isNotNull(); + assertThat(deriveStore.getDetectionValues()).hasSize(1); + + assertThat(node.getChildren().get(Generate.class)).isNotNull(); + } +} diff --git a/mapper/ciphersuites.json b/mapper/ciphersuites.json index 44542d946..9f4583301 100644 --- a/mapper/ciphersuites.json +++ b/mapper/ciphersuites.json @@ -1 +1 @@ -{"ciphersuites": [{"TLS_AES_128_CCM_8_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x13", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "TLS_AES_128_CCM_SHA256", "hex_byte_1": "0x13", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "TLS_AES_128_GCM_SHA256", "hex_byte_1": "0x13", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "TLS_AES_256_GCM_SHA384", "hex_byte_1": "0x13", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_CHACHA20_POLY1305_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x13", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x19", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x17", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_3DES_EDE_CBC_SHA1", "openssl_name": "ADH-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x1B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_AES_128_CBC_SHA1", "openssl_name": "ADH-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x34", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_128_CBC_SHA256", "openssl_name": "ADH-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6C", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_128_GCM_SHA256", "openssl_name": "ADH-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA6", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_AES_256_CBC_SHA1", "openssl_name": "ADH-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x3A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_256_CBC_SHA256", "openssl_name": "ADH-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6D", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DH_ANON_AES_256_GCM_SHA384", "openssl_name": "ADH-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA7", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x46", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x47", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_CBC_SHA1", "openssl_name": "ADH-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x46", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_CBC_SHA256", "openssl_name": "ADH-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBF", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x84", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_CBC_SHA1", "openssl_name": "ADH-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x89", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_CBC_SHA256", "openssl_name": "ADH-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC5", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x85", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_RC4_128_MD5": {"gnutls_name": "TLS_DH_ANON_ARCFOUR_128_MD5", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x18", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "ADH-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x9B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0B", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0D", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x30", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x3E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA4", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x36", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x68", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA5", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x58", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x59", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x42", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xBB", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x82", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x85", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC1", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x83", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0C", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x97", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x11", "protocol_version": "TLS EXPORT", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-DSS-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x13", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_AES_128_CBC_SHA1", "openssl_name": "DHE-DSS-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x32", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_128_CBC_SHA256", "openssl_name": "DHE-DSS-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x40", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_128_GCM_SHA256", "openssl_name": "DHE-DSS-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_AES_256_CBC_SHA1", "openssl_name": "DHE-DSS-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x38", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_256_CBC_SHA256", "openssl_name": "DHE-DSS-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6A", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_DSS_AES_256_GCM_SHA384", "openssl_name": "DHE-DSS-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x42", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x56", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x43", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x57", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_CBC_SHA1", "openssl_name": "DHE-DSS-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x44", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-DSS-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBD", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x80", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_CBC_SHA1", "openssl_name": "DHE-DSS-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x87", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_CBC_SHA256", "openssl_name": "DHE-DSS-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x81", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x12", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "DHE-DSS-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x99", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_AES_128_CBC_SHA1", "openssl_name": "DHE-PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x90", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_PSK_AES_128_CBC_SHA256", "openssl_name": "DHE-PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CCM": {"gnutls_name": "TLS_DHE_PSK_AES_128_CCM", "openssl_name": "DHE-PSK-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA6", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_PSK_AES_128_GCM_SHA256", "openssl_name": "DHE-PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_AES_256_CBC_SHA1", "openssl_name": "DHE-PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x91", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_DHE_PSK_AES_256_CBC_SHA384", "openssl_name": "DHE-PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CCM": {"gnutls_name": "TLS_DHE_PSK_AES_256_CCM", "openssl_name": "DHE-PSK-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA7", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_PSK_AES_256_GCM_SHA384", "openssl_name": "DHE-PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x66", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6C", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x67", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x96", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x90", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "DHE-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x97", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x91", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_DHE_PSK_CHACHA20_POLY1305", "openssl_name": "DHE-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA1", "openssl_name": "DHE-PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA256", "openssl_name": "DHE-PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB4", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA384", "openssl_name": "DHE-PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB5", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_DHE_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x8E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x14", "protocol_version": "TLS EXPORT", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-RSA-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x16", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_AES_128_CBC_SHA1", "openssl_name": "DHE-RSA-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x33", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_128_CBC_SHA256", "openssl_name": "DHE-RSA-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x67", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_DHE_RSA_AES_128_CCM", "openssl_name": "DHE-RSA-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_DHE_RSA_AES_128_CCM_8", "openssl_name": "DHE-RSA-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_128_GCM_SHA256", "openssl_name": "DHE-RSA-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x9E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_AES_256_CBC_SHA1", "openssl_name": "DHE-RSA-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x39", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_256_CBC_SHA256", "openssl_name": "DHE-RSA-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6B", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_DHE_RSA_AES_256_CCM", "openssl_name": "DHE-RSA-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_DHE_RSA_AES_256_CCM_8", "openssl_name": "DHE-RSA-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_RSA_AES_256_GCM_SHA384", "openssl_name": "DHE-RSA-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0x9F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x44", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x52", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x45", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x53", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_CBC_SHA1", "openssl_name": "DHE-RSA-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x45", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-RSA-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBE", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7C", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_CBC_SHA1", "openssl_name": "DHE-RSA-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x88", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_CBC_SHA256", "openssl_name": "DHE-RSA-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC4", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_DHE_RSA_CHACHA20_POLY1305", "openssl_name": "DHE-RSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x15", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "DHE-RSA-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x9A", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0E", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x10", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x31", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x3F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA0", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x37", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x69", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA1", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x40", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x54", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x41", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x55", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x43", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xBC", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x86", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC2", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x98", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB2", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB0", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_256_CCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB3", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB1", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_3DES_EDE_CBC_SHA1", "openssl_name": "AECDH-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x17", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_AES_128_CBC_SHA1", "openssl_name": "AECDH-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x18", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_AES_256_CBC_SHA1", "openssl_name": "AECDH-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x19", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDH_ANON_NULL_SHA1", "openssl_name": "AECDH-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x15", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDH_ANON_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x16", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x25", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x26", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4A", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4B", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x74", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x88", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x75", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x89", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_NULL_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x08", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x09", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CBC_SHA256", "openssl_name": "ECDHE-ECDSA-AES128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x23", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CCM", "openssl_name": "ECDHE-ECDSA-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CCM_8", "openssl_name": "ECDHE-ECDSA-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_GCM_SHA256", "openssl_name": "ECDHE-ECDSA-AES128-GCM-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x2B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x0A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CBC_SHA384", "openssl_name": "ECDHE-ECDSA-AES256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x24", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CCM", "openssl_name": "ECDHE-ECDSA-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CCM_8", "openssl_name": "ECDHE-ECDSA-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAF", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_GCM_SHA384", "openssl_name": "ECDHE-ECDSA-AES256-GCM-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x2C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x48", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x49", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5D", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-ECDSA-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x72", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x86", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-ECDSA-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x73", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x87", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CHACHA20_POLY1305", "openssl_name": "ECDHE-ECDSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_NULL_SHA1", "openssl_name": "ECDHE-ECDSA-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x06", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x07", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x34", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_AES_128_CBC_SHA1", "openssl_name": "ECDHE-PSK-AES128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x35", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_AES_128_CBC_SHA256", "openssl_name": "ECDHE-PSK-AES128-CBC-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x37", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_AES_256_CBC_SHA1", "openssl_name": "ECDHE-PSK-AES256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x36", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_AES_256_CBC_SHA384", "openssl_name": "ECDHE-PSK-AES256-CBC-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x38", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x70", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x71", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x9A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x9B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_CHACHA20_POLY1305", "openssl_name": "ECDHE-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA1", "openssl_name": "ECDHE-PSK-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x39", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA256", "openssl_name": "ECDHE-PSK-NULL-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x3A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA384", "openssl_name": "ECDHE-PSK-NULL-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x3B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x33", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-RSA-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x12", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_CBC_SHA1", "openssl_name": "ECDHE-RSA-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x13", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_CBC_SHA256", "openssl_name": "ECDHE-RSA-AES128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x27", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_GCM_SHA256", "openssl_name": "ECDHE-RSA-AES128-GCM-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x2F", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_CBC_SHA1", "openssl_name": "ECDHE-RSA-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x14", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_CBC_SHA384", "openssl_name": "ECDHE-RSA-AES256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x28", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_GCM_SHA384", "openssl_name": "ECDHE-RSA-AES256-GCM-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x30", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x60", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4D", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x61", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-RSA-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x76", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-RSA-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x77", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CHACHA20_POLY1305", "openssl_name": "ECDHE-RSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_RSA_NULL_SHA1", "openssl_name": "ECDHE-RSA-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x10", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_RSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x11", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x29", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x31", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2A", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x32", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x62", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x63", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x78", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8C", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x79", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_NULL_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0B", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0C", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_28147_CNT_IMIT": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "28147 CNT", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x00", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "KUZNYECHIK CTR", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "KUZNYECHIK MGM L", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "KUZNYECHIK MGM S", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_CTR_OMAC": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "MAGMA CTR", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_MGM_L": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "MAGMA MGM L", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_MGM_S": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x06", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "MAGMA MGM S", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x29", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x26", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x2A", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x27", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x2B", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC4_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x28", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_3DES_EDE_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x23", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1F", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_DES_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x22", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1E", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_IDEA_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x25", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "IDEA CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_IDEA_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x21", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "IDEA CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_RC4_128_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x24", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x20", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_NULL_WITH_NULL_NULL": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x00", "protocol_version": "TLS", "kex_algorithm": "NULL", "auth_algorithm": "NULL", "enc_algorithm": "NULL", "hash_algorithm": "NULL", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_DHE_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_DHE_PSK_AES_128_CCM_8", "openssl_name": "DHE-PSK-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_DHE_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_DHE_PSK_AES_256_CCM_8", "openssl_name": "DHE-PSK-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8B", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_PSK_AES_128_CBC_SHA1", "openssl_name": "PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8C", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_PSK_AES_128_CBC_SHA256", "openssl_name": "PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CCM": {"gnutls_name": "TLS_PSK_AES_128_CCM", "openssl_name": "PSK-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA4", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_PSK_AES_128_CCM_8", "openssl_name": "PSK-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_PSK_AES_128_GCM_SHA256", "openssl_name": "PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_PSK_AES_256_CBC_SHA1", "openssl_name": "PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8D", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_PSK_AES_256_CBC_SHA384", "openssl_name": "PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAF", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CCM": {"gnutls_name": "TLS_PSK_AES_256_CCM", "openssl_name": "PSK-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA5", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_PSK_AES_256_CCM_8", "openssl_name": "PSK-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_PSK_AES_256_GCM_SHA384", "openssl_name": "PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x64", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6A", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x65", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6B", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x94", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8E", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x95", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8F", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_PSK_CHACHA20_POLY1305", "openssl_name": "PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_PSK_NULL_SHA1", "openssl_name": "PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2C", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_PSK_NULL_SHA256", "openssl_name": "PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB0", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_PSK_NULL_SHA384", "openssl_name": "PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB1", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x8A", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x08", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x06", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x03", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "RSA-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x93", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_AES_128_CBC_SHA1", "openssl_name": "RSA-PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x94", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_PSK_AES_128_CBC_SHA256", "openssl_name": "RSA-PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB6", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_PSK_AES_128_GCM_SHA256", "openssl_name": "RSA-PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_AES_256_CBC_SHA1", "openssl_name": "RSA-PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x95", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_RSA_PSK_AES_256_CBC_SHA384", "openssl_name": "RSA-PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB7", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_PSK_AES_256_GCM_SHA384", "openssl_name": "RSA-PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x68", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6E", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x69", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6F", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "RSA-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x98", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x92", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "RSA-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x99", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x93", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_RSA_PSK_CHACHA20_POLY1305", "openssl_name": "RSA-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA1", "openssl_name": "RSA-PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2E", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA256", "openssl_name": "RSA-PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB8", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA384", "openssl_name": "RSA-PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB9", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_RSA_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x92", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x0A", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_RSA_AES_128_CBC_SHA1", "openssl_name": "AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2F", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_AES_128_CBC_SHA256", "openssl_name": "AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_RSA_AES_128_CCM", "openssl_name": "AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_RSA_AES_128_CCM_8", "openssl_name": "AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA0", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_AES_128_GCM_SHA256", "openssl_name": "AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x9C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_RSA_AES_256_CBC_SHA1", "openssl_name": "AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x35", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_RSA_AES_256_CBC_SHA256", "openssl_name": "AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_RSA_AES_256_CCM", "openssl_name": "AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_RSA_AES_256_CCM_8", "openssl_name": "AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA1", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_AES_256_GCM_SHA384", "openssl_name": "AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0x9D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x50", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x51", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_RSA_CAMELLIA_128_CBC_SHA1", "openssl_name": "CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x41", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBA", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7A", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_RSA_CAMELLIA_256_CBC_SHA1", "openssl_name": "CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x84", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_256_CBC_SHA256", "openssl_name": "CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC0", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7B", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x09", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_IDEA_CBC_SHA": {"gnutls_name": "", "openssl_name": "IDEA-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x07", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "IDEA CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_MD5": {"gnutls_name": "TLS_RSA_NULL_MD5", "openssl_name": "NULL-MD5", "hex_byte_1": "0x00", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_SHA": {"gnutls_name": "TLS_RSA_NULL_SHA1", "openssl_name": "NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_SHA256": {"gnutls_name": "TLS_RSA_NULL_SHA256", "openssl_name": "NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3B", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_RC4_128_MD5": {"gnutls_name": "TLS_RSA_ARCFOUR_128_MD5", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_RSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x96", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SHA256_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB4", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "SHA256", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SHA384_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB5", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "SHA384", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SM4_CCM_SM3": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC7", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "SM4 CCM", "hash_algorithm": "SM3", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SM4_GCM_SM3": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC6", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "SM4 GCM", "hash_algorithm": "SM3", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-DSS-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1C", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_AES_128_CBC_SHA1", "openssl_name": "SRP-DSS-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1F", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_AES_256_CBC_SHA1", "openssl_name": "SRP-DSS-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x22", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-RSA-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1B", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_AES_128_CBC_SHA1", "openssl_name": "SRP-RSA-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1E", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_AES_256_CBC_SHA1", "openssl_name": "SRP-RSA-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x21", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1A", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_AES_128_CBC_SHA1", "openssl_name": "SRP-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1D", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_AES_256_CBC_SHA1", "openssl_name": "SRP-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x20", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}]} \ No newline at end of file +{"ciphersuites": [{"TLS_AES_128_CCM_8_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x13", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_CCM_ASCONHASH256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x71", "protocol_version": "TLS", "kex_algorithm": "AES", "auth_algorithm": "128 CCM ASCONHASH256", "enc_algorithm": "", "hash_algorithm": "", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "TLS_AES_128_CCM_SHA256", "hex_byte_1": "0x13", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_GCM_ASCONHASH256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x70", "protocol_version": "TLS", "kex_algorithm": "AES", "auth_algorithm": "128 GCM ASCONHASH256", "enc_algorithm": "", "hash_algorithm": "", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "TLS_AES_128_GCM_SHA256", "hex_byte_1": "0x13", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "TLS_AES_256_GCM_SHA384", "hex_byte_1": "0x13", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_ASCONAEAD128_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x6F", "protocol_version": "TLS", "kex_algorithm": "ASCONAEAD128", "auth_algorithm": "SHA256", "enc_algorithm": "", "hash_algorithm": "", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_CHACHA20_POLY1305_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x13", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x19", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x17", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_3DES_EDE_CBC_SHA1", "openssl_name": "ADH-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x1B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_AES_128_CBC_SHA1", "openssl_name": "ADH-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x34", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_128_CBC_SHA256", "openssl_name": "ADH-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6C", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_128_GCM_SHA256", "openssl_name": "ADH-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA6", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_AES_256_CBC_SHA1", "openssl_name": "ADH-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x3A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_256_CBC_SHA256", "openssl_name": "ADH-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6D", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DH_ANON_AES_256_GCM_SHA384", "openssl_name": "ADH-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA7", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x46", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x47", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_CBC_SHA1", "openssl_name": "ADH-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x46", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_CBC_SHA256", "openssl_name": "ADH-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBF", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x84", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_CBC_SHA1", "openssl_name": "ADH-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x89", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_CBC_SHA256", "openssl_name": "ADH-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC5", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x85", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_RC4_128_MD5": {"gnutls_name": "TLS_DH_ANON_ARCFOUR_128_MD5", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x18", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "ADH-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x9B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0B", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0D", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x30", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x3E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA4", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x36", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x68", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA5", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x58", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x59", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x42", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xBB", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x82", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x85", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC1", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x83", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0C", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x97", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x11", "protocol_version": "TLS EXPORT", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-DSS-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x13", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_AES_128_CBC_SHA1", "openssl_name": "DHE-DSS-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x32", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_128_CBC_SHA256", "openssl_name": "DHE-DSS-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x40", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_128_GCM_SHA256", "openssl_name": "DHE-DSS-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_AES_256_CBC_SHA1", "openssl_name": "DHE-DSS-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x38", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_256_CBC_SHA256", "openssl_name": "DHE-DSS-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6A", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_DSS_AES_256_GCM_SHA384", "openssl_name": "DHE-DSS-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x42", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x56", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x43", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x57", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_CBC_SHA1", "openssl_name": "DHE-DSS-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x44", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-DSS-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBD", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x80", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_CBC_SHA1", "openssl_name": "DHE-DSS-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x87", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_CBC_SHA256", "openssl_name": "DHE-DSS-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x81", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x12", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "DHE-DSS-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x99", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_AES_128_CBC_SHA1", "openssl_name": "DHE-PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x90", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_PSK_AES_128_CBC_SHA256", "openssl_name": "DHE-PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CCM": {"gnutls_name": "TLS_DHE_PSK_AES_128_CCM", "openssl_name": "DHE-PSK-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA6", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_PSK_AES_128_GCM_SHA256", "openssl_name": "DHE-PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_AES_256_CBC_SHA1", "openssl_name": "DHE-PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x91", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_DHE_PSK_AES_256_CBC_SHA384", "openssl_name": "DHE-PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CCM": {"gnutls_name": "TLS_DHE_PSK_AES_256_CCM", "openssl_name": "DHE-PSK-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA7", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_PSK_AES_256_GCM_SHA384", "openssl_name": "DHE-PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x66", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6C", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x67", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x96", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x90", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "DHE-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x97", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x91", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_DHE_PSK_CHACHA20_POLY1305", "openssl_name": "DHE-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA1", "openssl_name": "DHE-PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA256", "openssl_name": "DHE-PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB4", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA384", "openssl_name": "DHE-PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB5", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_DHE_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x8E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x14", "protocol_version": "TLS EXPORT", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-RSA-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x16", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_AES_128_CBC_SHA1", "openssl_name": "DHE-RSA-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x33", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_128_CBC_SHA256", "openssl_name": "DHE-RSA-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x67", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_DHE_RSA_AES_128_CCM", "openssl_name": "DHE-RSA-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_DHE_RSA_AES_128_CCM_8", "openssl_name": "DHE-RSA-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_128_GCM_SHA256", "openssl_name": "DHE-RSA-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x9E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_AES_256_CBC_SHA1", "openssl_name": "DHE-RSA-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x39", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_256_CBC_SHA256", "openssl_name": "DHE-RSA-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6B", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_DHE_RSA_AES_256_CCM", "openssl_name": "DHE-RSA-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_DHE_RSA_AES_256_CCM_8", "openssl_name": "DHE-RSA-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_RSA_AES_256_GCM_SHA384", "openssl_name": "DHE-RSA-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0x9F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x44", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x52", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x45", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x53", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_CBC_SHA1", "openssl_name": "DHE-RSA-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x45", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-RSA-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBE", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7C", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_CBC_SHA1", "openssl_name": "DHE-RSA-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x88", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_CBC_SHA256", "openssl_name": "DHE-RSA-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC4", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_DHE_RSA_CHACHA20_POLY1305", "openssl_name": "DHE-RSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x15", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "DHE-RSA-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x9A", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0E", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x10", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x31", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x3F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA0", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x37", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x69", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA1", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x40", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x54", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x41", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x55", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x43", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xBC", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x86", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC2", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x98", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB2", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB0", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_256_CCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB3", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB1", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_3DES_EDE_CBC_SHA1", "openssl_name": "AECDH-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x17", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_AES_128_CBC_SHA1", "openssl_name": "AECDH-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x18", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_AES_256_CBC_SHA1", "openssl_name": "AECDH-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x19", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDH_ANON_NULL_SHA1", "openssl_name": "AECDH-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x15", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDH_ANON_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x16", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x25", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x26", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4A", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4B", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x74", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x88", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x75", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x89", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_NULL_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x08", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x09", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CBC_SHA256", "openssl_name": "ECDHE-ECDSA-AES128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x23", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CCM", "openssl_name": "ECDHE-ECDSA-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CCM_8", "openssl_name": "ECDHE-ECDSA-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_GCM_SHA256", "openssl_name": "ECDHE-ECDSA-AES128-GCM-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x2B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x0A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CBC_SHA384", "openssl_name": "ECDHE-ECDSA-AES256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x24", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CCM", "openssl_name": "ECDHE-ECDSA-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CCM_8", "openssl_name": "ECDHE-ECDSA-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAF", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_GCM_SHA384", "openssl_name": "ECDHE-ECDSA-AES256-GCM-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x2C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x48", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x49", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5D", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-ECDSA-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x72", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x86", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-ECDSA-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x73", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x87", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CHACHA20_POLY1305", "openssl_name": "ECDHE-ECDSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_NULL_SHA1", "openssl_name": "ECDHE-ECDSA-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x06", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x07", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x34", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_AES_128_CBC_SHA1", "openssl_name": "ECDHE-PSK-AES128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x35", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_AES_128_CBC_SHA256", "openssl_name": "ECDHE-PSK-AES128-CBC-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x37", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_AES_256_CBC_SHA1", "openssl_name": "ECDHE-PSK-AES256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x36", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_AES_256_CBC_SHA384", "openssl_name": "ECDHE-PSK-AES256-CBC-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x38", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x70", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x71", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x9A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x9B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_CHACHA20_POLY1305", "openssl_name": "ECDHE-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA1", "openssl_name": "ECDHE-PSK-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x39", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA256", "openssl_name": "ECDHE-PSK-NULL-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x3A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA384", "openssl_name": "ECDHE-PSK-NULL-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x3B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x33", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-RSA-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x12", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_CBC_SHA1", "openssl_name": "ECDHE-RSA-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x13", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_CBC_SHA256", "openssl_name": "ECDHE-RSA-AES128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x27", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_GCM_SHA256", "openssl_name": "ECDHE-RSA-AES128-GCM-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x2F", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_CBC_SHA1", "openssl_name": "ECDHE-RSA-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x14", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_CBC_SHA384", "openssl_name": "ECDHE-RSA-AES256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x28", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_GCM_SHA384", "openssl_name": "ECDHE-RSA-AES256-GCM-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x30", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x60", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4D", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x61", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-RSA-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x76", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-RSA-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x77", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CHACHA20_POLY1305", "openssl_name": "ECDHE-RSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_RSA_NULL_SHA1", "openssl_name": "ECDHE-RSA-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x10", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_RSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x11", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x29", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x31", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2A", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x32", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x62", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x63", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x78", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8C", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x79", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_NULL_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0B", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0C", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_28147_CNT_IMIT": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "28147 CNT", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x00", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "KUZNYECHIK CTR", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "KUZNYECHIK MGM L", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "KUZNYECHIK MGM S", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_CTR_OMAC": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "MAGMA CTR", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_MGM_L": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "MAGMA MGM L", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_MGM_S": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x06", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "MAGMA MGM S", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x29", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x26", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x2A", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x27", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x2B", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC4_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x28", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_3DES_EDE_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x23", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1F", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_DES_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x22", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1E", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_IDEA_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x25", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "IDEA CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_IDEA_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x21", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "IDEA CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_RC4_128_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x24", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x20", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_NULL_WITH_NULL_NULL": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x00", "protocol_version": "TLS", "kex_algorithm": "NULL", "auth_algorithm": "NULL", "enc_algorithm": "NULL", "hash_algorithm": "NULL", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_DHE_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_DHE_PSK_AES_128_CCM_8", "openssl_name": "DHE-PSK-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_DHE_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_DHE_PSK_AES_256_CCM_8", "openssl_name": "DHE-PSK-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8B", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_PSK_AES_128_CBC_SHA1", "openssl_name": "PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8C", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_PSK_AES_128_CBC_SHA256", "openssl_name": "PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CCM": {"gnutls_name": "TLS_PSK_AES_128_CCM", "openssl_name": "PSK-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA4", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_PSK_AES_128_CCM_8", "openssl_name": "PSK-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_PSK_AES_128_GCM_SHA256", "openssl_name": "PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_PSK_AES_256_CBC_SHA1", "openssl_name": "PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8D", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_PSK_AES_256_CBC_SHA384", "openssl_name": "PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAF", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CCM": {"gnutls_name": "TLS_PSK_AES_256_CCM", "openssl_name": "PSK-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA5", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_PSK_AES_256_CCM_8", "openssl_name": "PSK-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_PSK_AES_256_GCM_SHA384", "openssl_name": "PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x64", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6A", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x65", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6B", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x94", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8E", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x95", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8F", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_PSK_CHACHA20_POLY1305", "openssl_name": "PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_PSK_NULL_SHA1", "openssl_name": "PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2C", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_PSK_NULL_SHA256", "openssl_name": "PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB0", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_PSK_NULL_SHA384", "openssl_name": "PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB1", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x8A", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x08", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x06", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x03", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "RSA-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x93", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_AES_128_CBC_SHA1", "openssl_name": "RSA-PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x94", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_PSK_AES_128_CBC_SHA256", "openssl_name": "RSA-PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB6", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_PSK_AES_128_GCM_SHA256", "openssl_name": "RSA-PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_AES_256_CBC_SHA1", "openssl_name": "RSA-PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x95", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_RSA_PSK_AES_256_CBC_SHA384", "openssl_name": "RSA-PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB7", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_PSK_AES_256_GCM_SHA384", "openssl_name": "RSA-PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x68", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6E", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x69", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6F", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "RSA-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x98", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x92", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "RSA-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x99", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x93", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_RSA_PSK_CHACHA20_POLY1305", "openssl_name": "RSA-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA1", "openssl_name": "RSA-PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2E", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA256", "openssl_name": "RSA-PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB8", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA384", "openssl_name": "RSA-PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB9", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_RSA_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x92", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x0A", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_RSA_AES_128_CBC_SHA1", "openssl_name": "AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2F", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_AES_128_CBC_SHA256", "openssl_name": "AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_RSA_AES_128_CCM", "openssl_name": "AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_RSA_AES_128_CCM_8", "openssl_name": "AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA0", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_AES_128_GCM_SHA256", "openssl_name": "AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x9C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_RSA_AES_256_CBC_SHA1", "openssl_name": "AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x35", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_RSA_AES_256_CBC_SHA256", "openssl_name": "AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_RSA_AES_256_CCM", "openssl_name": "AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_RSA_AES_256_CCM_8", "openssl_name": "AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA1", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_AES_256_GCM_SHA384", "openssl_name": "AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0x9D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x50", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x51", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_RSA_CAMELLIA_128_CBC_SHA1", "openssl_name": "CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x41", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBA", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7A", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_RSA_CAMELLIA_256_CBC_SHA1", "openssl_name": "CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x84", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_256_CBC_SHA256", "openssl_name": "CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC0", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7B", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x09", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_IDEA_CBC_SHA": {"gnutls_name": "", "openssl_name": "IDEA-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x07", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "IDEA CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_MD5": {"gnutls_name": "TLS_RSA_NULL_MD5", "openssl_name": "NULL-MD5", "hex_byte_1": "0x00", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_SHA": {"gnutls_name": "TLS_RSA_NULL_SHA1", "openssl_name": "NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_SHA256": {"gnutls_name": "TLS_RSA_NULL_SHA256", "openssl_name": "NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3B", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_RC4_128_MD5": {"gnutls_name": "TLS_RSA_ARCFOUR_128_MD5", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_RSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x96", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SHA256_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB4", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "SHA256", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SHA384_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB5", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "SHA384", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SM4_CCM_SM3": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC7", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "SM4 CCM", "hash_algorithm": "SM3", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SM4_GCM_SM3": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC6", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "SM4 GCM", "hash_algorithm": "SM3", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-DSS-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1C", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_AES_128_CBC_SHA1", "openssl_name": "SRP-DSS-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1F", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_AES_256_CBC_SHA1", "openssl_name": "SRP-DSS-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x22", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-RSA-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1B", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_AES_128_CBC_SHA1", "openssl_name": "SRP-RSA-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1E", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_AES_256_CBC_SHA1", "openssl_name": "SRP-RSA-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x21", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1A", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_AES_128_CBC_SHA1", "openssl_name": "SRP-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1D", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_AES_256_CBC_SHA1", "openssl_name": "SRP-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x20", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}]} \ No newline at end of file diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/ssl/json/JsonCipherSuites.java b/mapper/src/main/java/com/ibm/mapper/mapper/ssl/json/JsonCipherSuites.java index b94d3d1a3..8bcc4a6bf 100644 --- a/mapper/src/main/java/com/ibm/mapper/mapper/ssl/json/JsonCipherSuites.java +++ b/mapper/src/main/java/com/ibm/mapper/mapper/ssl/json/JsonCipherSuites.java @@ -41,6 +41,17 @@ private JsonCipherSuites() { null, "AES 128 CCM 8", "SHA256")), + Map.entry( + "TLS_AES_128_CCM_ASCONHASH256", + new JsonCipherSuite( + "TLS_AES_128_CCM_ASCONHASH256", + null, + null, + new String[] {"0x00", "0x71"}, + "AES", + "128 CCM ASCONHASH256", + null, + null)), Map.entry( "TLS_AES_128_CCM_SHA256", new JsonCipherSuite( @@ -52,6 +63,17 @@ private JsonCipherSuites() { null, "AES 128 CCM", "SHA256")), + Map.entry( + "TLS_AES_128_GCM_ASCONHASH256", + new JsonCipherSuite( + "TLS_AES_128_GCM_ASCONHASH256", + null, + null, + new String[] {"0x00", "0x70"}, + "AES", + "128 GCM ASCONHASH256", + null, + null)), Map.entry( "TLS_AES_128_GCM_SHA256", new JsonCipherSuite( @@ -74,6 +96,17 @@ private JsonCipherSuites() { null, "AES 256 GCM", "SHA384")), + Map.entry( + "TLS_ASCONAEAD128_SHA256", + new JsonCipherSuite( + "TLS_ASCONAEAD128_SHA256", + null, + null, + new String[] {"0x00", "0x6F"}, + "ASCONAEAD128", + "SHA256", + null, + null)), Map.entry( "TLS_CHACHA20_POLY1305_SHA256", new JsonCipherSuite( diff --git a/mapper/src/main/java/com/ibm/mapper/model/algorithms/SPHINCSPlus.java b/mapper/src/main/java/com/ibm/mapper/model/algorithms/SPHINCSPlus.java index 29a92f845..dc8a01e76 100644 --- a/mapper/src/main/java/com/ibm/mapper/model/algorithms/SPHINCSPlus.java +++ b/mapper/src/main/java/com/ibm/mapper/model/algorithms/SPHINCSPlus.java @@ -20,9 +20,12 @@ package com.ibm.mapper.model.algorithms; import com.ibm.mapper.model.Algorithm; +import com.ibm.mapper.model.INode; import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.ParameterSetIdentifier; import com.ibm.mapper.model.Signature; import com.ibm.mapper.utils.DetectionLocation; +import java.util.Optional; import javax.annotation.Nonnull; /** @@ -52,9 +55,12 @@ public class SPHINCSPlus extends Algorithm implements Signature { @Override public @Nonnull String asString() { - return this.hasChildOfType(MessageDigest.class) - .map(node -> this.name + node.asString()) - .orElse(this.name); + StringBuilder builtName = new StringBuilder(this.name); + Optional parameterSetIdentifier = this.hasChildOfType(ParameterSetIdentifier.class); + parameterSetIdentifier.ifPresent(node -> builtName.append("-").append(node.asString())); + this.hasChildOfType(MessageDigest.class) + .ifPresent(node -> builtName.append(node.asString())); + return builtName.toString(); } public SPHINCSPlus(@Nonnull DetectionLocation detectionLocation) { @@ -65,4 +71,17 @@ public SPHINCSPlus(MessageDigest messageDigest) { this(messageDigest.getDetectionContext()); this.put(messageDigest); } + + /** + * Constructs an SLH-DSA node carrying a FIPS 205 parameter set identifier, e.g. {@code + * "SHA2-128s"} or {@code "SHAKE-256f"}, so that {@link #asString()} yields {@code + * "SLH-DSA-SHA2-128s"}. Used by the C# {@code SlhDsaAlgorithm}-parameterized detection rules + * (see {@code DotNetSlhDsa}), which can recover the exact parameter set from the enum member + * name at the call site. + */ + public SPHINCSPlus( + @Nonnull String parameterSetIdentifier, @Nonnull DetectionLocation detectionLocation) { + this(detectionLocation); + this.put(new ParameterSetIdentifier(parameterSetIdentifier, detectionLocation)); + } } From 513772a366ad5e004abacd4c7e4aff349a9cdca0 Mon Sep 17 00:00:00 2001 From: Fynn Thierling Date: Fri, 21 Aug 2026 08:08:41 +0200 Subject: [PATCH 06/10] complete first try for whole library coverage, this still needs verification and testing Signed-off-by: Fynn Thierling --- .../rules/detection/dotnet/DotNetAES.java | 90 +++++++++++++++++++ .../rules/detection/dotnet/DotNetDSA.java | 42 +++++++-- .../detection/dotnet/DotNetKeyDerivation.java | 68 +++++++++++++- .../dotnet/DotNetRfc2898DeriveBytes.java | 86 +++++++++++++++++- .../dotnet/DotNetX25519DiffieHellman.java | 77 +++++++++++++--- .../translator/CSharpTranslator.java | 18 ++++ .../contexts/CSharpKeyContextTranslator.java | 23 ++++- .../dotnet/DotNetAESComprehensiveTestFile.cs | 44 +++++++++ .../dotnet/DotNetDSAComprehensiveTestFile.cs | 22 ++++- .../dotnet/DotNetKeyDerivationTestFile.cs | 8 ++ .../DotNetRfc2898DeriveBytesTestFile.cs | 21 +++++ .../detection/dotnet/DotNetSHATestFile.cs | 6 ++ .../DotNetX25519DiffieHellmanTestFile.cs | 17 ++++ .../dotnet/DotNetAESComprehensiveTest.java | 16 ++++ .../dotnet/DotNetDSAComprehensiveTest.java | 26 ++++-- .../dotnet/DotNetKeyDerivationTest.java | 44 +++++++++ .../dotnet/DotNetRfc2898DeriveBytesTest.java | 42 +++++++-- .../rules/detection/dotnet/DotNetSHATest.java | 44 +++++++++ .../dotnet/DotNetX25519DiffieHellmanTest.java | 31 ++++--- 19 files changed, 672 insertions(+), 53 deletions(-) diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java index 337743adc..e3a17fe55 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetAES.java @@ -488,6 +488,91 @@ private DotNetAES() { .inBundle(() -> "DotNet") .withoutDependingDetectionRules(); + // ========================================================================= + // EncryptKeyWrapPadded / DecryptKeyWrapPadded / TryDecryptKeyWrapPadded rules + // RFC 5649 AES Key Wrap with Padding — declared on Aes (not SymmetricAlgorithm), + // introduced in .NET 10.0. Unlike EncryptCbc/EncryptEcb/EncryptCfb, this algorithm has + // no CipherMode/PaddingMode concept, so findings are generic Encrypt/Decrypt (same + // modeling as AesGcm/AesCcm below) rather than Mode/Padding-tagged. + // + // Verified overloads (Microsoft Learn, Aes class, net-10.0/net-11.0): + // EncryptKeyWrapPadded(byte[] plaintext) — 1 param + // EncryptKeyWrapPadded(ReadOnlySpan plaintext) — 1 param + // (same arity as above; the engine cannot distinguish by type, so one rule + // covers both overloads) + // EncryptKeyWrapPadded(ReadOnlySpan plaintext, Span destination) — 2 params + // DecryptKeyWrapPadded(byte[] ciphertext) — 1 param + // DecryptKeyWrapPadded(ReadOnlySpan ciphertext) — 1 param + // DecryptKeyWrapPadded(ReadOnlySpan ciphertext, Span destination) — 2 params + // TryDecryptKeyWrapPadded(ReadOnlySpan ciphertext, Span destination, + // out int bytesWritten) — 3 params + // There is no TryEncryptKeyWrapPadded overload (confirmed 404 on Microsoft Learn). + // ========================================================================= + + // aes.EncryptKeyWrapPadded(plaintext) + private static final IDetectionRule AES_ENCRYPT_KEY_WRAP_PADDED_1 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptKeyWrapPadded") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withMethodParameter(MethodMatcher.ANY) // plaintext + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // aes.EncryptKeyWrapPadded(plaintext, destination) + private static final IDetectionRule AES_ENCRYPT_KEY_WRAP_PADDED_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("EncryptKeyWrapPadded") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) + .withMethodParameter(MethodMatcher.ANY) // plaintext + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // aes.DecryptKeyWrapPadded(ciphertext) + private static final IDetectionRule AES_DECRYPT_KEY_WRAP_PADDED_1 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptKeyWrapPadded") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // aes.DecryptKeyWrapPadded(ciphertext, destination) + private static final IDetectionRule AES_DECRYPT_KEY_WRAP_PADDED_2 = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("DecryptKeyWrapPadded") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // aes.TryDecryptKeyWrapPadded(ciphertext, destination, out bytesWritten) + private static final IDetectionRule AES_TRY_DECRYPT_KEY_WRAP_PADDED = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("TryDecryptKeyWrapPadded") + .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) + .withMethodParameter(MethodMatcher.ANY) // ciphertext + .withMethodParameter(MethodMatcher.ANY) // destination buffer + .withMethodParameter(MethodMatcher.ANY) // out bytesWritten + .buildForContext(new CipherContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + // ========================================================================= // Key / IV generation rules // ========================================================================= @@ -549,6 +634,11 @@ private DotNetAES() { AES_TRY_DECRYPT_ECB, AES_TRY_ENCRYPT_CFB, AES_TRY_DECRYPT_CFB, + AES_ENCRYPT_KEY_WRAP_PADDED_1, + AES_ENCRYPT_KEY_WRAP_PADDED_2, + AES_DECRYPT_KEY_WRAP_PADDED_1, + AES_DECRYPT_KEY_WRAP_PADDED_2, + AES_TRY_DECRYPT_KEY_WRAP_PADDED, AES_GENERATE_KEY, AES_GENERATE_IV); diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSA.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSA.java index 44c0bb6a1..12507348f 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSA.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSA.java @@ -48,10 +48,12 @@ * * *

Architecture: all methods inherited from {@code AsymmetricAlgorithm} / {@code DSA} (KeySize - * property, CreateSignature, VerifySignature, SignData, VerifyData, Try* variants, etc.) are - * expressed as depending rules attached to each primary creation rule. The detection - * engine tracks the variable and fires these rules on every matching method call, regardless of the - * concrete DSA subclass. + * property, CreateSignature, VerifySignature, SignData, VerifyData, Try* variants, etc.), as well + * as the legacy {@code SignHash}/{@code VerifyHash} methods that exist only on {@code + * DSACryptoServiceProvider} (not on the abstract {@code DSA} base class; {@code DSA} has no {@code + * TrySignHash}), are expressed as depending rules attached to each primary creation rule. + * The detection engine tracks the variable and fires these rules on every matching method call, + * regardless of the concrete DSA subclass. */ @SuppressWarnings("java:S1192") public final class DotNetDSA { @@ -155,6 +157,34 @@ private DotNetDSA() { .inBundle(() -> "DotNet") .withoutDependingDetectionRules(); + // dsa.SignHash(hash, hashAlgorithmName) — legacy CSP-era method, only real on + // DSACryptoServiceProvider (not present on the abstract DSA base class), mirroring + // RSA_SIGN_HASH in DotNetRSA.java. Note that unlike RSA, DSA has no TrySignHash. + private static final IDetectionRule DSA_SIGN_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("SignHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.SIGN)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // dsa.VerifyHash(hash, hashAlgorithmName, signature) — legacy CSP-era method, only real + // on DSACryptoServiceProvider (not present on the abstract DSA base class), mirroring + // RSA_VERIFY_HASH in DotNetRSA.java. + private static final IDetectionRule DSA_VERIFY_HASH = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("VerifyHash") + .shouldBeDetectedAs(new SignatureActionFactory<>(SignatureAction.Action.VERIFY)) + .withAnyParameters() + .buildForContext(new SignatureContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + // ========================================================================= // Aggregated depending-rule list // ========================================================================= @@ -168,7 +198,9 @@ private DotNetDSA() { DSA_SIGN_DATA, DSA_TRY_SIGN_DATA, DSA_VERIFY_SIGNATURE, - DSA_VERIFY_DATA); + DSA_VERIFY_DATA, + DSA_SIGN_HASH, + DSA_VERIFY_HASH); // ========================================================================= // Primary creation rules diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivation.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivation.java index f8eccb88f..542f714a8 100644 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivation.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivation.java @@ -21,7 +21,10 @@ import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.engine.model.context.DigestContext; import com.ibm.engine.model.context.KeyContext; +import com.ibm.engine.model.factory.AlgorithmFactory; +import com.ibm.engine.model.factory.IterationCountFactory; import com.ibm.engine.model.factory.ValueActionFactory; import com.ibm.engine.rule.IDetectionRule; import com.ibm.engine.rule.builder.DetectionRuleBuilder; @@ -96,6 +99,38 @@ * withAnyParameters()} call site). Not a gap in coverage of the class's cryptographic identity or * of the "a key was derived" signal — only a granularity choice, called out here rather than * decided silently. + * + *

Modeling decision — {@code PasswordDeriveBytes}'s {@code HashName}/{@code IterationCount}/ + * {@code Salt} property setters: unlike {@code HMAC.Key} (see {@code DotNetHMAC}'s "Known gap" + * javadoc, {@code byte[]}-typed, never a literal in realistic code), {@code HashName} ({@code + * string}) and {@code IterationCount} ({@code int}) are verified (learn.microsoft.com, + * PasswordDeriveBytes class reference) to be plain {@code get; set;} properties of literal-friendly + * types — structurally identical to {@code Aes.KeySize}/{@code Aes.Mode} in {@link DotNetAES}, + * which are modeled as synthetic {@code set_X(literal)} depending rules. {@code pdb.IterationCount + * = 100000;} and {@code pdb.HashName = "SHA256";} are realistic, common patterns (raising the + * iteration count / picking a stronger digest are the two knobs this legacy PBKDF1 API exposes), so + * both are modeled below: {@code PDB_SET_ITERATION_COUNT} reuses {@code IterationCountFactory} (the + * same factory already used by {@code GoCryptoPBKDF2} for the equivalent Go PBKDF2 {@code iter} + * parameter — see {@code IterationCountFactory}/{@code GoKeyContextTranslator}), translated to the + * mapper's {@code NumberOfIterations} node. {@code PDB_SET_HASH_NAME} reuses {@code + * AlgorithmFactory} (already used elsewhere in this module for {@code + * HashAlgorithm.Create(string)}, see {@code DotNetAlgorithmFactory}) under a {@code DigestContext} + * rather than {@code KeyContext} — a depending rule is free to declare any context via {@code + * buildForContext(...)}; dispatch in {@code CSharpTranslator} is by each finding's own declared + * context, not its parent's, and this exact cross-context nesting (a {@code DigestContext} child + * rule attached under a {@code KeyContext} parent) is already precedented by the Go module (see + * {@code GoCryptoPBKDF2}'s {@code KEY_STDLIB} rule, which attaches {@code GoCryptoHash.rules()} — + * all {@code DigestContext}-based — as depending rules of a {@code KeyContext} PBKDF2 rule). This + * lets {@code CSharpDigestContextTranslator} resolve the captured hash-name string (e.g. {@code + * "SHA256"} -> {@code SHA2(256)}) with its existing, already-tested {@code Algorithm} branch, + * with no new translation code needed. + * + *

{@code Salt} ({@code byte[]}), by contrast, is deliberately not modeled: it is + * structurally identical to the excluded {@code HMAC.Key} case above (a byte-array-typed property + * that is realistically always assigned from a variable, e.g. {@code pdb.Salt = saltBytes;}, never + * a literal the ANTLR4 engine can read), so recording only that "a Salt was set" (no value) would + * require the same unprecedented valueless-marker mechanism rejected for {@code HMAC.Key} — this is + * an intentional, documented exclusion, not an oversight. */ @SuppressWarnings("java:S1192") public final class DotNetKeyDerivation { @@ -220,8 +255,39 @@ private DotNetKeyDerivation() { .inBundle(() -> "DotNet") .withoutDependingDetectionRules(); + // pdb.IterationCount = 100000 → synthetic set_IterationCount(100000) + // (see class javadoc "Modeling decision" for why this is captured, unlike HMAC.Key/PDB's Salt) + private static final IDetectionRule PDB_SET_ITERATION_COUNT = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_IterationCount") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new IterationCountFactory<>()) + .buildForContext(new KeyContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // pdb.HashName = "SHA256" → synthetic set_HashName("SHA256") + // Uses DigestContext (not KeyContext) so CSharpDigestContextTranslator's existing + // Algorithm branch resolves the digest name — see class javadoc "Modeling decision". + private static final IDetectionRule PDB_SET_HASH_NAME = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("set_HashName") + .withMethodParameter(MethodMatcher.ANY) + .shouldBeDetectedAs(new AlgorithmFactory<>()) + .buildForContext(new DigestContext()) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + private static final List> PDB_DEPENDING_RULES = - List.of(PDB_GET_BYTES, PDB_CRYPT_DERIVE_KEY); + List.of( + PDB_GET_BYTES, + PDB_CRYPT_DERIVE_KEY, + PDB_SET_ITERATION_COUNT, + PDB_SET_HASH_NAME); // new PasswordDeriveBytes(password, salt[, hashName, iterations][, cspParams]) — 8 // constructor overloads (password as string or byte[]; optional hashName/iterations; diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRfc2898DeriveBytes.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRfc2898DeriveBytes.java index 0bb0822ed..82ac414f5 100755 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRfc2898DeriveBytes.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRfc2898DeriveBytes.java @@ -19,6 +19,7 @@ */ package com.ibm.plugin.rules.detection.dotnet; +import com.ibm.engine.detection.MethodMatcher; import com.ibm.engine.language.csharp.tree.CSharpTree; import com.ibm.engine.model.context.KeyContext; import com.ibm.engine.model.factory.ValueActionFactory; @@ -36,7 +37,37 @@ *

    *
  • {@code new Rfc2898DeriveBytes(password, salt, iterations)} — SHA-1 default *
  • {@code new Rfc2898DeriveBytes(password, salt, iterations, hashAlgorithm)} — explicit hash + *
  • {@code Rfc2898DeriveBytes.Pbkdf2(...)} — static one-shot PBKDF2 (added in .NET 8; as of + * net-10.0/net-11.0 this is the recommended replacement for all 8 constructors above, which + * are now marked {@code Obsolete} per the official API reference) + *
  • {@code instance.GetBytes(int)} — pull more derived-key bytes from a constructed instance + *
  • {@code instance.CryptDeriveKey(string, string, int, byte[])} — CAPI-style key derivation + * from a constructed instance (also {@code Obsolete} per the reference, but still valid, + * detectable legacy source, same as the constructors) *
+ * + *

Modeling decision — {@code Pbkdf2(...)} as a top-level rule, not a depending rule: all + * 6 {@code Pbkdf2} overloads (per the official API reference: {@code Pbkdf2(byte[], byte[], int, + * HashAlgorithmName, int)}, {@code Pbkdf2(ReadOnlySpan, ReadOnlySpan, int, + * HashAlgorithmName, int)}, {@code Pbkdf2(ReadOnlySpan, ReadOnlySpan, Span, int, + * HashAlgorithmName)}, {@code Pbkdf2(ReadOnlySpan, ReadOnlySpan, int, + * HashAlgorithmName, int)}, {@code Pbkdf2(ReadOnlySpan, ReadOnlySpan, Span, int, + * HashAlgorithmName)}, {@code Pbkdf2(string, byte[], int, HashAlgorithmName, int)}) are {@code + * static} and self-contained: a single call is both the "creation" and the "operation" at once, + * exactly like {@code HKDF.Extract}/{@code Expand}/{@code DeriveKey} in {@link DotNetKeyDerivation} + * (see that class's javadoc "Modeling decision" for the full rationale). There is no object + * instance to track between a "creation" and a later "operation" step, so the Batch 3 + * creation-rule-with-depending-operations pattern does not apply here; the call is captured + * directly as a {@code ValueActionFactory<>("PBKDF2")} under the same {@code KeyContext} "kind" + * ({@code "KDF"}) as the constructor rule below, so both map to the identical {@code PBKDF2} mapper + * model node. + * + *

All 6 overloads happen to share the same arity (5 parameters each), so — as with every other + * file in this rule set — the ANTLR4-based C# engine's inability to resolve parameter types (see + * {@code CSharpLanguageTranslation}) is moot here even at the arity level: a single {@code + * withAnyParameters()} rule covers every overload without needing to disambiguate by parameter + * count. The {@code HashAlgorithmName} parameter is not decoded into a digest child node, + * consistent with the constructor rule and with {@code DotNetKeyDerivation}'s HKDF rules. */ @SuppressWarnings("java:S1192") public final class DotNetRfc2898DeriveBytes { @@ -45,6 +76,44 @@ private DotNetRfc2898DeriveBytes() { // nothing } + // ========================================================================= + // Instance operation depending rules, reusing the Batch 3 KeyDerivation pattern + // (see DotNetKeyDerivation's PasswordDeriveBytes section, which models the exact + // same two operation shapes for its sibling legacy KDF class). + // ========================================================================= + + // instance.GetBytes(cb) — pull more pseudo-random derived-key bytes + private static final IDetectionRule RFC2898_GET_BYTES = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("GetBytes") + .shouldBeDetectedAs(new ValueActionFactory<>("GetBytes")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_RFC2898_GET_BYTES"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + // instance.CryptDeriveKey(algName, algHashName, keySize, rgbIV) + private static final IDetectionRule RFC2898_CRYPT_DERIVE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes(MethodMatcher.ANY) + .forMethods("CryptDeriveKey") + .shouldBeDetectedAs(new ValueActionFactory<>("CryptDeriveKey")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF_RFC2898_CRYPT_DERIVE_KEY"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); + + private static final List> RFC2898_DEPENDING_RULES = + List.of(RFC2898_GET_BYTES, RFC2898_CRYPT_DERIVE_KEY); + + // ========================================================================= + // Primary rules + // ========================================================================= + + // new Rfc2898DeriveBytes(...) — all 8 constructor overloads, collapsed via withAnyParameters() private static final IDetectionRule RFC2898 = new DetectionRuleBuilder() .createDetectionRule() @@ -54,10 +123,23 @@ private DotNetRfc2898DeriveBytes() { .withAnyParameters() .buildForContext(new KeyContext(Map.of("kind", "KDF"))) .inBundle(() -> "DotNet") - .withDependingDetectionRules(List.of()); + .withDependingDetectionRules(RFC2898_DEPENDING_RULES); + + // Rfc2898DeriveBytes.Pbkdf2(...) — static one-shot, all 6 overloads (see class javadoc + // "Modeling decision"). + private static final IDetectionRule RFC2898_PBKDF2_STATIC = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("Rfc2898DeriveBytes") + .forMethods("Pbkdf2") + .shouldBeDetectedAs(new ValueActionFactory<>("PBKDF2")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "KDF"))) + .inBundle(() -> "DotNet") + .withoutDependingDetectionRules(); @Nonnull public static List> rules() { - return List.of(RFC2898); + return List.of(RFC2898, RFC2898_PBKDF2_STATIC); } } diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellman.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellman.java index cf7b4bdfc..feb35f5f6 100644 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellman.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellman.java @@ -48,12 +48,16 @@ * *

    *
  • {@code X25519DiffieHellman} — abstract base. Verified to have no {@code Create()} - * factory (unlike {@code ECDiffieHellman}); the only way to obtain a fresh instance is the - * static factory {@code X25519DiffieHellman.GenerateKey()} (confirmed via the dedicated - * method-reference page: {@code public static X25519DiffieHellman GenerateKey()}). Its - * constructor is {@code protected X25519DiffieHellman()} (confirmed via the dedicated - * constructor page), so the class cannot be instantiated directly — only through {@code - * GenerateKey()} or one of the two concrete subclasses below. + * factory (unlike {@code ECDiffieHellman}); fresh instances are obtained only through the + * static factories {@code X25519DiffieHellman.GenerateKey()} (confirmed via the dedicated + * method-reference page: {@code public static X25519DiffieHellman GenerateKey()}), {@code + * ImportPrivateKey(byte[]/ReadOnlySpan)}, or {@code + * ImportPublicKey(byte[]/ReadOnlySpan)} (both confirmed via their own dedicated + * method-reference pages, each declared directly on {@code X25519DiffieHellman} itself, each + * returning a new {@code X25519DiffieHellman}). Its constructor is {@code protected + * X25519DiffieHellman()} (confirmed via the dedicated constructor page), so the class cannot + * be instantiated directly — only through one of those static factories or one of the two + * concrete subclasses below. *
  • {@code X25519DiffieHellmanCng} — CNG-backed implementation. Single constructor {@code * X25519DiffieHellmanCng(CngKey)} (wraps an existing key). *
  • {@code X25519DiffieHellmanOpenSsl} — OpenSSL-backed implementation. Single constructor @@ -94,6 +98,21 @@ * by the JCA {@code XDH}/{@code X25519} key-agreement translation and by the Go {@code crypto/ecdh} * curve translation), so it is reused as-is here; no new mapper model class was needed. * + *

    {@code ImportPrivateKey}/{@code ImportPublicKey} — modeled as primary creation rules, not + * skipped: confirmed via the dedicated method-reference pages ({@code + * X25519DiffieHellman.ImportPrivateKey} / {@code X25519DiffieHellman.ImportPublicKey}, {@code + * learn.microsoft.com}, checked 2026-08-21) that both are declared directly on {@code + * X25519DiffieHellman} itself (not on a subclass) as {@code public static X25519DiffieHellman + * ImportPrivateKey(byte[])} / {@code ImportPrivateKey(ReadOnlySpan)}, and {@code public + * static X25519DiffieHellman ImportPublicKey(byte[])} / {@code ImportPublicKey(ReadOnlySpan)} + * — two overloads each, both returning a brand-new {@code X25519DiffieHellman} instance built + * directly from raw 32-byte key material. This is structurally identical to {@code + * MLKem.ImportDecapsulationKey}/{@code ImportEncapsulationKey} in {@code DotNetMLKem.java} (both + * static factories that are themselves the only way, besides {@code GenerateKey}, to obtain an + * instance) — the two overloads per method are collapsed with a single {@code withAnyParameters()} + * rule per method name, following the same ANTLR4-cannot-resolve-parameter-types rationale used + * throughout this file. + * *

    Known gaps (same reasoning as {@code DotNetECDiffieHellman.java}): * *

      @@ -101,11 +120,10 @@ * modeled as a depending rule, mirroring the {@code PublicKey} property gap documented in * {@code DotNetECDiffieHellman.java} for the same underlying reason: these calls carry no * additional cryptographic information beyond "an X25519 key exists" (already captured by the - * primary creation rule), and speculatively wiring up the full family of {@code Import} / - * {@code Export} / {@code TryExport} methods (PKCS8, SPKI, PEM, encrypted-PKCS8 — none of - * which are X25519-specific) would add many rules without adding any new detectable - * cryptographic fact. // TODO: revisit if a future need arises to track key material - * export/import as its own finding. + * primary creation rule), and speculatively wiring up the remaining family of {@code Export} + * / {@code TryExport} methods (PKCS8, SPKI, PEM, encrypted-PKCS8) would add many rules + * without adding any new detectable cryptographic fact. // TODO: revisit if a future need + * arises to track key material export as its own finding. *
    • {@code X25519DiffieHellmanCng.GetKey()} and {@code * X25519DiffieHellmanOpenSsl.DuplicateKeyHandle()} — these return the underlying platform key * handle ({@code CngKey} / {@code SafeEvpPKeyHandle}), not a new cryptographic fact; skipped @@ -161,6 +179,36 @@ private DotNetX25519DiffieHellman() { .inBundle(() -> "DotNet") .withDependingDetectionRules(X25519_DEPENDING_RULES); + // X25519DiffieHellman.ImportPrivateKey(source) — static factory, builds a new instance + // directly from raw 32-byte private key material. Collapses both overloads (byte[] / + // ReadOnlySpan) with withAnyParameters(), mirroring MLKEM_IMPORT_DECAPSULATION_KEY in + // DotNetMLKem.java. + private static final IDetectionRule X25519_IMPORT_PRIVATE_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("X25519DiffieHellman") + .forMethods("ImportPrivateKey") + .shouldBeDetectedAs(new ValueActionFactory<>("X25519")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "X25519"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(X25519_DEPENDING_RULES); + + // X25519DiffieHellman.ImportPublicKey(source) — static factory, builds a new instance + // directly from raw 32-byte public key material. Collapses both overloads (byte[] / + // ReadOnlySpan) with withAnyParameters(), mirroring MLKEM_IMPORT_ENCAPSULATION_KEY in + // DotNetMLKem.java. + private static final IDetectionRule X25519_IMPORT_PUBLIC_KEY = + new DetectionRuleBuilder() + .createDetectionRule() + .forObjectTypes("X25519DiffieHellman") + .forMethods("ImportPublicKey") + .shouldBeDetectedAs(new ValueActionFactory<>("X25519")) + .withAnyParameters() + .buildForContext(new KeyContext(Map.of("kind", "X25519"))) + .inBundle(() -> "DotNet") + .withDependingDetectionRules(X25519_DEPENDING_RULES); + // new X25519DiffieHellmanCng(CngKey) — CNG-backed implementation, wraps an existing key. // Uses withAnyParameters() for consistency with ECDH_CNG / AES_CNG_NAMED even though only one // constructor overload exists, to avoid a brittle single-parameter-type assumption. @@ -190,6 +238,11 @@ private DotNetX25519DiffieHellman() { @Nonnull public static List> rules() { - return List.of(X25519_GENERATE_KEY, X25519_CNG, X25519_OPENSSL); + return List.of( + X25519_GENERATE_KEY, + X25519_IMPORT_PRIVATE_KEY, + X25519_IMPORT_PUBLIC_KEY, + X25519_CNG, + X25519_OPENSSL); } } diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java index 97ad85de1..449367b14 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/CSharpTranslator.java @@ -30,6 +30,7 @@ import com.ibm.engine.model.context.DigestContext; import com.ibm.engine.model.context.IDetectionContext; 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.ProtocolContext; @@ -104,6 +105,23 @@ public Optional translate( return Optional.empty(); } + if (detectionValueContext.is(KeyDerivationFunctionContext.class)) { + // No C# rule currently builds a KeyDerivationFunctionContext: unlike the Python + // module (see PycaKDF/PycaKeyDerivationContextTranslator), every KDF rule in this + // module (DotNetKeyDerivation, DotNetRfc2898DeriveBytes; also mirrored by the Java + // BouncyCastle rules in BcDerivationFunction) reuses the generic KeyContext with a + // "kind" discriminator (e.g. "KDF_HKDF", "KDF_SP800108") instead, dispatched in + // CSharpKeyContextTranslator. That already produces the correct KDF-specific model + // nodes (HKDF/PBKDF1/PBKDF2/KDFCounter, which implement KeyDerivationFunction / + // PasswordBasedKeyDerivationFunction in the mapper model), so no information is lost + // by not using this context. This branch is a deliberate, documented no-op — it + // behaves the same as the default fallthrough below, but exists so this gap reads as + // an intentional choice rather than a missed dispatch case if a future C# rule ever + // reaches for KeyDerivationFunctionContext (at which point a real translator, mirroring + // CSharpKeyContextTranslator's "KDF_*" cases, would need to be wired in here). + return Optional.empty(); + } + return Optional.empty(); } 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 6dbb96e5d..ab7e46076 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 @@ -22,6 +22,7 @@ import com.ibm.engine.language.csharp.tree.CSharpTree; import com.ibm.engine.model.Algorithm; import com.ibm.engine.model.IValue; +import com.ibm.engine.model.IterationCount; import com.ibm.engine.model.KeyAction; import com.ibm.engine.model.KeySize; import com.ibm.engine.model.ParameterIdentifier; @@ -32,6 +33,7 @@ import com.ibm.mapper.IContextTranslation; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyLength; +import com.ibm.mapper.model.NumberOfIterations; import com.ibm.mapper.model.ParameterSetIdentifier; import com.ibm.mapper.model.algorithms.DSA; import com.ibm.mapper.model.algorithms.ECDH; @@ -124,10 +126,16 @@ public final class CSharpKeyContextTranslator implements IContextTranslation Optional.of(new PBKDF1(detectionLocation)); // Instance derive-operations on an already-identified KDF object // (SP800108HmacCounterKdf.DeriveKey, PasswordDeriveBytes.GetBytes/ - // CryptDeriveKey): reuses the same generic KeyDerivation functionality node as - // the ECDiffieHellman derive operations below (Batch 3 pattern), captured as a - // child of the already-typed KDF algorithm node rather than folded into it. - case "KDF_SP800108_DERIVE_KEY", "KDF_PDB_GET_BYTES", "KDF_PDB_CRYPT_DERIVE_KEY" -> + // CryptDeriveKey, and Rfc2898DeriveBytes.GetBytes/CryptDeriveKey — see + // DotNetRfc2898DeriveBytes.java): reuses the same generic KeyDerivation + // functionality node as the ECDiffieHellman derive operations below (Batch 3 + // pattern), captured as a child of the already-typed KDF algorithm node rather + // than folded into it. + case "KDF_SP800108_DERIVE_KEY", + "KDF_PDB_GET_BYTES", + "KDF_PDB_CRYPT_DERIVE_KEY", + "KDF_RFC2898_GET_BYTES", + "KDF_RFC2898_CRYPT_DERIVE_KEY" -> Optional.of(new KeyDerivation(detectionLocation)); // ECDiffieHellman key-derivation operations (DotNetECDiffieHellman.java): // no typed CipherAction.Action fits "derive a key", so each operation is @@ -153,6 +161,13 @@ public final class CSharpKeyContextTranslator implements IContextTranslation keySize) { return Optional.of(new KeyLength(keySize.getValue(), detectionLocation)); + } else if (value instanceof IterationCount iterationCount) { + // From set_IterationCount property setter (PasswordDeriveBytes.IterationCount — + // DotNetKeyDerivation.java): mirrors the Go module's identical IterationCount -> + // NumberOfIterations mapping (see GoKeyContextTranslator), the only other place in + // this codebase that produces an IterationCount value under a KeyContext today. + return Optional.of( + new NumberOfIterations(iterationCount.getValue(), detectionLocation)); } else if (value instanceof ParameterIdentifier parameterIdentifier && detectionContext instanceof DetectionContext context && "KEM".equals(context.get("kind").orElse(""))) { diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetAESComprehensiveTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetAESComprehensiveTestFile.cs index c24f86475..4ef62b151 100644 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetAESComprehensiveTestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetAESComprehensiveTestFile.cs @@ -454,4 +454,48 @@ public void TestAesCcmFullFlow() byte[] aad = new byte[8]; aesCcm.Encrypt(nonce, plaintext, ciphertext, tag, aad); } + + // ------------------------------------------------------------------------- + // Section 11: EncryptKeyWrapPadded / DecryptKeyWrapPadded / + // TryDecryptKeyWrapPadded (RFC 5649 AES Key Wrap with Padding, .NET 10.0+) + // ------------------------------------------------------------------------- + + public void TestEncryptKeyWrapPadded1() + { + var aes = Aes.Create(); + byte[] plaintext = new byte[32]; + byte[] wrapped = aes.EncryptKeyWrapPadded(plaintext); + } + + public void TestEncryptKeyWrapPadded2() + { + var aes = Aes.Create(); + byte[] plaintext = new byte[32]; + byte[] destination = new byte[40]; + aes.EncryptKeyWrapPadded(plaintext, destination); + } + + public void TestDecryptKeyWrapPadded1() + { + var aes = Aes.Create(); + byte[] ciphertext = new byte[40]; + byte[] unwrapped = aes.DecryptKeyWrapPadded(ciphertext); + } + + public void TestDecryptKeyWrapPadded2() + { + var aes = Aes.Create(); + byte[] ciphertext = new byte[40]; + byte[] destination = new byte[32]; + int written = aes.DecryptKeyWrapPadded(ciphertext, destination); + } + + public void TestTryDecryptKeyWrapPadded() + { + var aes = Aes.Create(); + byte[] ciphertext = new byte[40]; + byte[] destination = new byte[32]; + int bytesWritten; + aes.TryDecryptKeyWrapPadded(ciphertext, destination, out bytesWritten); + } } diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetDSAComprehensiveTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetDSAComprehensiveTestFile.cs index 6a7329371..8a870a576 100644 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetDSAComprehensiveTestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetDSAComprehensiveTestFile.cs @@ -151,7 +151,27 @@ public void TestVerifyData() } // ------------------------------------------------------------------------- - // Section 7: Combined usage patterns (real-world scenarios) + // Section 7: SignHash / VerifyHash (legacy CSP-era methods; only real on + // DSACryptoServiceProvider, not on the abstract DSA base class) + // ------------------------------------------------------------------------- + + public void TestSignHash() + { + var dsa = new DSACryptoServiceProvider(); + byte[] hash = new byte[20]; + byte[] signature = dsa.SignHash(hash, "SHA1"); + } + + public void TestVerifyHash() + { + var dsa = new DSACryptoServiceProvider(); + byte[] hash = new byte[20]; + byte[] signature = new byte[40]; + bool valid = dsa.VerifyHash(hash, "SHA1", signature); + } + + // ------------------------------------------------------------------------- + // Section 8: Combined usage patterns (real-world scenarios) // Demonstrates that depending rules fire correctly for ALL derived classes. // ------------------------------------------------------------------------- diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetKeyDerivationTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetKeyDerivationTestFile.cs index 534e2bfe1..200c555d9 100644 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetKeyDerivationTestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetKeyDerivationTestFile.cs @@ -80,4 +80,12 @@ public void TestPasswordDeriveBytesCryptDeriveKey() byte[] iv = new byte[8]; byte[] key = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, iv); } + + public void TestPasswordDeriveBytesProperties() + { + byte[] salt = new byte[16]; + var pdb = new PasswordDeriveBytes("password", salt); + pdb.IterationCount = 100000; + pdb.HashName = "SHA256"; + } } diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetRfc2898DeriveBytesTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetRfc2898DeriveBytesTestFile.cs index eefd47425..ad065467b 100755 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetRfc2898DeriveBytesTestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetRfc2898DeriveBytesTestFile.cs @@ -3,4 +3,25 @@ public class DotNetRfc2898DeriveBytesTest { public void TestPbkdf2() { var kdf = new Rfc2898DeriveBytes("password", new byte[16], 10000, HashAlgorithmName.SHA256); // Noncompliant } + + public void TestPbkdf2GetBytes() { + var kdf = new Rfc2898DeriveBytes("password", new byte[16], 10000, HashAlgorithmName.SHA256); // Noncompliant + byte[] derived = kdf.GetBytes(32); + } + + public void TestPbkdf2CryptDeriveKey() { + var kdf = new Rfc2898DeriveBytes("password", new byte[16], 10000, HashAlgorithmName.SHA256); // Noncompliant + byte[] iv = new byte[8]; + byte[] key = kdf.CryptDeriveKey("TripleDES", "SHA1", 192, iv); + } + + public void TestPbkdf2StaticByteArray() { + byte[] key = Rfc2898DeriveBytes.Pbkdf2( + new byte[8], new byte[16], 10000, HashAlgorithmName.SHA256, 32); // Noncompliant + } + + public void TestPbkdf2StaticString() { + byte[] key = Rfc2898DeriveBytes.Pbkdf2( + "password", new byte[16], 10000, HashAlgorithmName.SHA256, 32); // Noncompliant + } } diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetSHATestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetSHATestFile.cs index 44065051f..30fc8c085 100755 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetSHATestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetSHATestFile.cs @@ -25,4 +25,10 @@ public class DotNetSHATest { public void TestRipemd160Create() { var h = RIPEMD160.Create(); } // Noncompliant public void TestRipemd160CreateNamed() { var h = RIPEMD160.Create("RIPEMD160"); } // Noncompliant public void TestRipemd160Managed() { var h = new RIPEMD160Managed(); } // Noncompliant + + public void TestMd5Csp() { var h = new MD5CryptoServiceProvider(); } // Noncompliant + public void TestSha1Managed() { var h = new SHA1Managed(); } // Noncompliant + public void TestSha256Managed() { var h = new SHA256Managed(); } // Noncompliant + public void TestSha384Managed() { var h = new SHA384Managed(); } // Noncompliant + public void TestSha512Managed() { var h = new SHA512Managed(); } // Noncompliant } diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetX25519DiffieHellmanTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetX25519DiffieHellmanTestFile.cs index 2b6ca6791..eb1da1fdc 100644 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetX25519DiffieHellmanTestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetX25519DiffieHellmanTestFile.cs @@ -17,6 +17,11 @@ * Known gap: reading/exporting the public key (e.g. ExportPublicKey()) cannot be meaningfully * modeled as a depending rule (see DotNetX25519DiffieHellman.java class javadoc) — no test * attempts it. + * + * ImportPrivateKey/ImportPublicKey are static factories on X25519DiffieHellman itself (each with + * byte[] and ReadOnlySpan overloads collapsed into one rule) that build a brand-new instance + * directly from raw 32-byte key material — modeled as primary creation rules, mirroring + * MLKem.ImportDecapsulationKey/ImportEncapsulationKey in DotNetMLKem.java. */ using System.Security.Cryptography; @@ -44,6 +49,18 @@ public void TestX25519OpenSsl() var x25519 = new X25519DiffieHellmanOpenSsl(handle); } + public void TestX25519ImportPrivateKeyByteArray() + { + byte[] privateKey = new byte[32]; + var x25519 = X25519DiffieHellman.ImportPrivateKey(privateKey); + } + + public void TestX25519ImportPublicKeyByteArray() + { + byte[] publicKey = new byte[32]; + var x25519 = X25519DiffieHellman.ImportPublicKey(publicKey); + } + // ------------------------------------------------------------------------- // Section 2: DeriveRawSecretAgreement operation (all overloads collapse to one rule) // ------------------------------------------------------------------------- diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java index 9f68e2fe0..1bb96ae15 100644 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetAESComprehensiveTest.java @@ -133,6 +133,14 @@ * 50 TestAesEcbEncrypt → AES-ECB-None * 51 TestAesGcmFullFlow → AES + Encrypt * 52 TestAesCcmFullFlow → AES + Encrypt + * + * Section 11 – EncryptKeyWrapPadded / DecryptKeyWrapPadded / + * TryDecryptKeyWrapPadded (RFC 5649 AES Key Wrap with Padding, findings 53–57): + * 53 TestEncryptKeyWrapPadded1 → AES + Encrypt + * 54 TestEncryptKeyWrapPadded2 → AES + Encrypt + * 55 TestDecryptKeyWrapPadded1 → AES + Decrypt + * 56 TestDecryptKeyWrapPadded2 → AES + Decrypt + * 57 TestTryDecryptKeyWrapPadded → AES + Decrypt * */ class DotNetAESComprehensiveTest extends TestBase { @@ -325,6 +333,14 @@ public void asserts( case 51 -> assertEncryptFindings(detectionStore, nodes, "AES"); case 52 -> assertEncryptFindings(detectionStore, nodes, "AES"); + // ----------------------------------------------------------------- + // Section 11: EncryptKeyWrapPadded / DecryptKeyWrapPadded / + // TryDecryptKeyWrapPadded — generic Encrypt/Decrypt findings (no + // Mode/Padding concept for RFC 5649 Key Wrap with Padding) + // ----------------------------------------------------------------- + case 53, 54 -> assertEncryptFindings(detectionStore, nodes, "AES"); + case 55, 56, 57 -> assertDecryptFindings(detectionStore, nodes, "AES"); + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); } } diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSAComprehensiveTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSAComprehensiveTest.java index 1dcc4e588..72b692b17 100644 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSAComprehensiveTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetDSAComprehensiveTest.java @@ -81,10 +81,14 @@ * Section 6 – VerifyData (finding 16): * 16 TestVerifyData → DSA + Verify * - * Section 7 – combined usage patterns (findings 17–19): - * 17 TestDsaCngFullFlow → DSA-2048 + Sign - * 18 TestDsaCspVerifyFlow → DSA + Verify - * 19 TestDsaOpenSslSignFlow → DSA + Sign + * Section 7 – SignHash / VerifyHash (findings 17–18): + * 17 TestSignHash → DSA + Sign + * 18 TestVerifyHash → DSA + Verify + * + * Section 8 – combined usage patterns (findings 19–21): + * 19 TestDsaCngFullFlow → DSA-2048 + Sign + * 20 TestDsaCspVerifyFlow → DSA + Verify + * 21 TestDsaOpenSslSignFlow → DSA + Sign * */ class DotNetDSAComprehensiveTest extends TestBase { @@ -145,16 +149,22 @@ public void asserts( case 16 -> assertVerify(node); // ----------------------------------------------------------------- - // Section 7: combined usage patterns + // Section 7: SignHash / VerifyHash + // ----------------------------------------------------------------- + case 17 -> assertSign(node); + case 18 -> assertVerify(node); + + // ----------------------------------------------------------------- + // Section 8: combined usage patterns // ----------------------------------------------------------------- - case 17 -> { + case 19 -> { assertThat(node.asString()).isEqualTo("DSA-2048"); assertThat(node.getChildren().get(KeyLength.class)).isNotNull(); assertThat(node.getChildren().get(KeyLength.class).asString()).isEqualTo("2048"); assertThat(node.getChildren().get(Sign.class)).isNotNull(); } - case 18 -> assertVerify(node); - case 19 -> assertSign(node); + case 20 -> assertVerify(node); + case 21 -> assertSign(node); default -> throw new IllegalStateException("Unexpected findingId: " + findingId); } diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivationTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivationTest.java index 9405bfe4d..79ab41716 100644 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivationTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetKeyDerivationTest.java @@ -26,11 +26,15 @@ 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.Algorithm; import com.ibm.engine.model.IValue; +import com.ibm.engine.model.IterationCount; import com.ibm.engine.model.ValueAction; import com.ibm.engine.model.context.KeyContext; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyDerivationFunction; +import com.ibm.mapper.model.MessageDigest; +import com.ibm.mapper.model.NumberOfIterations; import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; import com.ibm.mapper.model.functionality.KeyDerivation; import com.ibm.plugin.CSharpVerifier; @@ -70,6 +74,9 @@ * + KeyDerivation "KEYDERIVATION" child * 6 TestPasswordDeriveBytesCryptDeriveKey → PasswordBasedKeyDerivationFunction "PBKDF1" * + KeyDerivation "KEYDERIVATION" child + * 7 TestPasswordDeriveBytesProperties → PasswordBasedKeyDerivationFunction "PBKDF1-SHA-256" + * + NumberOfIterations "100000" child + * + MessageDigest "SHA-256" child (from set_HashName) * */ class DotNetKeyDerivationTest extends TestBase { @@ -131,6 +138,43 @@ public void asserts( assertThat(node.asString()).isEqualTo("PBKDF1"); assertKeyDerivationChild(detectionStore, node); } + case 7 -> { + /* + * TestPasswordDeriveBytesProperties: pdb.IterationCount = 100000; + * pdb.HashName = "SHA256"; — property-setter depending rules + */ + assertThat(primary.asString()).isEqualTo("PBKDF1"); + assertThat(node.getKind()).isEqualTo(PasswordBasedKeyDerivationFunction.class); + + // Depending rule: set_IterationCount detected IterationCount(100000) + DetectionStore + iterationStore = + getStoreOfValueType( + IterationCount.class, detectionStore.getChildren()); + assertThat(iterationStore).isNotNull(); + assertThat(iterationStore.getDetectionValues()).hasSize(1); + assertThat(iterationStore.getDetectionValues().get(0).asString()) + .isEqualTo("100000"); + + // Depending rule: set_HashName detected Algorithm("SHA256") + DetectionStore + hashNameStore = + getStoreOfValueType(Algorithm.class, detectionStore.getChildren()); + assertThat(hashNameStore).isNotNull(); + assertThat(hashNameStore.getDetectionValues()).hasSize(1); + assertThat(hashNameStore.getDetectionValues().get(0).asString()) + .isEqualTo("SHA256"); + + // Translation: PBKDF1 node with NumberOfIterations + MessageDigest children + // (PBKDF1#asString() appends the digest child's name, per its own source) + assertThat(node.asString()).isEqualTo("PBKDF1-SHA-256"); + INode iterations = node.getChildren().get(NumberOfIterations.class); + assertThat(iterations).isNotNull(); + assertThat(iterations.asString()).isEqualTo("100000"); + INode digest = node.getChildren().get(MessageDigest.class); + assertThat(digest).isNotNull(); + assertThat(digest.asString()).isEqualTo("SHA-256"); + } default -> throw new IllegalStateException("Unexpected findingId: " + findingId); } diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRfc2898DeriveBytesTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRfc2898DeriveBytesTest.java index 4cf6473a4..c62f7d860 100755 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRfc2898DeriveBytesTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRfc2898DeriveBytesTest.java @@ -31,6 +31,7 @@ import com.ibm.engine.model.context.KeyContext; import com.ibm.mapper.model.INode; import com.ibm.mapper.model.PasswordBasedKeyDerivationFunction; +import com.ibm.mapper.model.functionality.KeyDerivation; import com.ibm.plugin.CSharpVerifier; import com.ibm.plugin.TestBase; import java.util.List; @@ -51,14 +52,43 @@ public void asserts( DetectionStore detectionStore, @Nonnull List nodes) { - assertThat(detectionStore.getDetectionValues()).hasSize(1); assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(KeyContext.class); - IValue value0 = detectionStore.getDetectionValues().get(0); - assertThat(value0).isInstanceOf(ValueAction.class); - assertThat(value0.asString()).isEqualTo("PBKDF2"); + assertThat(detectionStore.getDetectionValues()).hasSize(1); + IValue primary = detectionStore.getDetectionValues().get(0); + assertThat(primary).isInstanceOf(ValueAction.class); + assertThat(primary.asString()).isEqualTo("PBKDF2"); assertThat(nodes).hasSize(1); - assertThat(nodes.get(0).getKind()).isEqualTo(PasswordBasedKeyDerivationFunction.class); - assertThat(nodes.get(0).asString()).isEqualTo("PBKDF2"); + INode node = nodes.get(0); + assertThat(node.getKind()).isEqualTo(PasswordBasedKeyDerivationFunction.class); + assertThat(node.asString()).isEqualTo("PBKDF2"); + + switch (findingId) { + // 0: new Rfc2898DeriveBytes(...) alone — no operations called afterwards + case 0 -> assertThat(node.getChildren()).isEmpty(); + // 1: new Rfc2898DeriveBytes(...) followed by kdf.GetBytes(32) + case 1 -> assertKeyDerivationChild(detectionStore, node); + // 2: new Rfc2898DeriveBytes(...) followed by kdf.CryptDeriveKey(...) + case 2 -> assertKeyDerivationChild(detectionStore, node); + // 3: Rfc2898DeriveBytes.Pbkdf2(byte[], byte[], int, HashAlgorithmName, int) — static, + // self-contained, no depending operations + case 3 -> assertThat(node.getChildren()).isEmpty(); + // 4: Rfc2898DeriveBytes.Pbkdf2(string, byte[], int, HashAlgorithmName, int) — static + case 4 -> assertThat(node.getChildren()).isEmpty(); + default -> throw new IllegalStateException("Unexpected findingId: " + findingId); + } + } + + private void assertKeyDerivationChild( + @Nonnull DetectionStore store, + @Nonnull INode node) { + DetectionStore deriveStore = + getStoreOfValueType(ValueAction.class, store.getChildren()); + assertThat(deriveStore).isNotNull(); + assertThat(deriveStore.getDetectionValues()).hasSize(1); + + assertThat(node.getChildren().get(KeyDerivation.class)).isNotNull(); + assertThat(node.getChildren().get(KeyDerivation.class).asString()) + .isEqualTo("KEYDERIVATION"); } } diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHATest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHATest.java index ce2e05530..70e4abe4a 100755 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHATest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetSHATest.java @@ -189,6 +189,50 @@ public void asserts( assertThat(digestSize).isNotNull(); assertThat(digestSize.asString()).isEqualTo("160"); } + // new MD5CryptoServiceProvider(): legacy CAPI implementation, same translation as + // MD5.Create() (case 4). + case 22 -> { + assertThat(value0.asString()).isEqualTo("MD5"); + assertThat(node.asString()).isEqualTo("MD5"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("128"); + } + // new SHA1Managed(): pure-managed implementation, same translation as SHA1.Create(). + case 23 -> { + assertThat(value0.asString()).isEqualTo("SHA1"); + assertThat(node.asString()).isEqualTo("SHA-1"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("160"); + } + // new SHA256Managed(): pure-managed implementation, same translation as + // SHA256.Create(). + case 24 -> { + assertThat(value0.asString()).isEqualTo("SHA256"); + assertThat(node.asString()).isEqualTo("SHA-256"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("256"); + } + // new SHA384Managed(): pure-managed implementation, same translation as + // SHA384.Create(). + case 25 -> { + assertThat(value0.asString()).isEqualTo("SHA384"); + assertThat(node.asString()).isEqualTo("SHA-384"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("384"); + } + // new SHA512Managed(): pure-managed implementation, same translation as + // SHA512.Create(). + case 26 -> { + assertThat(value0.asString()).isEqualTo("SHA512"); + assertThat(node.asString()).isEqualTo("SHA-512"); + INode digestSize = node.getChildren().get(DigestSize.class); + assertThat(digestSize).isNotNull(); + assertThat(digestSize.asString()).isEqualTo("512"); + } default -> throw new IllegalStateException("Unexpected findingId: " + findingId); } } diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellmanTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellmanTest.java index c59d91dda..e26c5d940 100644 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellmanTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetX25519DiffieHellmanTest.java @@ -62,18 +62,20 @@ *

      Finding mapping (one finding per test method in DotNetX25519DiffieHellmanTestFile.cs): * *

      - * Section 1 – factory method / constructors (findings 0–2):
      - *   0 TestX25519GenerateKey   → X25519
      - *   1 TestX25519Cng           → X25519
      - *   2 TestX25519OpenSsl       → X25519
      + * Section 1 – factory method / constructors / static Import* factories (findings 0–4):
      + *   0 TestX25519GenerateKey              → X25519
      + *   1 TestX25519Cng                      → X25519
      + *   2 TestX25519OpenSsl                  → X25519
      + *   3 TestX25519ImportPrivateKeyByteArray → X25519
      + *   4 TestX25519ImportPublicKeyByteArray  → X25519
        *
      - * Section 2 – DeriveRawSecretAgreement operation (findings 3–4):
      - *   3 TestDeriveRawSecretAgreementByteArray  → X25519 + Generate child
      - *   4 TestDeriveRawSecretAgreementOtherParty → X25519 + Generate child
      + * Section 2 – DeriveRawSecretAgreement operation (findings 5–6):
      + *   5 TestDeriveRawSecretAgreementByteArray  → X25519 + Generate child
      + *   6 TestDeriveRawSecretAgreementOtherParty → X25519 + Generate child
        *
      - * Section 3 – combined usage patterns (findings 5–6):
      - *   5 TestX25519CngDeriveFlow       → X25519 + Generate child
      - *   6 TestX25519OpenSslDeriveFlow   → X25519 + Generate child
      + * Section 3 – combined usage patterns (findings 7–8):
      + *   7 TestX25519CngDeriveFlow       → X25519 + Generate child
      + *   8 TestX25519OpenSslDeriveFlow   → X25519 + Generate child
        * 
      */ class DotNetX25519DiffieHellmanTest extends TestBase { @@ -114,21 +116,22 @@ public void asserts( switch (findingId) { // ----------------------------------------------------------------- - // Section 1: simple constructors — only X25519, no extra children + // Section 1: simple constructors / static Import* factories — only X25519, no extra + // children // ----------------------------------------------------------------- - case 0, 1, 2 -> { + case 0, 1, 2, 3, 4 -> { // node.asString() already asserted to be "x25519" above } // ----------------------------------------------------------------- // Section 2: DeriveRawSecretAgreement operation // ----------------------------------------------------------------- - case 3, 4 -> assertRawSecretAgreement(detectionStore, node); + case 5, 6 -> assertRawSecretAgreement(detectionStore, node); // ----------------------------------------------------------------- // Section 3: combined usage patterns // ----------------------------------------------------------------- - case 5, 6 -> assertRawSecretAgreement(detectionStore, node); + case 7, 8 -> assertRawSecretAgreement(detectionStore, node); default -> throw new IllegalStateException("Unexpected findingId: " + findingId); } From 4b4a9a370f067b548bdcbc1560e39fcb6faa96de Mon Sep 17 00:00:00 2001 From: Fynn Thierling Date: Fri, 21 Aug 2026 08:27:15 +0200 Subject: [PATCH 07/10] fix in random number generator test-file Signed-off-by: Fynn Thierling --- .../dotnet/DotNetRandomNumberGenerator.java | 92 ++++++++++--------- .../DotNetRandomNumberGeneratorTestFile.cs | 17 ++-- .../DotNetRandomNumberGeneratorTest.java | 28 +++--- 3 files changed, 73 insertions(+), 64 deletions(-) diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGenerator.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGenerator.java index dcceafa4e..784fb4ea1 100644 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGenerator.java +++ b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGenerator.java @@ -38,17 +38,30 @@ *
        *
      • {@code RandomNumberGenerator.Create()} / {@code Create(string)} — instance factory (the * {@code Create(string)} overload is marked {@code Obsolete} in recent .NET versions but - * remains valid, detectable legacy source). The returned instance exposes the abstract - * instance methods {@code GetBytes(byte[])}, {@code GetBytes(byte[], int, int)} and {@code - * GetNonZeroBytes(byte[])}, tracked as depending rules. - *
      • {@code RandomNumberGenerator}'s static-only members — the actual focus of this - * batch, not just {@code Create()}: {@code Fill(Span)}, {@code GetBytes(int)} / {@code - * GetBytes(Span)}, {@code GetHexString(int, bool)} / {@code GetHexString(Span, - * bool)}, {@code GetInt32(int)} / {@code GetInt32(int, int)}, {@code - * GetItems(ReadOnlySpan, int)} / {@code GetItems(ReadOnlySpan, Span)}, {@code - * GetNonZeroBytes(Span)}, {@code GetString(ReadOnlySpan, int)}, and {@code - * Shuffle(Span)}. "Using the static members of this class is the preferred way to - * generate random values" per the official documentation. + * remains valid, detectable legacy source). The returned instance exposes the {@code + * virtual}/{@code abstract} instance methods {@code GetBytes(byte[])}, {@code + * GetBytes(byte[], int, int)}, {@code GetBytes(Span)}, {@code GetNonZeroBytes(byte[])} + * and {@code GetNonZeroBytes(Span)}, tracked as depending rules. Verified directly + * against the .NET 10 compiler (not just the doc prose), because the doc's "When overridden + * in a derived class" phrasing is the only reliable signal distinguishing these from the + * static overloads below — {@code GetBytes(Span)} and {@code + * GetNonZeroBytes(Span)} both fail to compile as {@code + * RandomNumberGenerator.GetBytes(span)}/{@code RandomNumberGenerator.GetNonZeroBytes(span)} + * (CS0120: "An object reference is required for the non-static ... method"), proving they are + * {@code virtual} instance members despite their {@code Span}-based signature superficially + * resembling the genuinely-static {@code Span}-based overloads of {@code Fill}/{@code + * GetHexString}/{@code GetItems}/{@code GetString}. An earlier version of this file + * incorrectly classified both as static-only; that was caught by compiling every {@code .cs} + * test fixture in this module against the real Roslyn compiler (see {@code + * DotNetRandomNumberGeneratorTestFile.cs}). + *
      • {@code RandomNumberGenerator}'s genuinely static-only members (each empirically + * confirmed to compile as a bare {@code RandomNumberGenerator.Method(...)} call with no + * instance): {@code Fill(Span)}, {@code GetBytes(int)}, {@code GetHexString(int, bool)} + * / {@code GetHexString(Span, bool)}, {@code GetInt32(int)} / {@code GetInt32(int, + * int)}, {@code GetItems(ReadOnlySpan, int)} / {@code GetItems(ReadOnlySpan, + * Span)}, {@code GetString(ReadOnlySpan, int)}, and {@code Shuffle(Span)}. + * "Using the static members of this class is the preferred way to generate random values" per + * the official documentation. *
      • {@code RNGCryptoServiceProvider} — legacy CSP-backed implementation ({@code Obsolete} since * .NET 6, still valid, detectable legacy source). Four constructor overloads ({@code ()}, * {@code (byte[])}, {@code (CspParameters)}, {@code (string)}), with instance {@code @@ -77,17 +90,18 @@ * *

        Modeling decision — static self-contained calls vs. instance depending-rule operations: * mirrors the {@code HKDF} vs. {@code SP800108HmacCounterKdf} distinction established in {@link - * DotNetKeyDerivation}. {@code RandomNumberGenerator}'s static methods ({@code Fill}, {@code - * GetBytes(int)}/{@code GetBytes(Span)}, {@code GetHexString}, {@code GetInt32}, {@code - * GetItems}, {@code GetNonZeroBytes(Span)}, {@code GetString}, {@code Shuffle}) are each a - * complete, self-contained "the platform CSPRNG was used" event with no instance to track — they - * are top-level rules mapping directly to the {@code NATIVEPRNG} algorithm identity, exactly like - * {@code HKDF.Extract}/{@code Expand}/{@code DeriveKey}. Where an actual instance *is* tracked - * ({@code RandomNumberGenerator.Create()}'s or {@code RNGCryptoServiceProvider}'s instance {@code - * GetBytes}/{@code GetNonZeroBytes}), those calls are depending rules attached to the creation - * rule, translated to the generic {@code Generate} functionality node (mirrors {@code - * AES_GENERATE_IV} in {@link DotNetAES}, which also has no more specific {@code CipherAction} - * available) as a child of the already-identified {@code NATIVEPRNG} algorithm node. + * DotNetKeyDerivation}. {@code RandomNumberGenerator}'s genuinely static methods ({@code Fill}, + * {@code GetBytes(int)}, {@code GetHexString}, {@code GetInt32}, {@code GetItems}, {@code + * GetString}, {@code Shuffle}) are each a complete, self-contained "the platform CSPRNG was used" + * event with no instance to track — they are top-level rules mapping directly to the {@code + * NATIVEPRNG} algorithm identity, exactly like {@code HKDF.Extract}/{@code Expand}/{@code + * DeriveKey}. Where an actual instance *is* tracked ({@code RandomNumberGenerator.Create()}'s or + * {@code RNGCryptoServiceProvider}'s instance {@code GetBytes}/{@code GetNonZeroBytes}, including + * their {@code Span} overloads — see the "Verified directly against the .NET 10 compiler" + * note above), those calls are depending rules attached to the creation rule, translated to the + * generic {@code Generate} functionality node (mirrors {@code AES_GENERATE_IV} in {@link + * DotNetAES}, which also has no more specific {@code CipherAction} available) as a child of the + * already-identified {@code NATIVEPRNG} algorithm node. * *

        As with every other file in this rule set, the ANTLR4-based C# engine cannot resolve parameter * types (see {@code CSharpLanguageTranslation}) or values held in variables (see {@code @@ -95,16 +109,6 @@ * differ by {@code byte[]} vs. {@code Span}/{@code ReadOnlySpan}, by an optional trailing * {@code bool}/output-buffer parameter, or by generic type argument are collapsed into a single * {@code withAnyParameters()} rule per method name. - * - *

        Known gap — {@code RandomNumberGenerator.Fill}/{@code GetNonZeroBytes(Span)}/etc. - * called through a base-class-typed local variable that is itself the result of {@code - * RandomNumberGenerator.Create()}: only the truly-static call form ({@code - * RandomNumberGenerator.Fill(...)}, receiver text literally {@code "RandomNumberGenerator"}) is - * covered by the top-level static rules in this file. {@code Fill} and the static {@code - * GetBytes(Span)}/{@code GetNonZeroBytes(Span)} overloads are not also - * addressable as instance methods on a concrete {@code RandomNumberGenerator} object in real .NET - * (they are {@code static} only), so this is not an actual coverage gap — it is called out here - * only because it might look, at a glance, like a missing depending rule. */ @SuppressWarnings("java:S1192") public final class DotNetRandomNumberGenerator { @@ -194,7 +198,9 @@ private DotNetRandomNumberGenerator() { .inBundle(() -> "DotNet") .withoutDependingDetectionRules(); - // RandomNumberGenerator.GetBytes(int count) / GetBytes(Span data) + // RandomNumberGenerator.GetBytes(int count) — the only genuinely static GetBytes overload + // (GetBytes(byte[]) and GetBytes(Span) are both virtual instance methods, covered by + // RNG_INSTANCE_GET_BYTES above — see class javadoc for the compiler-verified proof). private static final IDetectionRule RNG_STATIC_GET_BYTES = new DetectionRuleBuilder() .createDetectionRule() @@ -245,17 +251,14 @@ private DotNetRandomNumberGenerator() { .inBundle(() -> "DotNet") .withoutDependingDetectionRules(); - // RandomNumberGenerator.GetNonZeroBytes(Span data) — static overload - private static final IDetectionRule RNG_STATIC_GET_NON_ZERO_BYTES = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("RandomNumberGenerator") - .forMethods("GetNonZeroBytes") - .shouldBeDetectedAs(new ValueActionFactory<>("NATIVEPRNG")) - .withAnyParameters() - .buildForContext(new PRNGContext()) - .inBundle(() -> "DotNet") - .withoutDependingDetectionRules(); + // NOTE: there is no static GetNonZeroBytes rule here — GetNonZeroBytes(byte[]) *and* + // GetNonZeroBytes(Span) are BOTH virtual instance methods (confirmed by compiling + // `RandomNumberGenerator.GetNonZeroBytes(data)` with the real .NET 10 compiler: CS0120, + // "An object reference is required for the non-static ... method"). Unlike GetBytes, which has + // a genuinely static GetBytes(int) overload alongside its instance overloads, GetNonZeroBytes + // has no static form at all. Both GetNonZeroBytes overloads are already covered by the + // RNG_INSTANCE_GET_NON_ZERO_BYTES depending rule above (withAnyParameters() does not + // distinguish byte[] from Span). // RandomNumberGenerator.GetString(ReadOnlySpan choices, int length) private static final IDetectionRule RNG_GET_STRING = @@ -313,7 +316,6 @@ public static List> rules() { RNG_GET_HEX_STRING, RNG_GET_INT32, RNG_GET_ITEMS, - RNG_STATIC_GET_NON_ZERO_BYTES, RNG_GET_STRING, RNG_SHUFFLE, RNG_CSP_CTOR); diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetRandomNumberGeneratorTestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetRandomNumberGeneratorTestFile.cs index 13ac8c7b0..7d4d470e3 100644 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetRandomNumberGeneratorTestFile.cs +++ b/csharp/src/test/files/rules/detection/dotnet/DotNetRandomNumberGeneratorTestFile.cs @@ -4,9 +4,16 @@ * * Covers: * - RandomNumberGenerator.Create() / Create(string) + instance GetBytes/GetNonZeroBytes - * - RandomNumberGenerator's static-only methods: Fill, GetBytes(int)/(Span), - * GetHexString, GetInt32, GetItems, GetNonZeroBytes(Span), GetString, Shuffle + * (including their Span overloads, which are virtual instance methods, not static -- + * confirmed by compiling this exact file against the real .NET 10 compiler) + * - RandomNumberGenerator's genuinely static-only methods: Fill, GetBytes(int), + * GetHexString, GetInt32, GetItems, GetString, Shuffle * - RNGCryptoServiceProvider constructor overloads + instance GetBytes/GetNonZeroBytes + * + * Note: GetNonZeroBytes has NO static overload at all (unlike GetBytes, which has a genuinely + * static GetBytes(int) alongside its instance overloads) -- `RandomNumberGenerator.GetNonZeroBytes(data)` + * does not compile (CS0120). There is therefore no "static GetNonZeroBytes" test case in this file; + * TestCreateAndGetNonZeroBytes below already exercises the (only) instance form. */ using System.Security.Cryptography; @@ -74,12 +81,6 @@ public void TestStaticGetItems() int[] items = RandomNumberGenerator.GetItems(choices, 3); } - public void TestStaticGetNonZeroBytes() - { - byte[] data = new byte[32]; - RandomNumberGenerator.GetNonZeroBytes(data); - } - public void TestStaticGetString() { string alphabet = "abcdefghijklmnopqrstuvwxyz"; diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGeneratorTest.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGeneratorTest.java index 4a7a86c60..7e5e08296 100644 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGeneratorTest.java +++ b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetRandomNumberGeneratorTest.java @@ -47,9 +47,10 @@ *

          *
        • {@code RandomNumberGenerator.Create()} / {@code Create(string)} + instance {@code * GetBytes}/{@code GetNonZeroBytes} - *
        • {@code RandomNumberGenerator}'s static-only methods: {@code Fill}, {@code GetBytes(int)}, - * {@code GetHexString}, {@code GetInt32} (both overloads), {@code GetItems}, {@code - * GetNonZeroBytes(Span)}, {@code GetString}, {@code Shuffle} + *
        • {@code RandomNumberGenerator}'s genuinely static-only methods (verified against the real + * .NET compiler -- {@code GetNonZeroBytes} has no static overload at all): {@code Fill}, + * {@code GetBytes(int)}, {@code GetHexString}, {@code GetInt32} (both overloads), {@code + * GetItems}, {@code GetString}, {@code Shuffle} *
        • {@code RNGCryptoServiceProvider} constructor overloads + instance {@code GetBytes}/{@code * GetNonZeroBytes} *
        @@ -69,13 +70,18 @@ * 6 TestStaticGetInt32 → PseudorandomNumberGenerator "NATIVEPRNG" (no children) * 7 TestStaticGetInt32Range → PseudorandomNumberGenerator "NATIVEPRNG" (no children) * 8 TestStaticGetItems → PseudorandomNumberGenerator "NATIVEPRNG" (no children) - * 9 TestStaticGetNonZeroBytes → PseudorandomNumberGenerator "NATIVEPRNG" (no children) - * 10 TestStaticGetString → PseudorandomNumberGenerator "NATIVEPRNG" (no children) - * 11 TestStaticShuffle → PseudorandomNumberGenerator "NATIVEPRNG" (no children) - * 12 TestRngCspGetBytes → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child - * 13 TestRngCspGetNonZeroBytes → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child - * 14 TestRngCspWithSeed → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child + * 9 TestStaticGetString → PseudorandomNumberGenerator "NATIVEPRNG" (no children) + * 10 TestStaticShuffle → PseudorandomNumberGenerator "NATIVEPRNG" (no children) + * 11 TestRngCspGetBytes → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child + * 12 TestRngCspGetNonZeroBytes → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child + * 13 TestRngCspWithSeed → PseudorandomNumberGenerator "NATIVEPRNG" + Generate "GENERATE" child * + * + *

        Note: {@code GetNonZeroBytes} has no static overload at all (unlike {@code GetBytes}, which + * has a genuinely static {@code GetBytes(int)} alongside its instance overloads) -- verified by + * compiling {@code RandomNumberGenerator.GetNonZeroBytes(data)} against the real .NET 10 compiler + * (CS0120). There is therefore no "static GetNonZeroBytes" finding; finding 1 ( {@code + * TestCreateAndGetNonZeroBytes}) already exercises the only instance form. */ class DotNetRandomNumberGeneratorTest extends TestBase { @@ -114,12 +120,12 @@ public void asserts( // Section 2: RandomNumberGenerator static-only methods — self-contained, // no depending rules, no children. // ----------------------------------------------------------------- - case 3, 4, 5, 6, 7, 8, 9, 10, 11 -> assertThat(node.getChildren()).isEmpty(); + case 3, 4, 5, 6, 7, 8, 9, 10 -> assertThat(node.getChildren()).isEmpty(); // ----------------------------------------------------------------- // Section 3: RNGCryptoServiceProvider // ----------------------------------------------------------------- - case 12, 13, 14 -> assertGenerateChild(detectionStore, node); + case 11, 12, 13 -> assertGenerateChild(detectionStore, node); default -> throw new IllegalStateException("Unexpected findingId: " + findingId); } From efb154eb10a2d338431d202fe84c8c64bc7eb6a3 Mon Sep 17 00:00:00 2001 From: Fynn Thierling Date: Fri, 21 Aug 2026 09:41:22 +0200 Subject: [PATCH 08/10] load fixed csharp parser Signed-off-by: Fynn Thierling --- engine/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/pom.xml b/engine/pom.xml index 7b772606b..d4a6ed370 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -33,7 +33,7 @@ io.github.fynnth cbomkit-csharp-parser - 0.1.2 + 0.1.3 From 98f829eebc8e3509bf5c62b52d8f7f0ba53068df Mon Sep 17 00:00:00 2001 From: Fynn Thierling Date: Fri, 21 Aug 2026 10:59:30 +0200 Subject: [PATCH 09/10] exclude chacha from this branch Signed-off-by: Fynn Thierling --- .../rules/detection/CSharpDetectionRules.java | 2 - .../dotnet/DotNetChaCha20Poly1305.java | 136 ------------------ .../CSharpCipherContextTranslator.java | 3 - .../dotnet/DotNetChaCha20Poly1305TestFile.cs | 92 ------------ .../dotnet/DotNetChaCha20Poly1305Test.java | 118 --------------- 5 files changed, 351 deletions(-) delete mode 100644 csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305.java delete mode 100644 csharp/src/test/files/rules/detection/dotnet/DotNetChaCha20Poly1305TestFile.cs delete mode 100644 csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305Test.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 5f915a643..b7d1b57ae 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 @@ -23,7 +23,6 @@ import com.ibm.engine.rule.IDetectionRule; import com.ibm.plugin.rules.detection.dotnet.DotNetAES; import com.ibm.plugin.rules.detection.dotnet.DotNetAlgorithmFactory; -import com.ibm.plugin.rules.detection.dotnet.DotNetChaCha20Poly1305; import com.ibm.plugin.rules.detection.dotnet.DotNetDES; import com.ibm.plugin.rules.detection.dotnet.DotNetDSA; import com.ibm.plugin.rules.detection.dotnet.DotNetECDiffieHellman; @@ -59,7 +58,6 @@ private CSharpDetectionRules() { public static List> rules() { return Stream.of( DotNetAES.rules().stream(), - DotNetChaCha20Poly1305.rules().stream(), DotNetDES.rules().stream(), DotNetTripleDES.rules().stream(), DotNetRC2.rules().stream(), diff --git a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305.java b/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305.java deleted file mode 100644 index 756afc73c..000000000 --- a/csharp/src/main/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.detection.MethodMatcher; -import com.ibm.engine.language.csharp.tree.CSharpTree; -import com.ibm.engine.model.CipherAction; -import com.ibm.engine.model.context.CipherContext; -import com.ibm.engine.model.factory.CipherActionFactory; -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 javax.annotation.Nonnull; - -/** - * Detection rules for {@code ChaCha20Poly1305} in System.Security.Cryptography. - * - *

        {@code ChaCha20Poly1305} is a sealed AEAD cipher class (available since .NET 6, platform gated - * by {@code IsSupported}), structurally analogous to {@code AesGcm}/{@code AesCcm} in {@link - * DotNetAES}: it is constructed from a key and exposes {@code Encrypt}/{@code Decrypt} methods - * taking nonce, plaintext/ciphertext, tag and an optional associated-data buffer. There is no class - * hierarchy to cover (the class is {@code sealed}), and no inherited {@code SymmetricAlgorithm} - * surface (property setters, {@code CreateEncryptor}, mode-specific Encrypt/Decrypt, etc.) applies - * here. - * - *

        Constructors covered: - * - *

          - *
        • {@code ChaCha20Poly1305(byte[] key)} - *
        • {@code ChaCha20Poly1305(ReadOnlySpan key)} - *
        - * - * Both take exactly one parameter, so a single rule using {@code withAnyParameters()} covers both - * overloads (parameter types are not resolvable by the engine — see {@code - * CSharpLanguageTranslation}). - * - *

        The static {@code IsSupported} property is a platform-availability check, not - * detection-relevant cryptographic information, and is intentionally not modeled (mirrors how - * KMAC/SHA-3 platform-support properties are ignored elsewhere in this module). - * - *

        Operations covered as depending rules (fired only on a tracked {@code ChaCha20Poly1305} - * variable), mirroring the {@code AesGcm}/{@code AesCcm} pattern in {@link DotNetAES}: - * - *

          - *
        • {@code Encrypt(nonce, plaintext, ciphertext, tag [, associatedData])} — both the {@code - * byte[]} and {@code ReadOnlySpan} overloads always declare all five parameters (the - * last one defaults to {@code null}/{@code default}), but callers may omit the trailing - * associated-data argument at the call site, so {@code withAnyParameters()} is used to match - * both the 4- and 5-argument call shapes, exactly like {@code AesGcm.Encrypt}. - *
        • {@code Decrypt(nonce, ciphertext, tag, plaintext [, associatedData])} — same reasoning. - *
        - * - *

        Known gap: nonce length (fixed at 12 bytes), tag length (fixed at 16 bytes) and the - * associated-data content are not captured as separate values. As with {@code AesGcm}/{@code - * AesCcm}, these arguments are almost always local {@code byte[]} variables (e.g. {@code new - * byte[12]}) rather than literals passed directly into {@code Encrypt}/{@code Decrypt}, and the - * engine cannot resolve values across variable assignments (see {@code CSharpSymbol}). // TODO: - * ChaCha20Poly1305 nonce/tag length capture is not possible with the current engine and is left as - * a known gap, consistent with the same limitation for AesGcm/AesCcm. - */ -public final class DotNetChaCha20Poly1305 { - - private DotNetChaCha20Poly1305() { - // nothing - } - - // ========================================================================= - // ChaCha20Poly1305 AEAD operation rules - // ========================================================================= - - // chaCha20Poly1305.Encrypt(nonce, plaintext, ciphertext, tag [, associatedData]) - private static final IDetectionRule CHACHA20POLY1305_ENCRYPT_OP = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(MethodMatcher.ANY) - .forMethods("Encrypt") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.ENCRYPT)) - .withAnyParameters() - .buildForContext(new CipherContext()) - .inBundle(() -> "DotNet") - .withoutDependingDetectionRules(); - - // chaCha20Poly1305.Decrypt(nonce, ciphertext, tag, plaintext [, associatedData]) - private static final IDetectionRule CHACHA20POLY1305_DECRYPT_OP = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes(MethodMatcher.ANY) - .forMethods("Decrypt") - .shouldBeDetectedAs(new CipherActionFactory<>(CipherAction.Action.DECRYPT)) - .withAnyParameters() - .buildForContext(new CipherContext()) - .inBundle(() -> "DotNet") - .withoutDependingDetectionRules(); - - private static final List> CHACHA20POLY1305_OP_RULES = - List.of(CHACHA20POLY1305_ENCRYPT_OP, CHACHA20POLY1305_DECRYPT_OP); - - // ========================================================================= - // Primary creation rule - // ========================================================================= - - // new ChaCha20Poly1305(key) — AEAD (byte[] or ReadOnlySpan, 1 param) - private static final IDetectionRule CHACHA20_POLY1305 = - new DetectionRuleBuilder() - .createDetectionRule() - .forObjectTypes("ChaCha20Poly1305") - .forMethods("") - .shouldBeDetectedAs(new ValueActionFactory<>("CHACHA20-POLY1305")) - .withAnyParameters() - .buildForContext(new CipherContext()) - .inBundle(() -> "DotNet") - .withDependingDetectionRules(CHACHA20POLY1305_OP_RULES); - - @Nonnull - public static List> rules() { - return List.of(CHACHA20_POLY1305); - } -} diff --git a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java index 21724c80a..ed6f17517 100755 --- a/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java +++ b/csharp/src/main/java/com/ibm/plugin/translation/translator/contexts/CSharpCipherContextTranslator.java @@ -38,7 +38,6 @@ import com.ibm.mapper.model.INode; import com.ibm.mapper.model.KeyLength; import com.ibm.mapper.model.algorithms.AES; -import com.ibm.mapper.model.algorithms.ChaCha20Poly1305; import com.ibm.mapper.model.algorithms.DES; import com.ibm.mapper.model.algorithms.DESede; import com.ibm.mapper.model.algorithms.RC2; @@ -76,8 +75,6 @@ public final class CSharpCipherContextTranslator Optional.of(new DESede(detectionLocation)); case "RSA" -> Optional.of(new RSA(detectionLocation)); case "RC2" -> Optional.of(new RC2(detectionLocation)); - case "CHACHA20-POLY1305" -> - Optional.of(new ChaCha20Poly1305(detectionLocation)); case "GENERATEKEY" -> Optional.of(new KeyGeneration(detectionLocation)); case "GENERATEIV" -> Optional.of(new Generate(detectionLocation)); // DPAPI (System.Security.Cryptography.ProtectedData / ProtectedMemory / diff --git a/csharp/src/test/files/rules/detection/dotnet/DotNetChaCha20Poly1305TestFile.cs b/csharp/src/test/files/rules/detection/dotnet/DotNetChaCha20Poly1305TestFile.cs deleted file mode 100644 index 7b321330d..000000000 --- a/csharp/src/test/files/rules/detection/dotnet/DotNetChaCha20Poly1305TestFile.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Test file for System.Security.Cryptography ChaCha20Poly1305 detection rules. - * - * ChaCha20Poly1305 is a sealed AEAD cipher class, structurally analogous to AesGcm/AesCcm: - * constructed from a key, with Encrypt/Decrypt methods taking nonce, plaintext/ciphertext, - * tag and an optional associated-data buffer. - */ - -using System.Security.Cryptography; - -public class DotNetChaCha20Poly1305Test -{ - // ------------------------------------------------------------------------- - // Constructor - // ------------------------------------------------------------------------- - - public void TestChaCha20Poly1305Ctor() - { - byte[] key = new byte[32]; - var chaCha20Poly1305 = new ChaCha20Poly1305(key); - } - - // ------------------------------------------------------------------------- - // Encrypt (with and without the optional associated-data argument) - // ------------------------------------------------------------------------- - - public void TestChaCha20Poly1305Encrypt() - { - byte[] key = new byte[32]; - var chaCha20Poly1305 = new ChaCha20Poly1305(key); - byte[] nonce = new byte[12]; - byte[] plaintext = new byte[32]; - byte[] ciphertext = new byte[32]; - byte[] tag = new byte[16]; - byte[] associatedData = new byte[8]; - chaCha20Poly1305.Encrypt(nonce, plaintext, ciphertext, tag, associatedData); - } - - public void TestChaCha20Poly1305EncryptNoAad() - { - byte[] key = new byte[32]; - var chaCha20Poly1305 = new ChaCha20Poly1305(key); - byte[] nonce = new byte[12]; - byte[] plaintext = new byte[32]; - byte[] ciphertext = new byte[32]; - byte[] tag = new byte[16]; - chaCha20Poly1305.Encrypt(nonce, plaintext, ciphertext, tag); - } - - // ------------------------------------------------------------------------- - // Decrypt (with and without the optional associated-data argument) - // ------------------------------------------------------------------------- - - public void TestChaCha20Poly1305Decrypt() - { - byte[] key = new byte[32]; - var chaCha20Poly1305 = new ChaCha20Poly1305(key); - byte[] nonce = new byte[12]; - byte[] ciphertext = new byte[32]; - byte[] tag = new byte[16]; - byte[] plaintext = new byte[32]; - byte[] associatedData = new byte[8]; - chaCha20Poly1305.Decrypt(nonce, ciphertext, tag, plaintext, associatedData); - } - - public void TestChaCha20Poly1305DecryptNoAad() - { - byte[] key = new byte[32]; - var chaCha20Poly1305 = new ChaCha20Poly1305(key); - byte[] nonce = new byte[12]; - byte[] ciphertext = new byte[32]; - byte[] tag = new byte[16]; - byte[] plaintext = new byte[32]; - chaCha20Poly1305.Decrypt(nonce, ciphertext, tag, plaintext); - } - - // ------------------------------------------------------------------------- - // Combined usage pattern (real-world scenario) - // ------------------------------------------------------------------------- - - public void TestChaCha20Poly1305FullFlow() - { - byte[] key = new byte[32]; - var chaCha20Poly1305 = new ChaCha20Poly1305(key); - byte[] nonce = new byte[12]; - byte[] plaintext = new byte[32]; - byte[] ciphertext = new byte[32]; - byte[] tag = new byte[16]; - byte[] associatedData = new byte[8]; - chaCha20Poly1305.Encrypt(nonce, plaintext, ciphertext, tag, associatedData); - } -} diff --git a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305Test.java b/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305Test.java deleted file mode 100644 index 2ab00400a..000000000 --- a/csharp/src/test/java/com/ibm/plugin/rules/detection/dotnet/DotNetChaCha20Poly1305Test.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.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.functionality.Decrypt; -import com.ibm.mapper.model.functionality.Encrypt; -import com.ibm.plugin.CSharpVerifier; -import com.ibm.plugin.TestBase; -import java.util.List; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; - -/** - * Test for {@link DotNetChaCha20Poly1305} detection rules. - * - *

        Finding mapping (one finding per test method in DotNetChaCha20Poly1305TestFile.cs): - * - *

        - * 0  TestChaCha20Poly1305Ctor          → ChaCha20-Poly1305
        - * 1  TestChaCha20Poly1305Encrypt       → ChaCha20-Poly1305 + Encrypt
        - * 2  TestChaCha20Poly1305EncryptNoAad  → ChaCha20-Poly1305 + Encrypt
        - * 3  TestChaCha20Poly1305Decrypt       → ChaCha20-Poly1305 + Decrypt
        - * 4  TestChaCha20Poly1305DecryptNoAad  → ChaCha20-Poly1305 + Decrypt
        - * 5  TestChaCha20Poly1305FullFlow      → ChaCha20-Poly1305 + Encrypt
        - * 
        - */ -class DotNetChaCha20Poly1305Test extends TestBase { - - @Test - void test() throws Exception { - CSharpVerifier.verify("rules/detection/dotnet/DotNetChaCha20Poly1305TestFile.cs", this); - } - - @Override - public void asserts( - int findingId, - @Nonnull - DetectionStore - detectionStore, - @Nonnull List nodes) { - - // Every top-level finding must be ChaCha20-Poly1305 - assertThat(detectionStore.getDetectionValueContext()).isInstanceOf(CipherContext.class); - assertThat(detectionStore.getDetectionValues()).hasSize(1); - IValue primary = detectionStore.getDetectionValues().get(0); - assertThat(primary).isInstanceOf(ValueAction.class); - assertThat(primary.asString()).isEqualTo("CHACHA20-POLY1305"); - - switch (findingId) { - case 0 -> { - assertThat(nodes).hasSize(1); - assertThat(nodes.get(0).asString()).isEqualTo("ChaCha20-Poly1305"); - } - case 1, 2, 5 -> assertEncryptFindings(detectionStore, nodes); - case 3, 4 -> assertDecryptFindings(detectionStore, nodes); - default -> throw new IllegalStateException("Unexpected findingId: " + findingId); - } - } - - private void assertEncryptFindings( - @Nonnull DetectionStore store, - @Nonnull List nodes) { - - DetectionStore encryptStore = - getStoreOfValueType(CipherAction.class, store.getChildren()); - assertThat(encryptStore).isNotNull(); - assertThat(encryptStore.getDetectionValues()).hasSize(1); - assertThat(encryptStore.getDetectionValues().get(0).asString()).isEqualTo("ENCRYPT"); - - assertThat(nodes).hasSize(1); - assertThat(nodes.get(0).asString()).isEqualTo("ChaCha20-Poly1305"); - assertThat(nodes.get(0).getChildren().get(Encrypt.class)).isNotNull(); - } - - private void assertDecryptFindings( - @Nonnull DetectionStore store, - @Nonnull List nodes) { - - DetectionStore decryptStore = - getStoreOfValueType(CipherAction.class, store.getChildren()); - assertThat(decryptStore).isNotNull(); - assertThat(decryptStore.getDetectionValues()).hasSize(1); - assertThat(decryptStore.getDetectionValues().get(0).asString()).isEqualTo("DECRYPT"); - - assertThat(nodes).hasSize(1); - assertThat(nodes.get(0).asString()).isEqualTo("ChaCha20-Poly1305"); - assertThat(nodes.get(0).getChildren().get(Decrypt.class)).isNotNull(); - } -} From 82c9848542dbe38aebc29c15a51b7e41cc3c7ca4 Mon Sep 17 00:00:00 2001 From: Fynn Thierling Date: Fri, 21 Aug 2026 11:44:52 +0200 Subject: [PATCH 10/10] revert unrelated cipher-suite data drift picked up by an mvn build during the batch run Signed-off-by: Fynn Thierling --- mapper/ciphersuites.json | 2 +- .../mapper/ssl/json/JsonCipherSuites.java | 33 ------------------- 2 files changed, 1 insertion(+), 34 deletions(-) diff --git a/mapper/ciphersuites.json b/mapper/ciphersuites.json index 9f4583301..44542d946 100644 --- a/mapper/ciphersuites.json +++ b/mapper/ciphersuites.json @@ -1 +1 @@ -{"ciphersuites": [{"TLS_AES_128_CCM_8_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x13", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_CCM_ASCONHASH256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x71", "protocol_version": "TLS", "kex_algorithm": "AES", "auth_algorithm": "128 CCM ASCONHASH256", "enc_algorithm": "", "hash_algorithm": "", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "TLS_AES_128_CCM_SHA256", "hex_byte_1": "0x13", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_GCM_ASCONHASH256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x70", "protocol_version": "TLS", "kex_algorithm": "AES", "auth_algorithm": "128 GCM ASCONHASH256", "enc_algorithm": "", "hash_algorithm": "", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "TLS_AES_128_GCM_SHA256", "hex_byte_1": "0x13", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "TLS_AES_256_GCM_SHA384", "hex_byte_1": "0x13", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_ASCONAEAD128_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x6F", "protocol_version": "TLS", "kex_algorithm": "ASCONAEAD128", "auth_algorithm": "SHA256", "enc_algorithm": "", "hash_algorithm": "", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_CHACHA20_POLY1305_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x13", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x19", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x17", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_3DES_EDE_CBC_SHA1", "openssl_name": "ADH-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x1B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_AES_128_CBC_SHA1", "openssl_name": "ADH-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x34", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_128_CBC_SHA256", "openssl_name": "ADH-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6C", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_128_GCM_SHA256", "openssl_name": "ADH-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA6", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_AES_256_CBC_SHA1", "openssl_name": "ADH-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x3A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_256_CBC_SHA256", "openssl_name": "ADH-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6D", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DH_ANON_AES_256_GCM_SHA384", "openssl_name": "ADH-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA7", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x46", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x47", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_CBC_SHA1", "openssl_name": "ADH-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x46", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_CBC_SHA256", "openssl_name": "ADH-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBF", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x84", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_CBC_SHA1", "openssl_name": "ADH-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x89", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_CBC_SHA256", "openssl_name": "ADH-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC5", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x85", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_RC4_128_MD5": {"gnutls_name": "TLS_DH_ANON_ARCFOUR_128_MD5", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x18", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "ADH-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x9B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0B", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0D", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x30", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x3E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA4", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x36", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x68", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA5", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x58", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x59", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x42", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xBB", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x82", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x85", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC1", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x83", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0C", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x97", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x11", "protocol_version": "TLS EXPORT", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-DSS-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x13", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_AES_128_CBC_SHA1", "openssl_name": "DHE-DSS-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x32", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_128_CBC_SHA256", "openssl_name": "DHE-DSS-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x40", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_128_GCM_SHA256", "openssl_name": "DHE-DSS-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_AES_256_CBC_SHA1", "openssl_name": "DHE-DSS-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x38", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_256_CBC_SHA256", "openssl_name": "DHE-DSS-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6A", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_DSS_AES_256_GCM_SHA384", "openssl_name": "DHE-DSS-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x42", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x56", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x43", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x57", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_CBC_SHA1", "openssl_name": "DHE-DSS-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x44", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-DSS-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBD", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x80", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_CBC_SHA1", "openssl_name": "DHE-DSS-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x87", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_CBC_SHA256", "openssl_name": "DHE-DSS-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x81", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x12", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "DHE-DSS-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x99", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_AES_128_CBC_SHA1", "openssl_name": "DHE-PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x90", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_PSK_AES_128_CBC_SHA256", "openssl_name": "DHE-PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CCM": {"gnutls_name": "TLS_DHE_PSK_AES_128_CCM", "openssl_name": "DHE-PSK-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA6", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_PSK_AES_128_GCM_SHA256", "openssl_name": "DHE-PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_AES_256_CBC_SHA1", "openssl_name": "DHE-PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x91", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_DHE_PSK_AES_256_CBC_SHA384", "openssl_name": "DHE-PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CCM": {"gnutls_name": "TLS_DHE_PSK_AES_256_CCM", "openssl_name": "DHE-PSK-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA7", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_PSK_AES_256_GCM_SHA384", "openssl_name": "DHE-PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x66", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6C", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x67", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x96", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x90", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "DHE-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x97", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x91", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_DHE_PSK_CHACHA20_POLY1305", "openssl_name": "DHE-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA1", "openssl_name": "DHE-PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA256", "openssl_name": "DHE-PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB4", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA384", "openssl_name": "DHE-PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB5", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_DHE_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x8E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x14", "protocol_version": "TLS EXPORT", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-RSA-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x16", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_AES_128_CBC_SHA1", "openssl_name": "DHE-RSA-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x33", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_128_CBC_SHA256", "openssl_name": "DHE-RSA-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x67", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_DHE_RSA_AES_128_CCM", "openssl_name": "DHE-RSA-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_DHE_RSA_AES_128_CCM_8", "openssl_name": "DHE-RSA-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_128_GCM_SHA256", "openssl_name": "DHE-RSA-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x9E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_AES_256_CBC_SHA1", "openssl_name": "DHE-RSA-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x39", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_256_CBC_SHA256", "openssl_name": "DHE-RSA-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6B", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_DHE_RSA_AES_256_CCM", "openssl_name": "DHE-RSA-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_DHE_RSA_AES_256_CCM_8", "openssl_name": "DHE-RSA-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_RSA_AES_256_GCM_SHA384", "openssl_name": "DHE-RSA-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0x9F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x44", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x52", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x45", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x53", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_CBC_SHA1", "openssl_name": "DHE-RSA-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x45", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-RSA-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBE", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7C", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_CBC_SHA1", "openssl_name": "DHE-RSA-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x88", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_CBC_SHA256", "openssl_name": "DHE-RSA-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC4", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_DHE_RSA_CHACHA20_POLY1305", "openssl_name": "DHE-RSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x15", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "DHE-RSA-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x9A", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0E", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x10", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x31", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x3F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA0", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x37", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x69", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA1", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x40", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x54", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x41", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x55", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x43", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xBC", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x86", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC2", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x98", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB2", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB0", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_256_CCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB3", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB1", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_3DES_EDE_CBC_SHA1", "openssl_name": "AECDH-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x17", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_AES_128_CBC_SHA1", "openssl_name": "AECDH-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x18", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_AES_256_CBC_SHA1", "openssl_name": "AECDH-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x19", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDH_ANON_NULL_SHA1", "openssl_name": "AECDH-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x15", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDH_ANON_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x16", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x25", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x26", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4A", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4B", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x74", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x88", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x75", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x89", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_NULL_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x08", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x09", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CBC_SHA256", "openssl_name": "ECDHE-ECDSA-AES128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x23", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CCM", "openssl_name": "ECDHE-ECDSA-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CCM_8", "openssl_name": "ECDHE-ECDSA-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_GCM_SHA256", "openssl_name": "ECDHE-ECDSA-AES128-GCM-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x2B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x0A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CBC_SHA384", "openssl_name": "ECDHE-ECDSA-AES256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x24", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CCM", "openssl_name": "ECDHE-ECDSA-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CCM_8", "openssl_name": "ECDHE-ECDSA-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAF", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_GCM_SHA384", "openssl_name": "ECDHE-ECDSA-AES256-GCM-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x2C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x48", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x49", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5D", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-ECDSA-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x72", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x86", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-ECDSA-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x73", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x87", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CHACHA20_POLY1305", "openssl_name": "ECDHE-ECDSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_NULL_SHA1", "openssl_name": "ECDHE-ECDSA-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x06", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x07", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x34", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_AES_128_CBC_SHA1", "openssl_name": "ECDHE-PSK-AES128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x35", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_AES_128_CBC_SHA256", "openssl_name": "ECDHE-PSK-AES128-CBC-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x37", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_AES_256_CBC_SHA1", "openssl_name": "ECDHE-PSK-AES256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x36", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_AES_256_CBC_SHA384", "openssl_name": "ECDHE-PSK-AES256-CBC-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x38", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x70", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x71", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x9A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x9B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_CHACHA20_POLY1305", "openssl_name": "ECDHE-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA1", "openssl_name": "ECDHE-PSK-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x39", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA256", "openssl_name": "ECDHE-PSK-NULL-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x3A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA384", "openssl_name": "ECDHE-PSK-NULL-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x3B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x33", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-RSA-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x12", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_CBC_SHA1", "openssl_name": "ECDHE-RSA-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x13", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_CBC_SHA256", "openssl_name": "ECDHE-RSA-AES128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x27", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_GCM_SHA256", "openssl_name": "ECDHE-RSA-AES128-GCM-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x2F", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_CBC_SHA1", "openssl_name": "ECDHE-RSA-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x14", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_CBC_SHA384", "openssl_name": "ECDHE-RSA-AES256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x28", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_GCM_SHA384", "openssl_name": "ECDHE-RSA-AES256-GCM-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x30", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x60", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4D", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x61", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-RSA-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x76", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-RSA-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x77", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CHACHA20_POLY1305", "openssl_name": "ECDHE-RSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_RSA_NULL_SHA1", "openssl_name": "ECDHE-RSA-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x10", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_RSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x11", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x29", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x31", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2A", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x32", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x62", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x63", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x78", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8C", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x79", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_NULL_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0B", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0C", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_28147_CNT_IMIT": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "28147 CNT", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x00", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "KUZNYECHIK CTR", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "KUZNYECHIK MGM L", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "KUZNYECHIK MGM S", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_CTR_OMAC": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "MAGMA CTR", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_MGM_L": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "MAGMA MGM L", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_MGM_S": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x06", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "MAGMA MGM S", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x29", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x26", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x2A", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x27", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x2B", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC4_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x28", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_3DES_EDE_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x23", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1F", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_DES_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x22", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1E", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_IDEA_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x25", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "IDEA CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_IDEA_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x21", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "IDEA CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_RC4_128_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x24", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x20", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_NULL_WITH_NULL_NULL": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x00", "protocol_version": "TLS", "kex_algorithm": "NULL", "auth_algorithm": "NULL", "enc_algorithm": "NULL", "hash_algorithm": "NULL", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_DHE_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_DHE_PSK_AES_128_CCM_8", "openssl_name": "DHE-PSK-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_DHE_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_DHE_PSK_AES_256_CCM_8", "openssl_name": "DHE-PSK-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8B", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_PSK_AES_128_CBC_SHA1", "openssl_name": "PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8C", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_PSK_AES_128_CBC_SHA256", "openssl_name": "PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CCM": {"gnutls_name": "TLS_PSK_AES_128_CCM", "openssl_name": "PSK-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA4", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_PSK_AES_128_CCM_8", "openssl_name": "PSK-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_PSK_AES_128_GCM_SHA256", "openssl_name": "PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_PSK_AES_256_CBC_SHA1", "openssl_name": "PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8D", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_PSK_AES_256_CBC_SHA384", "openssl_name": "PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAF", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CCM": {"gnutls_name": "TLS_PSK_AES_256_CCM", "openssl_name": "PSK-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA5", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_PSK_AES_256_CCM_8", "openssl_name": "PSK-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_PSK_AES_256_GCM_SHA384", "openssl_name": "PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x64", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6A", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x65", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6B", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x94", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8E", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x95", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8F", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_PSK_CHACHA20_POLY1305", "openssl_name": "PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_PSK_NULL_SHA1", "openssl_name": "PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2C", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_PSK_NULL_SHA256", "openssl_name": "PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB0", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_PSK_NULL_SHA384", "openssl_name": "PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB1", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x8A", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x08", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x06", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x03", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "RSA-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x93", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_AES_128_CBC_SHA1", "openssl_name": "RSA-PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x94", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_PSK_AES_128_CBC_SHA256", "openssl_name": "RSA-PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB6", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_PSK_AES_128_GCM_SHA256", "openssl_name": "RSA-PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_AES_256_CBC_SHA1", "openssl_name": "RSA-PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x95", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_RSA_PSK_AES_256_CBC_SHA384", "openssl_name": "RSA-PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB7", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_PSK_AES_256_GCM_SHA384", "openssl_name": "RSA-PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x68", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6E", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x69", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6F", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "RSA-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x98", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x92", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "RSA-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x99", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x93", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_RSA_PSK_CHACHA20_POLY1305", "openssl_name": "RSA-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA1", "openssl_name": "RSA-PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2E", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA256", "openssl_name": "RSA-PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB8", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA384", "openssl_name": "RSA-PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB9", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_RSA_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x92", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x0A", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_RSA_AES_128_CBC_SHA1", "openssl_name": "AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2F", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_AES_128_CBC_SHA256", "openssl_name": "AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_RSA_AES_128_CCM", "openssl_name": "AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_RSA_AES_128_CCM_8", "openssl_name": "AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA0", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_AES_128_GCM_SHA256", "openssl_name": "AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x9C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_RSA_AES_256_CBC_SHA1", "openssl_name": "AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x35", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_RSA_AES_256_CBC_SHA256", "openssl_name": "AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_RSA_AES_256_CCM", "openssl_name": "AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_RSA_AES_256_CCM_8", "openssl_name": "AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA1", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_AES_256_GCM_SHA384", "openssl_name": "AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0x9D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x50", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x51", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_RSA_CAMELLIA_128_CBC_SHA1", "openssl_name": "CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x41", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBA", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7A", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_RSA_CAMELLIA_256_CBC_SHA1", "openssl_name": "CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x84", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_256_CBC_SHA256", "openssl_name": "CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC0", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7B", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x09", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_IDEA_CBC_SHA": {"gnutls_name": "", "openssl_name": "IDEA-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x07", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "IDEA CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_MD5": {"gnutls_name": "TLS_RSA_NULL_MD5", "openssl_name": "NULL-MD5", "hex_byte_1": "0x00", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_SHA": {"gnutls_name": "TLS_RSA_NULL_SHA1", "openssl_name": "NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_SHA256": {"gnutls_name": "TLS_RSA_NULL_SHA256", "openssl_name": "NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3B", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_RC4_128_MD5": {"gnutls_name": "TLS_RSA_ARCFOUR_128_MD5", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_RSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x96", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SHA256_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB4", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "SHA256", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SHA384_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB5", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "SHA384", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SM4_CCM_SM3": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC7", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "SM4 CCM", "hash_algorithm": "SM3", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SM4_GCM_SM3": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC6", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "SM4 GCM", "hash_algorithm": "SM3", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-DSS-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1C", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_AES_128_CBC_SHA1", "openssl_name": "SRP-DSS-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1F", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_AES_256_CBC_SHA1", "openssl_name": "SRP-DSS-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x22", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-RSA-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1B", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_AES_128_CBC_SHA1", "openssl_name": "SRP-RSA-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1E", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_AES_256_CBC_SHA1", "openssl_name": "SRP-RSA-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x21", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1A", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_AES_128_CBC_SHA1", "openssl_name": "SRP-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1D", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_AES_256_CBC_SHA1", "openssl_name": "SRP-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x20", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}]} \ No newline at end of file +{"ciphersuites": [{"TLS_AES_128_CCM_8_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x13", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "TLS_AES_128_CCM_SHA256", "hex_byte_1": "0x13", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.3"]}}, {"TLS_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "TLS_AES_128_GCM_SHA256", "hex_byte_1": "0x13", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "TLS_AES_256_GCM_SHA384", "hex_byte_1": "0x13", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_CHACHA20_POLY1305_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x13", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.3"]}}, {"TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x19", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x17", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_3DES_EDE_CBC_SHA1", "openssl_name": "ADH-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x1B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_AES_128_CBC_SHA1", "openssl_name": "ADH-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x34", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_128_CBC_SHA256", "openssl_name": "ADH-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6C", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_128_GCM_SHA256", "openssl_name": "ADH-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA6", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_AES_256_CBC_SHA1", "openssl_name": "ADH-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x3A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_AES_256_CBC_SHA256", "openssl_name": "ADH-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6D", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DH_ANON_AES_256_GCM_SHA384", "openssl_name": "ADH-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA7", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x46", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x47", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_CBC_SHA1", "openssl_name": "ADH-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x46", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_CBC_SHA256", "openssl_name": "ADH-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBF", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x84", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_CBC_SHA1", "openssl_name": "ADH-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x89", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_CBC_SHA256", "openssl_name": "ADH-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC5", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DH_ANON_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x85", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1A", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_RC4_128_MD5": {"gnutls_name": "TLS_DH_ANON_ARCFOUR_128_MD5", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x18", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_anon_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "ADH-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x9B", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "anon", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0B", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0D", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x30", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x3E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA4", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x36", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x68", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA5", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x58", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x59", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x42", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xBB", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x82", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x85", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC1", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x83", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0C", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_DSS_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x97", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "DSS", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x11", "protocol_version": "TLS EXPORT", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-DSS-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x13", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_AES_128_CBC_SHA1", "openssl_name": "DHE-DSS-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x32", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_128_CBC_SHA256", "openssl_name": "DHE-DSS-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x40", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_128_GCM_SHA256", "openssl_name": "DHE-DSS-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_AES_256_CBC_SHA1", "openssl_name": "DHE-DSS-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x38", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_AES_256_CBC_SHA256", "openssl_name": "DHE-DSS-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6A", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_DSS_AES_256_GCM_SHA384", "openssl_name": "DHE-DSS-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x42", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x56", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x43", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x57", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_CBC_SHA1", "openssl_name": "DHE-DSS-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x44", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-DSS-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBD", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x80", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_CBC_SHA1", "openssl_name": "DHE-DSS-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x87", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_CBC_SHA256", "openssl_name": "DHE-DSS-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_DSS_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x81", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x12", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_DSS_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "DHE-DSS-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x99", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "DSS", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_AES_128_CBC_SHA1", "openssl_name": "DHE-PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x90", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_PSK_AES_128_CBC_SHA256", "openssl_name": "DHE-PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_CCM": {"gnutls_name": "TLS_DHE_PSK_AES_128_CCM", "openssl_name": "DHE-PSK-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA6", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_PSK_AES_128_GCM_SHA256", "openssl_name": "DHE-PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_PSK_AES_256_CBC_SHA1", "openssl_name": "DHE-PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x91", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_DHE_PSK_AES_256_CBC_SHA384", "openssl_name": "DHE-PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_CCM": {"gnutls_name": "TLS_DHE_PSK_AES_256_CCM", "openssl_name": "DHE-PSK-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA7", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_PSK_AES_256_GCM_SHA384", "openssl_name": "DHE-PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x66", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6C", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x67", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x96", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x90", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "DHE-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x97", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x91", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_DHE_PSK_CHACHA20_POLY1305", "openssl_name": "DHE-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA1", "openssl_name": "DHE-PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA256", "openssl_name": "DHE-PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB4", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_DHE_PSK_NULL_SHA384", "openssl_name": "DHE-PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB5", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_DHE_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x8E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x14", "protocol_version": "TLS EXPORT", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "DHE-RSA-DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x16", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_AES_128_CBC_SHA1", "openssl_name": "DHE-RSA-AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x33", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_128_CBC_SHA256", "openssl_name": "DHE-RSA-AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x67", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_DHE_RSA_AES_128_CCM", "openssl_name": "DHE-RSA-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_DHE_RSA_AES_128_CCM_8", "openssl_name": "DHE-RSA-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA2", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_128_GCM_SHA256", "openssl_name": "DHE-RSA-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x9E", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_AES_256_CBC_SHA1", "openssl_name": "DHE-RSA-AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x39", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_AES_256_CBC_SHA256", "openssl_name": "DHE-RSA-AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x6B", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_DHE_RSA_AES_256_CCM", "openssl_name": "DHE-RSA-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_DHE_RSA_AES_256_CCM_8", "openssl_name": "DHE-RSA-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA3", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_RSA_AES_256_GCM_SHA384", "openssl_name": "DHE-RSA-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0x9F", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x44", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x52", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x45", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x53", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_CBC_SHA1", "openssl_name": "DHE-RSA-CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x45", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "DHE-RSA-CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBE", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7C", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_CBC_SHA1", "openssl_name": "DHE-RSA-CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x88", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_CBC_SHA256", "openssl_name": "DHE-RSA-CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC4", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_DHE_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7D", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_DHE_RSA_CHACHA20_POLY1305", "openssl_name": "DHE-RSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x15", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DHE_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "DHE-RSA-SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x9A", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0E", "protocol_version": "TLS EXPORT", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x10", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x31", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x3F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA0", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x37", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x69", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xA1", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x40", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x54", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x41", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x55", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x43", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xBC", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7E", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x86", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC2", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x0F", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_DH_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x98", "protocol_version": "TLS", "kex_algorithm": "DH", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB2", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB0", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_256_CCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB3", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECCPWD_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB1", "protocol_version": "TLS", "kex_algorithm": "ECCPWD", "auth_algorithm": "ECCPWD", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_3DES_EDE_CBC_SHA1", "openssl_name": "AECDH-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x17", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_AES_128_CBC_SHA1", "openssl_name": "AECDH-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x18", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDH_ANON_AES_256_CBC_SHA1", "openssl_name": "AECDH-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x19", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDH_ANON_NULL_SHA1", "openssl_name": "AECDH-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x15", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_anon_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDH_ANON_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x16", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "anon", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x25", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x26", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4A", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4B", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x74", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x88", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x75", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x89", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_NULL_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_ECDSA_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "ECDSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x08", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x09", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CBC_SHA256", "openssl_name": "ECDHE-ECDSA-AES128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x23", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CCM", "openssl_name": "ECDHE-ECDSA-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_CCM_8", "openssl_name": "ECDHE-ECDSA-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_128_GCM_SHA256", "openssl_name": "ECDHE-ECDSA-AES128-GCM-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x2B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CBC_SHA1", "openssl_name": "ECDHE-ECDSA-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x0A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CBC_SHA384", "openssl_name": "ECDHE-ECDSA-AES256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x24", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CCM", "openssl_name": "ECDHE-ECDSA-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_CCM_8", "openssl_name": "ECDHE-ECDSA-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAF", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_AES_256_GCM_SHA384", "openssl_name": "ECDHE-ECDSA-AES256-GCM-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x2C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x48", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x49", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x5D", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-ECDSA-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x72", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x86", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-ECDSA-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x73", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_ECDSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x87", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_ECDSA_CHACHA20_POLY1305", "openssl_name": "ECDHE-ECDSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_NULL_SHA1", "openssl_name": "ECDHE-ECDSA-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x06", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_ECDSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x07", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "ECDSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x34", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_AES_128_CBC_SHA1", "openssl_name": "ECDHE-PSK-AES128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x35", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_AES_128_CBC_SHA256", "openssl_name": "ECDHE-PSK-AES128-CBC-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x37", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_PSK_AES_256_CBC_SHA1", "openssl_name": "ECDHE-PSK-AES256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x36", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_AES_256_CBC_SHA384", "openssl_name": "ECDHE-PSK-AES256-CBC-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x38", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xD0", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x70", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x71", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x9A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x9B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_CHACHA20_POLY1305", "openssl_name": "ECDHE-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "recommended", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA1", "openssl_name": "ECDHE-PSK-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x39", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA256", "openssl_name": "ECDHE-PSK-NULL-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x3A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_ECDHE_PSK_NULL_SHA384", "openssl_name": "ECDHE-PSK-NULL-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x3B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x33", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "ECDHE-RSA-DES-CBC3-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x12", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_CBC_SHA1", "openssl_name": "ECDHE-RSA-AES128-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x13", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_CBC_SHA256", "openssl_name": "ECDHE-RSA-AES128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x27", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_AES_128_GCM_SHA256", "openssl_name": "ECDHE-RSA-AES128-GCM-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x2F", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_CBC_SHA1", "openssl_name": "ECDHE-RSA-AES256-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x14", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_CBC_SHA384", "openssl_name": "ECDHE-RSA-AES256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x28", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_AES_256_GCM_SHA384", "openssl_name": "ECDHE-RSA-AES256-GCM-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x30", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4C", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x60", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4D", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x61", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "ECDHE-RSA-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x76", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8A", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_256_CBC_SHA384", "openssl_name": "ECDHE-RSA-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x77", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_ECDHE_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8B", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_ECDHE_RSA_CHACHA20_POLY1305", "openssl_name": "ECDHE-RSA-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "secure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_NULL_SHA": {"gnutls_name": "TLS_ECDHE_RSA_NULL_SHA1", "openssl_name": "ECDHE-RSA-NULL-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x10", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDHE_RSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_ECDHE_RSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x11", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x29", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x31", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x2A", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x32", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4E", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x62", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x4F", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x63", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x78", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8C", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x79", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8D", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_NULL_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0B", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_ECDH_RSA_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x0C", "protocol_version": "TLS", "kex_algorithm": "ECDH", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_28147_CNT_IMIT": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "28147 CNT", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x00", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "KUZNYECHIK CTR", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x03", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "KUZNYECHIK MGM L", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "KUZNYECHIK MGM S", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_CTR_OMAC": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "GOSTR341112 256", "auth_algorithm": "GOSTR341012", "enc_algorithm": "MAGMA CTR", "hash_algorithm": "GOSTR341112", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_MGM_L": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "MAGMA MGM L", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_GOSTR341112_256_WITH_MAGMA_MGM_S": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC1", "hex_byte_2": "0x06", "protocol_version": "TLS", "kex_algorithm": "ECDHE", "auth_algorithm": "-", "enc_algorithm": "MAGMA MGM S", "hash_algorithm": "-", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x29", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x26", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x2A", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x27", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x2B", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_EXPORT_WITH_RC4_40_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x28", "protocol_version": "TLS EXPORT", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 40", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_3DES_EDE_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x23", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1F", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_DES_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x22", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x1E", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_IDEA_CBC_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x25", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "IDEA CBC", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_IDEA_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x21", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "IDEA CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_RC4_128_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x24", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_KRB5_WITH_RC4_128_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x20", "protocol_version": "TLS", "kex_algorithm": "KRB5", "auth_algorithm": "KRB5", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_NULL_WITH_NULL_NULL": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x00", "protocol_version": "TLS", "kex_algorithm": "NULL", "auth_algorithm": "NULL", "enc_algorithm": "NULL", "hash_algorithm": "NULL", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_DHE_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_DHE_PSK_AES_128_CCM_8", "openssl_name": "DHE-PSK-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAA", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_DHE_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_DHE_PSK_AES_256_CCM_8", "openssl_name": "DHE-PSK-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "DHE", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8B", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_PSK_AES_128_CBC_SHA1", "openssl_name": "PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8C", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_PSK_AES_128_CBC_SHA256", "openssl_name": "PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CCM": {"gnutls_name": "TLS_PSK_AES_128_CCM", "openssl_name": "PSK-AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA4", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_PSK_AES_128_CCM_8", "openssl_name": "PSK-AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_PSK_AES_128_GCM_SHA256", "openssl_name": "PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xA8", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_PSK_AES_256_CBC_SHA1", "openssl_name": "PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x8D", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_PSK_AES_256_CBC_SHA384", "openssl_name": "PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAF", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CCM": {"gnutls_name": "TLS_PSK_AES_256_CCM", "openssl_name": "PSK-AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0xA5", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_PSK_AES_256_CCM_8", "openssl_name": "PSK-AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_PSK_AES_256_GCM_SHA384", "openssl_name": "PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xA9", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x64", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6A", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x65", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6B", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x94", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8E", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x95", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x8F", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_PSK_CHACHA20_POLY1305", "openssl_name": "PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAB", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_PSK_NULL_SHA1", "openssl_name": "PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2C", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_PSK_NULL_SHA256", "openssl_name": "PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB0", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_PSK_NULL_SHA384", "openssl_name": "PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB1", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x8A", "protocol_version": "TLS", "kex_algorithm": "PSK", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_DES40_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x08", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "DES40 CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x06", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC2 CBC 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_EXPORT_WITH_RC4_40_MD5": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x03", "protocol_version": "TLS EXPORT", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 40", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_3DES_EDE_CBC_SHA1", "openssl_name": "RSA-PSK-3DES-EDE-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x93", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_AES_128_CBC_SHA1", "openssl_name": "RSA-PSK-AES128-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x94", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_PSK_AES_128_CBC_SHA256", "openssl_name": "RSA-PSK-AES128-CBC-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB6", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_PSK_AES_128_GCM_SHA256", "openssl_name": "RSA-PSK-AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xAC", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_RSA_PSK_AES_256_CBC_SHA1", "openssl_name": "RSA-PSK-AES256-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x95", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_CBC_SHA384": {"gnutls_name": "TLS_RSA_PSK_AES_256_CBC_SHA384", "openssl_name": "RSA-PSK-AES256-CBC-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB7", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_PSK_AES_256_GCM_SHA384", "openssl_name": "RSA-PSK-AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xAD", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x68", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6E", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x69", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x6F", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_128_CBC_SHA256", "openssl_name": "RSA-PSK-CAMELLIA128-SHA256", "hex_byte_1": "0xC0", "hex_byte_2": "0x98", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x92", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_256_CBC_SHA384", "openssl_name": "RSA-PSK-CAMELLIA256-SHA384", "hex_byte_1": "0xC0", "hex_byte_2": "0x99", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_PSK_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x93", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256": {"gnutls_name": "TLS_RSA_PSK_CHACHA20_POLY1305", "openssl_name": "RSA-PSK-CHACHA20-POLY1305", "hex_byte_1": "0xCC", "hex_byte_2": "0xAE", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "CHACHA20 POLY1305", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA1", "openssl_name": "RSA-PSK-NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2E", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA256": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA256", "openssl_name": "RSA-PSK-NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xB8", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_NULL_SHA384": {"gnutls_name": "TLS_RSA_PSK_NULL_SHA384", "openssl_name": "RSA-PSK-NULL-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0xB9", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_PSK_WITH_RC4_128_SHA": {"gnutls_name": "TLS_RSA_PSK_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x92", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "PSK", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "DES-CBC3-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x0A", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_RSA_AES_128_CBC_SHA1", "openssl_name": "AES128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x2F", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_AES_128_CBC_SHA256", "openssl_name": "AES128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CCM": {"gnutls_name": "TLS_RSA_AES_128_CCM", "openssl_name": "AES128-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_CCM_8": {"gnutls_name": "TLS_RSA_AES_128_CCM_8", "openssl_name": "AES128-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA0", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_AES_128_GCM_SHA256", "openssl_name": "AES128-GCM-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x9C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_RSA_AES_256_CBC_SHA1", "openssl_name": "AES256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x35", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CBC_SHA256": {"gnutls_name": "TLS_RSA_AES_256_CBC_SHA256", "openssl_name": "AES256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CCM": {"gnutls_name": "TLS_RSA_AES_256_CCM", "openssl_name": "AES256-CCM", "hex_byte_1": "0xC0", "hex_byte_2": "0x9D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_CCM_8": {"gnutls_name": "TLS_RSA_AES_256_CCM_8", "openssl_name": "AES256-CCM8", "hex_byte_1": "0xC0", "hex_byte_2": "0xA1", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 CCM 8", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_AES_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_AES_256_GCM_SHA384", "openssl_name": "AES256-GCM-SHA384", "hex_byte_1": "0x00", "hex_byte_2": "0x9D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "AES 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_128_CBC_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3C", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_128_GCM_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x50", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_256_CBC_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x3D", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 CBC", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_ARIA_256_GCM_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x51", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "ARIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA": {"gnutls_name": "TLS_RSA_CAMELLIA_128_CBC_SHA1", "openssl_name": "CAMELLIA128-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x41", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_128_CBC_SHA256", "openssl_name": "CAMELLIA128-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xBA", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_128_GCM_SHA256", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7A", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 128 GCM", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA": {"gnutls_name": "TLS_RSA_CAMELLIA_256_CBC_SHA1", "openssl_name": "CAMELLIA256-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x84", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256": {"gnutls_name": "TLS_RSA_CAMELLIA_256_CBC_SHA256", "openssl_name": "CAMELLIA256-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0xC0", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 CBC", "hash_algorithm": "SHA256", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384": {"gnutls_name": "TLS_RSA_CAMELLIA_256_GCM_SHA384", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0x7B", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "CAMELLIA 256 GCM", "hash_algorithm": "SHA384", "security": "weak", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_DES_CBC_SHA": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x09", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "DES CBC", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_IDEA_CBC_SHA": {"gnutls_name": "", "openssl_name": "IDEA-CBC-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x07", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "IDEA CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_MD5": {"gnutls_name": "TLS_RSA_NULL_MD5", "openssl_name": "NULL-MD5", "hex_byte_1": "0x00", "hex_byte_2": "0x01", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_SHA": {"gnutls_name": "TLS_RSA_NULL_SHA1", "openssl_name": "NULL-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x02", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_NULL_SHA256": {"gnutls_name": "TLS_RSA_NULL_SHA256", "openssl_name": "NULL-SHA256", "hex_byte_1": "0x00", "hex_byte_2": "0x3B", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_RC4_128_MD5": {"gnutls_name": "TLS_RSA_ARCFOUR_128_MD5", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x04", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "MD5", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_RC4_128_SHA": {"gnutls_name": "TLS_RSA_ARCFOUR_128_SHA1", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0x05", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "RC4 128", "hash_algorithm": "SHA", "security": "insecure", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_RSA_WITH_SEED_CBC_SHA": {"gnutls_name": "", "openssl_name": "SEED-SHA", "hex_byte_1": "0x00", "hex_byte_2": "0x96", "protocol_version": "TLS", "kex_algorithm": "RSA", "auth_algorithm": "RSA", "enc_algorithm": "SEED CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SHA256_SHA256": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB4", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "SHA256", "enc_algorithm": "NULL", "hash_algorithm": "SHA256", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SHA384_SHA384": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0xC0", "hex_byte_2": "0xB5", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "SHA384", "enc_algorithm": "NULL", "hash_algorithm": "SHA384", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SM4_CCM_SM3": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC7", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "SM4 CCM", "hash_algorithm": "SM3", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SM4_GCM_SM3": {"gnutls_name": "", "openssl_name": "", "hex_byte_1": "0x00", "hex_byte_2": "0xC6", "protocol_version": "TLS", "kex_algorithm": "-", "auth_algorithm": "-", "enc_algorithm": "SM4 GCM", "hash_algorithm": "SM3", "security": "insecure", "tls_version": ["TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-DSS-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1C", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_AES_128_CBC_SHA1", "openssl_name": "SRP-DSS-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1F", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_DSS_AES_256_CBC_SHA1", "openssl_name": "SRP-DSS-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x22", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA DSS", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-RSA-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1B", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_AES_128_CBC_SHA1", "openssl_name": "SRP-RSA-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1E", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_RSA_AES_256_CBC_SHA1", "openssl_name": "SRP-RSA-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x21", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA RSA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_3DES_EDE_CBC_SHA1", "openssl_name": "SRP-3DES-EDE-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1A", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "3DES EDE CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_AES_128_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_AES_128_CBC_SHA1", "openssl_name": "SRP-AES-128-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x1D", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "AES 128 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}, {"TLS_SRP_SHA_WITH_AES_256_CBC_SHA": {"gnutls_name": "TLS_SRP_SHA_AES_256_CBC_SHA1", "openssl_name": "SRP-AES-256-CBC-SHA", "hex_byte_1": "0xC0", "hex_byte_2": "0x20", "protocol_version": "TLS", "kex_algorithm": "SRP", "auth_algorithm": "SHA", "enc_algorithm": "AES 256 CBC", "hash_algorithm": "SHA", "security": "weak", "tls_version": ["TLS1.0", "TLS1.1", "TLS1.2", "TLS1.3"]}}]} \ No newline at end of file diff --git a/mapper/src/main/java/com/ibm/mapper/mapper/ssl/json/JsonCipherSuites.java b/mapper/src/main/java/com/ibm/mapper/mapper/ssl/json/JsonCipherSuites.java index 8bcc4a6bf..b94d3d1a3 100644 --- a/mapper/src/main/java/com/ibm/mapper/mapper/ssl/json/JsonCipherSuites.java +++ b/mapper/src/main/java/com/ibm/mapper/mapper/ssl/json/JsonCipherSuites.java @@ -41,17 +41,6 @@ private JsonCipherSuites() { null, "AES 128 CCM 8", "SHA256")), - Map.entry( - "TLS_AES_128_CCM_ASCONHASH256", - new JsonCipherSuite( - "TLS_AES_128_CCM_ASCONHASH256", - null, - null, - new String[] {"0x00", "0x71"}, - "AES", - "128 CCM ASCONHASH256", - null, - null)), Map.entry( "TLS_AES_128_CCM_SHA256", new JsonCipherSuite( @@ -63,17 +52,6 @@ private JsonCipherSuites() { null, "AES 128 CCM", "SHA256")), - Map.entry( - "TLS_AES_128_GCM_ASCONHASH256", - new JsonCipherSuite( - "TLS_AES_128_GCM_ASCONHASH256", - null, - null, - new String[] {"0x00", "0x70"}, - "AES", - "128 GCM ASCONHASH256", - null, - null)), Map.entry( "TLS_AES_128_GCM_SHA256", new JsonCipherSuite( @@ -96,17 +74,6 @@ private JsonCipherSuites() { null, "AES 256 GCM", "SHA384")), - Map.entry( - "TLS_ASCONAEAD128_SHA256", - new JsonCipherSuite( - "TLS_ASCONAEAD128_SHA256", - null, - null, - new String[] {"0x00", "0x6F"}, - "ASCONAEAD128", - "SHA256", - null, - null)), Map.entry( "TLS_CHACHA20_POLY1305_SHA256", new JsonCipherSuite(