From e079a4d5b24a1daece4abf8807670da7442fded2 Mon Sep 17 00:00:00 2001 From: royb Date: Thu, 29 Sep 2022 18:30:54 -0400 Subject: Added ASN.1 key encoding/decoding for Kyber, Dilithium, and Falcon. --- crypto/src/asn1/bc/BCObjectIdentifiers.cs | 33 ++++++ .../dilithium/DilithiumPrivateKeyParameters.cs | 36 ++----- .../dilithium/DilithiumPublicKeyParameters.cs | 11 +- .../crystals/kyber/KyberPrivateKeyParameters.cs | 7 ++ .../crystals/kyber/KyberPublicKeyParameters.cs | 3 + .../pqc/crypto/falcon/FalconKeyPairGenerator.cs | 11 +- crypto/src/pqc/crypto/falcon/FalconNIST.cs | 65 +++++++----- .../crypto/falcon/FalconPrivateKeyParameters.cs | 34 ++++++- crypto/src/pqc/crypto/utils/PqcUtilities.cs | 83 +++++++++++++-- crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs | 111 +++++++++++++++++++++ .../src/pqc/crypto/utils/PrivateKeyInfoFactory.cs | 59 +++++++++++ crypto/src/pqc/crypto/utils/PublicKeyFactory.cs | 97 ++++++++++++++++++ .../crypto/utils/SubjectPublicKeyInfoFactory.cs | 32 ++++++ crypto/test/src/pqc/crypto/test/FalconTest.cs | 55 ++-------- 14 files changed, 509 insertions(+), 128 deletions(-) diff --git a/crypto/src/asn1/bc/BCObjectIdentifiers.cs b/crypto/src/asn1/bc/BCObjectIdentifiers.cs index a649e0c73..0a2db9fd6 100644 --- a/crypto/src/asn1/bc/BCObjectIdentifiers.cs +++ b/crypto/src/asn1/bc/BCObjectIdentifiers.cs @@ -116,6 +116,27 @@ namespace Org.BouncyCastle.Asn1.BC public static readonly DerObjectIdentifier picnicl1full = picnic.Branch("10"); public static readonly DerObjectIdentifier picnicl3full = picnic.Branch("11"); public static readonly DerObjectIdentifier picnicl5full = picnic.Branch("12"); + + /* + * Falcon + */ + public static readonly DerObjectIdentifier falcon = bc_sig.Branch("7"); + + public static readonly DerObjectIdentifier falcon_512 = new DerObjectIdentifier("1.3.9999.3.1"); // falcon.branch("1"); + public static readonly DerObjectIdentifier falcon_1024 = new DerObjectIdentifier("1.3.9999.3.4"); // falcon.branch("2"); + + /* + * Dilithium + */ + public static readonly DerObjectIdentifier dilithium = bc_sig.Branch("8"); + + // OpenSSL OIDs + public static readonly DerObjectIdentifier dilithium2 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.7.4.4"); // dilithium.branch("1"); + public static readonly DerObjectIdentifier dilithium3 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.7.6.5"); // dilithium.branch("2"); + public static readonly DerObjectIdentifier dilithium5 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.7.8.7"); // dilithium.branch("3"); + public static readonly DerObjectIdentifier dilithium2_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.11.4.4"); // dilithium.branch("4"); + public static readonly DerObjectIdentifier dilithium3_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.11.6.5"); // dilithium.branch("5"); + public static readonly DerObjectIdentifier dilithium5_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.11.8.7"); // dilithium.branch("6"); /** @@ -184,5 +205,17 @@ namespace Org.BouncyCastle.Asn1.BC public static readonly DerObjectIdentifier sikep503_compressed = pqc_kem_sike.Branch("6"); public static readonly DerObjectIdentifier sikep610_compressed = pqc_kem_sike.Branch("7"); public static readonly DerObjectIdentifier sikep751_compressed = pqc_kem_sike.Branch("8"); + + /** + * Kyber + */ + public static readonly DerObjectIdentifier pqc_kem_kyber = bc_kem.Branch("6"); + + public static readonly DerObjectIdentifier kyber512 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.8.2.2"); // pqc_kem_kyber.Branch("1"); + public static readonly DerObjectIdentifier kyber768 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.8.3.3"); // pqc_kem_kyber.Branch("2"); + public static readonly DerObjectIdentifier kyber1024 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.8.4.4"); // pqc_kem_kyber.Branch("3"); + public static readonly DerObjectIdentifier kyber512_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.10.2.2"); // pqc_kem_kyber.Branch("4"); + public static readonly DerObjectIdentifier kyber768_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.10.3.3"); // pqc_kem_kyber.Branch("5"); + public static readonly DerObjectIdentifier kyber1024_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.10.4.4"); // pqc_kem_kyber.Branch("6"); } } diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs index ced1932bb..bb1b95ff6 100644 --- a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs +++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs @@ -26,40 +26,20 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium this.t1 = Arrays.Clone(t1); } - public byte[] GetRho() - { - return Arrays.Clone(rho); - } + public byte[] Rho => Arrays.Clone(rho); - public byte[] GetK() - { - return Arrays.Clone(k); - } + public byte[] K => Arrays.Clone(k); - public byte[] GetTr() - { - return Arrays.Clone(tr); - } + public byte[] Tr => Arrays.Clone(tr); - public byte[] GetS1() - { - return Arrays.Clone(s1); - } + public byte[] S1 => Arrays.Clone(s1); - public byte[] GetS2() - { - return Arrays.Clone(s2); - } + public byte[] S2 => Arrays.Clone(s2); + - public byte[] GetT0() - { - return Arrays.Clone(t0); - } + public byte[] T0 => Arrays.Clone(t0); - public byte[] GetT1() - { - return t1; - } + public byte[] T1 => t1; public byte[] GetEncoded() { diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs index 138fb4983..506b919b7 100644 --- a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs +++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs @@ -26,14 +26,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium { return Arrays.Concatenate(rho, t1); } - public byte[] GetRho() - { - return Arrays.Clone(rho); - } - public byte[] GetT1() - { - return Arrays.Clone(t1); - } + public byte[] Rho => Arrays.Clone(rho); + + public byte[] T1 => Arrays.Clone(t1); } } \ No newline at end of file diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs index ab355be54..c9cf3a360 100644 --- a/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs +++ b/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs @@ -25,5 +25,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber { return Arrays.ConcatenateAll(m_s, m_t, m_rho, m_hpk, m_nonce); } + + internal byte[] S => m_s; + internal byte[] Hpk => m_hpk; + internal byte[] Nonce => m_nonce; + internal byte[] T => m_t; + internal byte[] Rho => m_rho; + } } \ No newline at end of file diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs index 56d80f535..efc582abc 100644 --- a/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs +++ b/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs @@ -26,6 +26,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber m_t = Arrays.Clone(t); m_rho = Arrays.Clone(rho); } + + internal byte[] T => m_t; + internal byte[] Rho => m_rho; } } \ No newline at end of file diff --git a/crypto/src/pqc/crypto/falcon/FalconKeyPairGenerator.cs b/crypto/src/pqc/crypto/falcon/FalconKeyPairGenerator.cs index d7e4e5539..eafd1a9eb 100644 --- a/crypto/src/pqc/crypto/falcon/FalconKeyPairGenerator.cs +++ b/crypto/src/pqc/crypto/falcon/FalconKeyPairGenerator.cs @@ -13,7 +13,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon private uint noncelen; private int pk_size; - private int sk_size; public void Init(KeyGenerationParameters param) { @@ -36,18 +35,16 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon { sk_coeff_size = 7; } + this.pk_size = 1 + (14 * n / 8); - this.sk_size = 1 + (2 * sk_coeff_size * n / 8) + (n); } public AsymmetricCipherKeyPair GenerateKeyPair() { - byte[] pk, sk; - pk = new byte[pk_size]; - sk = new byte[sk_size]; - nist.crypto_sign_keypair(pk, 0, sk, 0); + byte[] pk, sk, f, g, F; + nist.crypto_sign_keypair(out pk, out f, out g, out F); FalconParameters p = ((FalconKeyGenerationParameters)this.parameters).Parameters; - FalconPrivateKeyParameters privk = new FalconPrivateKeyParameters(p, sk); + FalconPrivateKeyParameters privk = new FalconPrivateKeyParameters(p, f, g, F, pk); FalconPublicKeyParameters pubk = new FalconPublicKeyParameters(p, pk); return new AsymmetricCipherKeyPair(pubk, privk); } diff --git a/crypto/src/pqc/crypto/falcon/FalconNIST.cs b/crypto/src/pqc/crypto/falcon/FalconNIST.cs index 50459532f..cce790734 100644 --- a/crypto/src/pqc/crypto/falcon/FalconNIST.cs +++ b/crypto/src/pqc/crypto/falcon/FalconNIST.cs @@ -57,13 +57,13 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon } } - internal int crypto_sign_keypair(byte[] pksrc, int pk, byte[] sksrc, int sk) + internal int crypto_sign_keypair(out byte[] pk, out byte[] fEnc, out byte[] gEnc, out byte[] FEnc) { + byte[] sk = new byte[CRYPTO_SECRETKEYBYTES]; + pk = new byte[CRYPTO_PUBLICKEYBYTES]; int n = (int)1 << (int)this.logn; SHAKE256 rng = new SHAKE256(); - sbyte[] f = new sbyte[n], - g = new sbyte[n], - F = new sbyte[n]; + sbyte[] f = new sbyte[n], g = new sbyte[n], F = new sbyte[n]; ushort[] h = new ushort[n]; byte[] seed = new byte[48]; int u, v; @@ -81,26 +81,29 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon /* * Encode private key. */ - sksrc[sk+0] = (byte)(0x50 + this.logn); + sk[0] = (byte)(0x50 + this.logn); u = 1; - v = this.codec.trim_i8_encode(sksrc, sk + u, CRYPTO_SECRETKEYBYTES - u, + v = this.codec.trim_i8_encode(sk, u, CRYPTO_SECRETKEYBYTES - u, f, 0, this.logn, this.codec.max_fg_bits[this.logn]); if (v == 0) { // TODO check which exception types to use here throw new InvalidOperationException("f encode failed"); } + fEnc = Arrays.CopyOfRange(sk, u, u + v); u += v; - v = this.codec.trim_i8_encode(sksrc, sk + u, CRYPTO_SECRETKEYBYTES - u, + v = this.codec.trim_i8_encode(sk, u, CRYPTO_SECRETKEYBYTES - u, g, 0, this.logn, this.codec.max_fg_bits[this.logn]); if (v == 0) { throw new InvalidOperationException("g encode failed"); } + gEnc = Arrays.CopyOfRange(sk, u, u + v); u += v; - v = this.codec.trim_i8_encode(sksrc, sk + u, CRYPTO_SECRETKEYBYTES - u, + v = this.codec.trim_i8_encode(sk, u, CRYPTO_SECRETKEYBYTES - u, F, 0, this.logn, this.codec.max_FG_bits[this.logn]); if (v == 0) { throw new InvalidOperationException("F encode failed"); } + FEnc = Arrays.CopyOfRange(sk, u, u + v); u += v; if (u != CRYPTO_SECRETKEYBYTES) { throw new InvalidOperationException("secret key encoding failed"); @@ -109,12 +112,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon /* * Encode public key. */ - pksrc[pk+0] = (byte)(0x00 + this.logn); - v = this.codec.modq_encode(pksrc, pk + 1, CRYPTO_PUBLICKEYBYTES - 1, h, 0, this.logn); + pk[0] = (byte)(0x00 + this.logn); + v = this.codec.modq_encode(pk, 1, CRYPTO_PUBLICKEYBYTES - 1, h, 0, this.logn); if (v != CRYPTO_PUBLICKEYBYTES - 1) { throw new InvalidOperationException("public key encoding failed"); } + pk = Arrays.CopyOfRange(pk, 1, pk.Length); + return 0; } @@ -133,43 +138,51 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon g = new sbyte[n], F = new sbyte[n], G = new sbyte[n]; + short[] sig = new short[n]; ushort[] hm = new ushort[n]; + byte[] seed = new byte[48], nonce = new byte[this.noncelen]; + byte[] esig = new byte[this.CRYPTO_BYTES - 2 - this.noncelen]; SHAKE256 sc = new SHAKE256(); FalconSign signer = new FalconSign(this.common); - /* - * Decode the private key. - */ - if (sksrc[sk+0] != 0x50 + this.logn) { - throw new ArgumentException("private key header incorrect"); - } - u = 1; + // /* + // * Decode the private key. + // */ + // if (sksrc[sk+0] != 0x50 + this.logn) { + // throw new ArgumentException("private key header incorrect"); + // } + u = 0; v = this.codec.trim_i8_decode(f, 0, this.logn, this.codec.max_fg_bits[this.logn], sksrc, sk + u, CRYPTO_SECRETKEYBYTES - u); - if (v == 0) { + if (v == 0) + { throw new InvalidOperationException("f decode failed"); } u += v; v = this.codec.trim_i8_decode(g, 0, this.logn, this.codec.max_fg_bits[this.logn], sksrc, sk + u, CRYPTO_SECRETKEYBYTES - u); - if (v == 0) { + if (v == 0) + { throw new InvalidOperationException("g decode failed"); } u += v; v = this.codec.trim_i8_decode(F, 0, this.logn, this.codec.max_FG_bits[this.logn], sksrc, sk + u, CRYPTO_SECRETKEYBYTES - u); - if (v == 0) { + if (v == 0) + { throw new InvalidOperationException("F decode failed"); } u += v; - if (u != CRYPTO_SECRETKEYBYTES) { + if (u != CRYPTO_SECRETKEYBYTES - 1) + { throw new InvalidOperationException("full Key not used"); } - if (this.vrfy.complete_private(G, 0, f, 0, g, 0, F, 0, this.logn, new ushort[2 * n],0) == 0) { + if (this.vrfy.complete_private(G, 0, f, 0, g, 0, F, 0, this.logn, new ushort[2 * n],0) == 0) + { throw new InvalidOperationException("complete private failed"); } @@ -238,10 +251,10 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon /* * Decode public key. */ - if (pksrc[pk+0] != 0x00 + this.logn) { - return -1; - } - if (this.codec.modq_decode(h, 0, this.logn, pksrc, pk + 1, CRYPTO_PUBLICKEYBYTES - 1) + // if (pksrc[pk+0] != 0x00 + this.logn) { + // return -1; + // } + if (this.codec.modq_decode(h, 0, this.logn, pksrc, pk, CRYPTO_PUBLICKEYBYTES - 1) != CRYPTO_PUBLICKEYBYTES - 1) { return -1; diff --git a/crypto/src/pqc/crypto/falcon/FalconPrivateKeyParameters.cs b/crypto/src/pqc/crypto/falcon/FalconPrivateKeyParameters.cs index d30aafcf4..c912a222c 100644 --- a/crypto/src/pqc/crypto/falcon/FalconPrivateKeyParameters.cs +++ b/crypto/src/pqc/crypto/falcon/FalconPrivateKeyParameters.cs @@ -6,17 +6,43 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon public class FalconPrivateKeyParameters : FalconKeyParameters { - private byte[] privateKey; + private byte[] pk; + private byte[] f; + private byte[] g; + private byte[] F; - public FalconPrivateKeyParameters(FalconParameters parameters, byte[] sk_encoded) + public FalconPrivateKeyParameters(FalconParameters parameters, byte[] f, byte[] g, byte[] F, byte[] pk_encoded) : base(true, parameters) { - this.privateKey = Arrays.Clone(sk_encoded); + this.f = Arrays.Clone(f); + this.g = Arrays.Clone(g); + this.F = Arrays.Clone(F); + this.pk = Arrays.Clone(pk_encoded); } public byte[] GetEncoded() { - return Arrays.Clone(privateKey); + return Arrays.ConcatenateAll(f, g, F); + } + + public byte[] GetPublicKey() + { + return Arrays.Clone(pk); + } + + public byte[] GetSpolyf() + { + return Arrays.Clone(f); + } + + public byte[] GetG() + { + return Arrays.Clone(g); + } + + public byte[] GetSpolyF() + { + return Arrays.Clone(F); } } } diff --git a/crypto/src/pqc/crypto/utils/PqcUtilities.cs b/crypto/src/pqc/crypto/utils/PqcUtilities.cs index a1fb04340..f0603ed16 100644 --- a/crypto/src/pqc/crypto/utils/PqcUtilities.cs +++ b/crypto/src/pqc/crypto/utils/PqcUtilities.cs @@ -4,6 +4,9 @@ using System.Collections.Generic; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.BC; using Org.BouncyCastle.Pqc.Crypto.Cmce; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; +using Org.BouncyCastle.Pqc.Crypto.Falcon; using Org.BouncyCastle.Pqc.Crypto.Picnic; using Org.BouncyCastle.Pqc.Crypto.Saber; using Org.BouncyCastle.Pqc.Crypto.Sike; @@ -24,6 +27,15 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities private readonly static Dictionary sikeOids = new Dictionary(); private readonly static Dictionary sikeParams = new Dictionary(); + + private readonly static Dictionary kyberOids = new Dictionary(); + private readonly static Dictionary kyberParams = new Dictionary(); + + private readonly static Dictionary dilithiumOids = new Dictionary(); + private readonly static Dictionary dilithiumParams = new Dictionary(); + + private readonly static Dictionary falconOids = new Dictionary(); + private readonly static Dictionary falconParams = new Dictionary(); static PqcUtilities() @@ -108,13 +120,46 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities sikeParams[BCObjectIdentifiers.sikep751_compressed] = SIKEParameters.sikep751_compressed; sikeOids[SIKEParameters.sikep434] = BCObjectIdentifiers.sikep434; - sikeOids[SIKEParameters.sikep503] = BCObjectIdentifiers.sikep503; - sikeOids[SIKEParameters.sikep610] = BCObjectIdentifiers.sikep610; - sikeOids[SIKEParameters.sikep751] = BCObjectIdentifiers.sikep751; - sikeOids[SIKEParameters.sikep434_compressed] = BCObjectIdentifiers.sikep434_compressed; - sikeOids[SIKEParameters.sikep503_compressed] = BCObjectIdentifiers.sikep503_compressed; - sikeOids[SIKEParameters.sikep610_compressed] = BCObjectIdentifiers.sikep610_compressed; - sikeOids[SIKEParameters.sikep751_compressed] = BCObjectIdentifiers.sikep751_compressed; + sikeOids[SIKEParameters.sikep434] = BCObjectIdentifiers.sikep434; + sikeOids[SIKEParameters.sikep434] = BCObjectIdentifiers.sikep434; + sikeOids[SIKEParameters.sikep434] = BCObjectIdentifiers.sikep434; + sikeOids[SIKEParameters.sikep434] = BCObjectIdentifiers.sikep434; + sikeOids[SIKEParameters.sikep434] = BCObjectIdentifiers.sikep434; + + kyberOids[KyberParameters.kyber512] = BCObjectIdentifiers.kyber512; + kyberOids[KyberParameters.kyber768] = BCObjectIdentifiers.kyber768; + kyberOids[KyberParameters.kyber1024] = BCObjectIdentifiers.kyber1024; + kyberOids[KyberParameters.kyber512_aes] = BCObjectIdentifiers.kyber512_aes; + kyberOids[KyberParameters.kyber768_aes] = BCObjectIdentifiers.kyber768_aes; + kyberOids[KyberParameters.kyber1024_aes] = BCObjectIdentifiers.kyber1024_aes; + + kyberParams[BCObjectIdentifiers.kyber512] = KyberParameters.kyber512; + kyberParams[BCObjectIdentifiers.kyber768] = KyberParameters.kyber768; + kyberParams[BCObjectIdentifiers.kyber1024] = KyberParameters.kyber1024; + kyberParams[BCObjectIdentifiers.kyber512_aes] = KyberParameters.kyber512_aes; + kyberParams[BCObjectIdentifiers.kyber768_aes] = KyberParameters.kyber768_aes; + kyberParams[BCObjectIdentifiers.kyber1024_aes] = KyberParameters.kyber1024_aes; + + + falconOids[FalconParameters.falcon_512] = BCObjectIdentifiers.falcon_512; + falconOids[FalconParameters.falcon_1024] = BCObjectIdentifiers.falcon_1024; + + falconParams[BCObjectIdentifiers.falcon_512] = FalconParameters.falcon_512; + falconParams[BCObjectIdentifiers.falcon_1024] = FalconParameters.falcon_1024; + + dilithiumOids[DilithiumParameters.Dilithium2] = BCObjectIdentifiers.dilithium2; + dilithiumOids[DilithiumParameters.Dilithium3] = BCObjectIdentifiers.dilithium3; + dilithiumOids[DilithiumParameters.Dilithium5] = BCObjectIdentifiers.dilithium5; + dilithiumOids[DilithiumParameters.Dilithium2Aes] = BCObjectIdentifiers.dilithium2_aes; + dilithiumOids[DilithiumParameters.Dilithium3Aes] = BCObjectIdentifiers.dilithium3_aes; + dilithiumOids[DilithiumParameters.Dilithium5Aes] = BCObjectIdentifiers.dilithium5_aes; + + dilithiumParams[BCObjectIdentifiers.dilithium2] = DilithiumParameters.Dilithium2; + dilithiumParams[BCObjectIdentifiers.dilithium3] = DilithiumParameters.Dilithium3; + dilithiumParams[BCObjectIdentifiers.dilithium5] = DilithiumParameters.Dilithium5; + dilithiumParams[BCObjectIdentifiers.dilithium2_aes] = DilithiumParameters.Dilithium2Aes; + dilithiumParams[BCObjectIdentifiers.dilithium3_aes] = DilithiumParameters.Dilithium3Aes; + dilithiumParams[BCObjectIdentifiers.dilithium5_aes] = DilithiumParameters.Dilithium5Aes; } public static DerObjectIdentifier McElieceOidLookup(CmceParameters parameters) @@ -135,6 +180,30 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities { return saberParams[oid]; } + internal static KyberParameters KyberParamsLookup(DerObjectIdentifier oid) + { + return kyberParams[oid]; + } + internal static DerObjectIdentifier KyberOidLookup(KyberParameters parameters) + { + return kyberOids[parameters]; + } + internal static FalconParameters FalconParamsLookup(DerObjectIdentifier oid) + { + return falconParams[oid]; + } + internal static DerObjectIdentifier FalconOidLookup(FalconParameters parameters) + { + return falconOids[parameters]; + } + internal static DilithiumParameters DilithiumParamsLookup(DerObjectIdentifier oid) + { + return dilithiumParams[oid]; + } + internal static DerObjectIdentifier DilithiumOidLookup(DilithiumParameters parameters) + { + return dilithiumOids[parameters]; + } internal static DerObjectIdentifier SphincsPlusOidLookup(SPHINCSPlusParameters parameters) { diff --git a/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs b/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs index 0fadab855..db424faac 100644 --- a/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs +++ b/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs @@ -10,6 +10,9 @@ using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Math; using Org.BouncyCastle.Pqc.Asn1; using Org.BouncyCastle.Pqc.Crypto.Cmce; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; +using Org.BouncyCastle.Pqc.Crypto.Falcon; using Org.BouncyCastle.Pqc.Crypto.Lms; using Org.BouncyCastle.Pqc.Crypto.Picnic; using Org.BouncyCastle.Pqc.Crypto.Saber; @@ -105,6 +108,114 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities return new SIKEPrivateKeyParameters(sikeParams, keyEnc); } + if (algOID.Equals(BCObjectIdentifiers.kyber512) + || algOID.Equals(BCObjectIdentifiers.kyber512_aes) + || algOID.Equals(BCObjectIdentifiers.kyber768) + || algOID.Equals(BCObjectIdentifiers.kyber768_aes) + || algOID.Equals(BCObjectIdentifiers.kyber1024) + || algOID.Equals(BCObjectIdentifiers.kyber1024_aes)) + { + Asn1Sequence keyEnc = Asn1Sequence.GetInstance(keyInfo.ParsePrivateKey()); + + KyberParameters spParams = PqcUtilities.KyberParamsLookup(keyInfo.PrivateKeyAlgorithm.Algorithm); + + int version = DerInteger.GetInstance(keyEnc[0]).Value.IntValue; + if (version != 0) + { + throw new IOException("unknown private key version: " + version); + } + + if (keyInfo.PublicKeyData != null) + { + Asn1Sequence pubKey = Asn1Sequence.GetInstance(keyInfo.PublicKeyData.GetOctets()); + return new KyberPrivateKeyParameters(spParams, + Asn1OctetString.GetInstance(keyEnc[1]).GetDerEncoded(), + Asn1OctetString.GetInstance(keyEnc[2]).GetOctets(), + Asn1OctetString.GetInstance(keyEnc[3]).GetOctets(), + Asn1OctetString.GetInstance(pubKey[0]).GetOctets(), // t + Asn1OctetString.GetInstance(pubKey[1]).GetOctets()); // rho + } + else + { + return new KyberPrivateKeyParameters(spParams, + Asn1OctetString.GetInstance(keyEnc[1]).GetOctets(), + Asn1OctetString.GetInstance(keyEnc[2]).GetOctets(), + Asn1OctetString.GetInstance(keyEnc[3]).GetOctets(), + null, + null); + } + } + if (algOID.Equals(BCObjectIdentifiers.dilithium2) + || algOID.Equals(BCObjectIdentifiers.dilithium3) + || algOID.Equals(BCObjectIdentifiers.dilithium5) + || algOID.Equals(BCObjectIdentifiers.dilithium2_aes) + || algOID.Equals(BCObjectIdentifiers.dilithium3_aes) + || algOID.Equals(BCObjectIdentifiers.dilithium5_aes)) + { + Asn1Sequence keyEnc = Asn1Sequence.GetInstance(keyInfo.ParsePrivateKey()); + + DilithiumParameters spParams = PqcUtilities.DilithiumParamsLookup(keyInfo.PrivateKeyAlgorithm.Algorithm); + + int version = DerInteger.GetInstance(keyEnc[0]).Value.IntValue; + if (version != 0) + { + throw new IOException("unknown private key version: " + version); + } + + if (keyInfo.PublicKeyData != null) + { + Asn1Sequence pubKey = Asn1Sequence.GetInstance(keyInfo.PublicKeyData.GetOctets()); + return new DilithiumPrivateKeyParameters(spParams, + DerBitString.GetInstance(keyEnc[1]).GetOctets(), + DerBitString.GetInstance(keyEnc[2]).GetOctets(), + DerBitString.GetInstance(keyEnc[3]).GetOctets(), + DerBitString.GetInstance(keyEnc[4]).GetOctets(), + DerBitString.GetInstance(keyEnc[5]).GetOctets(), + DerBitString.GetInstance(keyEnc[6]).GetOctets(), + Asn1OctetString.GetInstance(pubKey[1]).GetOctets()); // encT1 + } + else + { + return new DilithiumPrivateKeyParameters(spParams, + DerBitString.GetInstance(keyEnc[1]).GetOctets(), + DerBitString.GetInstance(keyEnc[2]).GetOctets(), + DerBitString.GetInstance(keyEnc[3]).GetOctets(), + DerBitString.GetInstance(keyEnc[4]).GetOctets(), + DerBitString.GetInstance(keyEnc[5]).GetOctets(), + DerBitString.GetInstance(keyEnc[6]).GetOctets(), + null); + } + } + if (algOID.Equals(BCObjectIdentifiers.falcon_512) || algOID.Equals(BCObjectIdentifiers.falcon_1024)) + { + Asn1Sequence keyEnc = Asn1Sequence.GetInstance(keyInfo.ParsePrivateKey()); + FalconParameters spParams = PqcUtilities.FalconParamsLookup(keyInfo.PrivateKeyAlgorithm.Algorithm); + + DerBitString publicKeyData = keyInfo.PublicKeyData; + int version = DerInteger.GetInstance(keyEnc[0]).Value.IntValue; + if (version != 1) + { + throw new IOException("unknown private key version: " + version); + } + + if (keyInfo.PublicKeyData != null) + { + //ASN1Sequence pubKey = ASN1Sequence.getInstance(keyInfo.getPublicKeyData().getOctets()); + return new FalconPrivateKeyParameters(spParams, + Asn1OctetString.GetInstance(keyEnc[1]).GetOctets(), + Asn1OctetString.GetInstance(keyEnc[2]).GetOctets(), + Asn1OctetString.GetInstance(keyEnc[3]).GetOctets(), + publicKeyData.GetOctets()); // encT1 + } + else + { + return new FalconPrivateKeyParameters(spParams, + Asn1OctetString.GetInstance(keyEnc[1]).GetOctets(), + Asn1OctetString.GetInstance(keyEnc[2]).GetOctets(), + Asn1OctetString.GetInstance(keyEnc[3]).GetOctets(), + null); + } + } throw new Exception("algorithm identifier in private key not recognised"); diff --git a/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs b/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs index c5c3f9e45..42bc3ca91 100644 --- a/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs +++ b/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs @@ -6,6 +6,9 @@ using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Pqc.Asn1; using Org.BouncyCastle.Pqc.Crypto.Cmce; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; +using Org.BouncyCastle.Pqc.Crypto.Falcon; using Org.BouncyCastle.Pqc.Crypto.Lms; using Org.BouncyCastle.Pqc.Crypto.Picnic; using Org.BouncyCastle.Pqc.Crypto.Saber; @@ -109,6 +112,62 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PqcUtilities.SikeOidLookup(parameters.GetParameters())); return new PrivateKeyInfo(algorithmIdentifier, new DerOctetString(encoding), attributes); } + if (privateKey is FalconPrivateKeyParameters) + { + FalconPrivateKeyParameters parameters = (FalconPrivateKeyParameters)privateKey; + + Asn1EncodableVector v = new Asn1EncodableVector(); + + v.Add(new DerInteger(1)); + v.Add(new DerOctetString(parameters.GetSpolyf())); + v.Add(new DerOctetString(parameters.GetG())); + v.Add(new DerOctetString(parameters.GetSpolyF())); + + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PqcUtilities.FalconOidLookup(parameters.Parameters)); + + return new PrivateKeyInfo(algorithmIdentifier, new DerSequence(v), attributes, parameters.GetPublicKey()); + } + if (privateKey is KyberPrivateKeyParameters) + { + KyberPrivateKeyParameters parameters = (KyberPrivateKeyParameters)privateKey; + + Asn1EncodableVector v = new Asn1EncodableVector(); + + v.Add(new DerInteger(0)); + v.Add(new DerOctetString(parameters.S)); + v.Add(new DerOctetString(parameters.Hpk)); + v.Add(new DerOctetString(parameters.Nonce)); + + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PqcUtilities.KyberOidLookup(parameters.Parameters)); + + Asn1EncodableVector vPub = new Asn1EncodableVector(); + vPub.Add(new DerOctetString(parameters.T)); + vPub.Add(new DerOctetString(parameters.Rho)); + + return new PrivateKeyInfo(algorithmIdentifier, new DerSequence(v), attributes, new DerSequence(vPub).GetEncoded()); + } + if (privateKey is DilithiumPrivateKeyParameters) + { + DilithiumPrivateKeyParameters parameters = (DilithiumPrivateKeyParameters)privateKey; + + Asn1EncodableVector v = new Asn1EncodableVector(); + + v.Add(new DerInteger(0)); + v.Add(new DerBitString(parameters.Rho)); + v.Add(new DerBitString(parameters.K)); + v.Add(new DerBitString(parameters.Tr)); + v.Add(new DerBitString(parameters.S1)); + v.Add(new DerBitString(parameters.S2)); + v.Add(new DerBitString(parameters.T0)); + + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PqcUtilities.DilithiumOidLookup(parameters.Parameters)); + + Asn1EncodableVector vPub = new Asn1EncodableVector(); + vPub.Add(new DerOctetString(parameters.Rho)); + vPub.Add(new DerOctetString(parameters.T1)); + + return new PrivateKeyInfo(algorithmIdentifier, new DerSequence(v), attributes, new DerSequence(vPub).GetEncoded()); + } throw new ArgumentException("Class provided is not convertible: " + Platform.GetTypeName(privateKey)); } diff --git a/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs b/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs index 3610711ff..e2279c15c 100644 --- a/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs +++ b/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs @@ -10,6 +10,9 @@ using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Math; using Org.BouncyCastle.Pqc.Asn1; using Org.BouncyCastle.Pqc.Crypto.Cmce; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; +using Org.BouncyCastle.Pqc.Crypto.Falcon; using Org.BouncyCastle.Pqc.Crypto.Picnic; using Org.BouncyCastle.Pqc.Crypto.Saber; using Org.BouncyCastle.Pqc.Crypto.Sike; @@ -73,6 +76,23 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities converters[BCObjectIdentifiers.sikep503_compressed] = new SikeConverter(); converters[BCObjectIdentifiers.sikep610_compressed] = new SikeConverter(); converters[BCObjectIdentifiers.sikep751_compressed] = new SikeConverter(); + + converters[BCObjectIdentifiers.dilithium2] = new DilithiumConverter(); + converters[BCObjectIdentifiers.dilithium3] = new DilithiumConverter(); + converters[BCObjectIdentifiers.dilithium5] = new DilithiumConverter(); + converters[BCObjectIdentifiers.dilithium2_aes] = new DilithiumConverter(); + converters[BCObjectIdentifiers.dilithium3_aes] = new DilithiumConverter(); + converters[BCObjectIdentifiers.dilithium5_aes] = new DilithiumConverter(); + + converters[BCObjectIdentifiers.falcon_512] = new FalconConverter(); + converters[BCObjectIdentifiers.falcon_1024] = new FalconConverter(); + + converters[BCObjectIdentifiers.kyber512] = new KyberConverter(); + converters[BCObjectIdentifiers.kyber512_aes] = new KyberConverter(); + converters[BCObjectIdentifiers.kyber768] = new KyberConverter(); + converters[BCObjectIdentifiers.kyber768_aes] = new KyberConverter(); + converters[BCObjectIdentifiers.kyber1024] = new KyberConverter(); + converters[BCObjectIdentifiers.kyber1024_aes] = new KyberConverter(); } /// Create a public key from a SubjectPublicKeyInfo encoding @@ -190,5 +210,82 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities return new SIKEPublicKeyParameters(sikeParams, keyEnc); } } + private class DilithiumConverter + : SubjectPublicKeyInfoConverter + { + internal override AsymmetricKeyParameter GetPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + { + DilithiumParameters dilithiumParams = PqcUtilities.DilithiumParamsLookup(keyInfo.AlgorithmID.Algorithm); + + Asn1Object obj = keyInfo.ParsePublicKey(); + if (obj is Asn1Sequence) + { + Asn1Sequence keySeq = Asn1Sequence.GetInstance(obj); + + return new DilithiumPublicKeyParameters(dilithiumParams, + Asn1OctetString.GetInstance(keySeq[0]).GetOctets(), + Asn1OctetString.GetInstance(keySeq[1]).GetOctets()); + } + else + { + byte[] encKey = Asn1OctetString.GetInstance(obj).GetOctets(); + + return new DilithiumPublicKeyParameters(dilithiumParams, encKey); + } + } + } + + private class KyberConverter + : SubjectPublicKeyInfoConverter + { + internal override AsymmetricKeyParameter GetPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + { + KyberParameters kyberParameters = PqcUtilities.KyberParamsLookup(keyInfo.AlgorithmID.Algorithm); + + Asn1Object obj = keyInfo.ParsePublicKey(); + if (obj is Asn1Sequence) + { + Asn1Sequence keySeq = Asn1Sequence.GetInstance(obj); + + return new KyberPublicKeyParameters(kyberParameters, + Asn1OctetString.GetInstance(keySeq[0]).GetOctets(), + Asn1OctetString.GetInstance(keySeq[1]).GetOctets()); + } + else + { + byte[] encKey = Asn1OctetString.GetInstance(obj).GetOctets(); + + return new KyberPublicKeyParameters(kyberParameters, encKey); + } + } + } + + private class FalconConverter + : SubjectPublicKeyInfoConverter + { + internal override AsymmetricKeyParameter GetPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + { + FalconParameters falconParams = PqcUtilities.FalconParamsLookup(keyInfo.AlgorithmID.Algorithm); + + Asn1Object obj = keyInfo.ParsePublicKey(); + if (obj is Asn1Sequence) + { + byte[] keyEnc = Asn1OctetString.GetInstance(Asn1Sequence.GetInstance(obj)[0]).GetOctets(); + + return new FalconPublicKeyParameters(falconParams, keyEnc); + } + else + { + // header byte + h + byte[] keyEnc = Asn1OctetString.GetInstance(obj).GetOctets(); + + if (keyEnc[0] != (byte)(0x00 + falconParams.LogN)) + { + throw new ArgumentException("byte[] enc of Falcon h value not tagged correctly"); + } + return new FalconPublicKeyParameters(falconParams, Arrays.CopyOfRange(keyEnc, 1, keyEnc.Length)); + } + } + } } } \ No newline at end of file diff --git a/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs b/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs index 0cf80bbe3..eea6b8717 100644 --- a/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs +++ b/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs @@ -6,6 +6,9 @@ using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Math; using Org.BouncyCastle.Pqc.Asn1; using Org.BouncyCastle.Pqc.Crypto.Cmce; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; +using Org.BouncyCastle.Pqc.Crypto.Falcon; using Org.BouncyCastle.Pqc.Crypto.Picnic; using Org.BouncyCastle.Pqc.Crypto.Saber; using Org.BouncyCastle.Pqc.Crypto.Sike; @@ -87,6 +90,35 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PqcUtilities.SikeOidLookup(parameters.GetParameters())); return new SubjectPublicKeyInfo(algorithmIdentifier, new DerOctetString(encoding)); } + if (publicKey is FalconPublicKeyParameters) + { + FalconPublicKeyParameters parameters = (FalconPublicKeyParameters)publicKey; + + byte[] encoding = parameters.GetEncoded(); + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PqcUtilities.FalconOidLookup(parameters.Parameters)); + + return new SubjectPublicKeyInfo(algorithmIdentifier, new DerSequence(new DerOctetString(encoding))); + } + if (publicKey is KyberPublicKeyParameters) + { + KyberPublicKeyParameters parameters = (KyberPublicKeyParameters)publicKey; + + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PqcUtilities.KyberOidLookup(parameters.Parameters)); + Asn1EncodableVector v = new Asn1EncodableVector(); + v.Add(new DerOctetString(parameters.T)); + v.Add(new DerOctetString(parameters.Rho)); + return new SubjectPublicKeyInfo(algorithmIdentifier, new DerSequence(v)); + } + if (publicKey is DilithiumPublicKeyParameters) + { + DilithiumPublicKeyParameters parameters = (DilithiumPublicKeyParameters)publicKey; + + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PqcUtilities.DilithiumOidLookup(parameters.Parameters)); + Asn1EncodableVector v = new Asn1EncodableVector(); + v.Add(new DerOctetString(parameters.Rho)); + v.Add(new DerOctetString(parameters.T1)); + return new SubjectPublicKeyInfo(algorithmIdentifier, new DerSequence(v)); + } throw new ArgumentException("Class provided no convertible: " + Platform.GetTypeName(publicKey)); diff --git a/crypto/test/src/pqc/crypto/test/FalconTest.cs b/crypto/test/src/pqc/crypto/test/FalconTest.cs index bcda32669..4ede0803d 100644 --- a/crypto/test/src/pqc/crypto/test/FalconTest.cs +++ b/crypto/test/src/pqc/crypto/test/FalconTest.cs @@ -63,11 +63,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests AsymmetricCipherKeyPair ackp = kpg.GenerateKeyPair(); byte[] respk = ((FalconPublicKeyParameters) ackp.Public).GetEncoded(); byte[] ressk = ((FalconPrivateKeyParameters) ackp.Private).GetEncoded(); + + //keygen + Assert.True(Arrays.AreEqual(respk, 0, respk.Length, pk, 1, pk.Length), name + " " + count + " public key"); + Assert.True(Arrays.AreEqual(ressk, 0, ressk.Length, sk, 1, sk.Length), name + " " + count + " private key"); // sign FalconSigner signer = new FalconSigner(); - FalconPrivateKeyParameters skparam = new FalconPrivateKeyParameters(parameters[fileIndex], sk); - ParametersWithRandom skwrand = new ParametersWithRandom(skparam, random); + ParametersWithRandom skwrand = new ParametersWithRandom(ackp.Private, random); signer.Init(true, skwrand); byte[] sig = signer.GenerateSignature(msg); byte[] ressm = new byte[2 + msg.Length + sig.Length - 1]; @@ -79,7 +82,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests // verify FalconSigner verifier = new FalconSigner(); - FalconPublicKeyParameters pkparam = new FalconPublicKeyParameters(parameters[fileIndex], pk); + FalconPublicKeyParameters pkparam = (FalconPublicKeyParameters)ackp.Public; verifier.Init(false, pkparam); byte[] noncesig = new byte[sm_len - m_len - 2 + 1]; noncesig[0] = (byte)(0x30 + parameters[fileIndex].LogN); @@ -88,51 +91,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests bool vrfyrespass = verifier.VerifySignature(msg, noncesig); noncesig[42]++; // changing the signature by 1 byte should cause it to fail bool vrfyresfail = verifier.VerifySignature(msg, noncesig); - - // print results - /* - System.out.println("--Keygen"); - bool kgenpass = true; - if (!Arrays.areEqual(respk, pk)) { - System.out.println(" == Keygen: pk do not match"); - kgenpass = false; - } - if (!Arrays.areEqual(ressk, sk)) { - System.out.println(" == Keygen: sk do not match"); - kgenpass = false; - } - if (kgenpass) { - System.out.println(" ++ Keygen pass"); - } else { - System.out.println(" == Keygen failed"); - return; - } - - System.out.println("--Sign"); - bool spass = true; - if (!Arrays.areEqual(ressm, sm)) { - System.out.println(" == Sign: signature do not match"); - spass = false; - } - if (spass) { - System.out.println(" ++ Sign pass"); - } else { - System.out.println(" == Sign failed"); - return; - } - - System.out.println("--Verify"); - if (vrfyrespass && !vrfyresfail) { - System.out.println(" ++ Verify pass"); - } else { - System.out.println(" == Verify failed"); - return; - } - */ - // Assert.True - //keygen - Assert.True(Arrays.AreEqual(respk, pk), name + " " + count + " public key"); - Assert.True(Arrays.AreEqual(ressk, sk), name + " " + count + " private key"); + //sign Assert.True(Arrays.AreEqual(ressm, sm), name + " " + count + " signature"); //verify -- cgit 1.4.1