From daa52b50b945dddb08b640c5794b1d3c9c39a212 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sun, 16 Apr 2023 00:04:25 +0700 Subject: Fixes and improvements for github_439 --- .../src/crypto/util/OpenSshPrivateKeyUtilities.cs | 92 ++++++-------- .../src/crypto/util/OpenSshPublicKeyUtilities.cs | 116 +++++++++--------- crypto/src/crypto/util/SshBuffer.cs | 104 ++++++++-------- crypto/src/crypto/util/SshBuilder.cs | 22 ++-- crypto/src/crypto/util/SshNamedCurves.cs | 132 ++++++++++++--------- .../test/src/crypto/test/OpenSshKeyParsingTest.cs | 2 +- 6 files changed, 236 insertions(+), 232 deletions(-) diff --git a/crypto/src/crypto/util/OpenSshPrivateKeyUtilities.cs b/crypto/src/crypto/util/OpenSshPrivateKeyUtilities.cs index 187ecc39e..aa934b1d6 100644 --- a/crypto/src/crypto/util/OpenSshPrivateKeyUtilities.cs +++ b/crypto/src/crypto/util/OpenSshPrivateKeyUtilities.cs @@ -1,7 +1,7 @@ using System; +using System.Text; using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.X9; @@ -16,7 +16,7 @@ namespace Org.BouncyCastle.Crypto.Utilities { /// Magic value for proprietary OpenSSH private key. /// C string so null terminated. - private static readonly byte[] AUTH_MAGIC = Strings.ToByteArray("openssh-key-v1\0"); + private static readonly byte[] AUTH_MAGIC = Encoding.ASCII.GetBytes("openssh-key-v1\0"); /** * Encode a cipher parameters into an OpenSSH private key. @@ -66,9 +66,9 @@ namespace Org.BouncyCastle.Crypto.Utilities SshBuilder builder = new SshBuilder(); builder.WriteBytes(AUTH_MAGIC); - builder.WriteString("none"); // cipher name - builder.WriteString("none"); // KDF name - builder.WriteString(""); // KDF options + builder.WriteStringAscii("none"); // cipher name + builder.WriteStringAscii("none"); // KDF name + builder.WriteStringAscii(""); // KDF options builder.U32(1); // Number of keys @@ -84,7 +84,7 @@ namespace Org.BouncyCastle.Crypto.Utilities pkBuild.U32((uint)checkint); pkBuild.U32((uint)checkint); - pkBuild.WriteString("ssh-ed25519"); + pkBuild.WriteStringAscii("ssh-ed25519"); // Public key (as part of private key pair) byte[] pubKeyEncoded = publicKeyParameters.GetEncoded(); @@ -94,7 +94,7 @@ namespace Org.BouncyCastle.Crypto.Utilities pkBuild.WriteBlock(Arrays.Concatenate(ed25519PrivateKey.GetEncoded(), pubKeyEncoded)); // Comment for this private key (empty) - pkBuild.WriteString(""); + pkBuild.WriteStringUtf8(""); builder.WriteBlock(pkBuild.GetPaddedBytes()); } @@ -102,8 +102,7 @@ namespace Org.BouncyCastle.Crypto.Utilities return builder.GetBytes(); } - throw new ArgumentException("unable to convert " + parameters.GetType().Name + " to openssh private key"); - + throw new ArgumentException("unable to convert " + Platform.GetTypeName(parameters) + " to openssh private key"); } /** @@ -177,11 +176,9 @@ namespace Org.BouncyCastle.Crypto.Utilities { SshBuffer kIn = new SshBuffer(AUTH_MAGIC, blob); - string cipherName = kIn.ReadString(); + string cipherName = kIn.ReadStringAscii(); if (!"none".Equals(cipherName)) - { throw new InvalidOperationException("encrypted keys not supported"); - } // KDF name kIn.SkipBlock(); @@ -191,9 +188,7 @@ namespace Org.BouncyCastle.Crypto.Utilities int publicKeyCount = kIn.ReadU32(); if (publicKeyCount != 1) - { throw new InvalidOperationException("multiple keys not supported"); - } // Burn off public key. OpenSshPublicKeyUtilities.ParsePublicKey(kIn.ReadBlock()); @@ -201,87 +196,80 @@ namespace Org.BouncyCastle.Crypto.Utilities byte[] privateKeyBlock = kIn.ReadPaddedBlock(); if (kIn.HasRemaining()) - { throw new InvalidOperationException("decoded key has trailing data"); - } SshBuffer pkIn = new SshBuffer(privateKeyBlock); int check1 = pkIn.ReadU32(); int check2 = pkIn.ReadU32(); if (check1 != check2) - { throw new InvalidOperationException("private key check values are not the same"); - } - string keyType = pkIn.ReadString(); + string keyType = pkIn.ReadStringAscii(); if ("ssh-ed25519".Equals(keyType)) { // Public key - pkIn.ReadBlock(); + pkIn.SkipBlock(); + // Private key value.. +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ReadOnlySpan edPrivateKey = pkIn.ReadBlockSpan(); +#else byte[] edPrivateKey = pkIn.ReadBlock(); +#endif + if (edPrivateKey.Length != Ed25519PrivateKeyParameters.KeySize + Ed25519PublicKeyParameters.KeySize) - { throw new InvalidOperationException("private key value of wrong length"); - } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + result = new Ed25519PrivateKeyParameters(edPrivateKey[..Ed25519PrivateKeyParameters.KeySize]); +#else result = new Ed25519PrivateKeyParameters(edPrivateKey, 0); +#endif } else if (keyType.StartsWith("ecdsa")) { - DerObjectIdentifier oid = SshNamedCurves.GetByName(Strings.FromByteArray(pkIn.ReadBlock())) ?? + var curveName = pkIn.ReadStringAscii(); + + var oid = SshNamedCurves.GetOid(curveName) ?? throw new InvalidOperationException("OID not found for: " + keyType); - X9ECParameters curveParams = NistNamedCurves.GetByOid(oid) ?? throw new InvalidOperationException("Curve not found for: " + oid); + + var x9ECParameters = SshNamedCurves.GetByOid(oid) ?? + throw new InvalidOperationException("Curve not found for: " + oid); // Skip public key. - pkIn.ReadBlock(); - byte[] privKey = pkIn.ReadBlock(); + pkIn.SkipBlock(); - result = new ECPrivateKeyParameters(new BigInteger(1, privKey), - new ECNamedDomainParameters(oid, curveParams)); + var d = pkIn.ReadMpintPositive(); + + result = new ECPrivateKeyParameters(d, new ECNamedDomainParameters(oid, x9ECParameters)); } else if (keyType.StartsWith("ssh-rsa")) { - BigInteger modulus = new BigInteger(1, pkIn.ReadBlock()); - BigInteger pubExp = new BigInteger(1, pkIn.ReadBlock()); - BigInteger privExp = new BigInteger(1, pkIn.ReadBlock()); - BigInteger coef = new BigInteger(1, pkIn.ReadBlock()); - BigInteger p = new BigInteger(1, pkIn.ReadBlock()); - BigInteger q = new BigInteger(1, pkIn.ReadBlock()); + BigInteger modulus = pkIn.ReadMpintPositive(); + BigInteger pubExp = pkIn.ReadMpintPositive(); + BigInteger privExp = pkIn.ReadMpintPositive(); + BigInteger coef = pkIn.ReadMpintPositive(); + BigInteger p = pkIn.ReadMpintPositive(); + BigInteger q = pkIn.ReadMpintPositive(); BigInteger pSub1 = p.Subtract(BigIntegers.One); BigInteger qSub1 = q.Subtract(BigIntegers.One); BigInteger dP = privExp.Remainder(pSub1); BigInteger dQ = privExp.Remainder(qSub1); - result = new RsaPrivateCrtKeyParameters( - modulus, - pubExp, - privExp, - p, - q, - dP, - dQ, - coef); + result = new RsaPrivateCrtKeyParameters(modulus, pubExp, privExp, p, q, dP, dQ, coef); } // Comment for private key pkIn.SkipBlock(); if (pkIn.HasRemaining()) - { throw new ArgumentException("private key block has trailing data"); - } } - if (result == null) - { - throw new ArgumentException("unable to parse key"); - } - - return result; + return result ?? throw new ArgumentException("unable to parse key"); ; } /** @@ -292,9 +280,7 @@ namespace Org.BouncyCastle.Crypto.Utilities for (int t = 0; t < sequence.Count; t++) { if (!(sequence[t] is DerInteger)) - { return false; - } } return true; } diff --git a/crypto/src/crypto/util/OpenSshPublicKeyUtilities.cs b/crypto/src/crypto/util/OpenSshPublicKeyUtilities.cs index cdb16b06a..f04ab85f7 100644 --- a/crypto/src/crypto/util/OpenSshPublicKeyUtilities.cs +++ b/crypto/src/crypto/util/OpenSshPublicKeyUtilities.cs @@ -1,9 +1,9 @@ using System; -using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Utilities { @@ -38,43 +38,34 @@ namespace Org.BouncyCastle.Crypto.Utilities public static byte[] EncodePublicKey(AsymmetricKeyParameter cipherParameters) { if (cipherParameters == null) - { - throw new ArgumentException("cipherParameters was null."); - } + throw new ArgumentNullException(nameof(cipherParameters)); + if (cipherParameters.IsPrivate) + throw new ArgumentException("Not a public key", nameof(cipherParameters)); - if (cipherParameters is RsaKeyParameters) + if (cipherParameters is RsaKeyParameters rsaPubKey) { - if (cipherParameters.IsPrivate) - { - throw new ArgumentException("RSAKeyParamaters was for encryption"); - } - - RsaKeyParameters rsaPubKey = (RsaKeyParameters)cipherParameters; - SshBuilder builder = new SshBuilder(); - builder.WriteString(RSA); - builder.WriteBigNum(rsaPubKey.Exponent); - builder.WriteBigNum(rsaPubKey.Modulus); - + builder.WriteStringAscii(RSA); + builder.WriteMpint(rsaPubKey.Exponent); + builder.WriteMpint(rsaPubKey.Modulus); return builder.GetBytes(); - } else if (cipherParameters is ECPublicKeyParameters ecPublicKey) { - SshBuilder builder = new SshBuilder(); + string curveName = null; - // - // checked for named curve parameters.. - // - string name = SshNamedCurves.GetNameForParameters(ecPublicKey.Parameters); - - if (name == null) + var oid = ecPublicKey.PublicKeyParamSet; + if (oid != null) { - throw new ArgumentException("unable to derive ssh curve name for " + ecPublicKey.Parameters.Curve.GetType().Name); + curveName = SshNamedCurves.GetName(oid); } - builder.WriteString(ECDSA + "-sha2-" + name); // Magic - builder.WriteString(name); + if (curveName == null) + throw new ArgumentException("unable to derive ssh curve name for EC public key"); + + SshBuilder builder = new SshBuilder(); + builder.WriteStringAscii(ECDSA + "-sha2-" + curveName); // Magic + builder.WriteStringAscii(curveName); builder.WriteBlock(ecPublicKey.Q.GetEncoded(false)); //Uncompressed return builder.GetBytes(); } @@ -83,22 +74,22 @@ namespace Org.BouncyCastle.Crypto.Utilities DsaParameters dsaParams = dsaPubKey.Parameters; SshBuilder builder = new SshBuilder(); - builder.WriteString(DSS); - builder.WriteBigNum(dsaParams.P); - builder.WriteBigNum(dsaParams.Q); - builder.WriteBigNum(dsaParams.G); - builder.WriteBigNum(dsaPubKey.Y); + builder.WriteStringAscii(DSS); + builder.WriteMpint(dsaParams.P); + builder.WriteMpint(dsaParams.Q); + builder.WriteMpint(dsaParams.G); + builder.WriteMpint(dsaPubKey.Y); return builder.GetBytes(); } else if (cipherParameters is Ed25519PublicKeyParameters ed25519PublicKey) { SshBuilder builder = new SshBuilder(); - builder.WriteString(ED_25519); + builder.WriteStringAscii(ED_25519); builder.WriteBlock(ed25519PublicKey.GetEncoded()); return builder.GetBytes(); } - throw new ArgumentException("unable to convert " + cipherParameters.GetType().Name + " to private key"); + throw new ArgumentException("unable to convert " + Platform.GetTypeName(cipherParameters) + " to private key"); } /** @@ -111,55 +102,60 @@ namespace Org.BouncyCastle.Crypto.Utilities { AsymmetricKeyParameter result = null; - string magic = buffer.ReadString(); + string magic = buffer.ReadStringAscii(); if (RSA.Equals(magic)) { - BigInteger e = buffer.ReadBigNumPositive(); - BigInteger n = buffer.ReadBigNumPositive(); + BigInteger e = buffer.ReadMpintPositive(); + BigInteger n = buffer.ReadMpintPositive(); result = new RsaKeyParameters(false, n, e); } else if (DSS.Equals(magic)) { - BigInteger p = buffer.ReadBigNumPositive(); - BigInteger q = buffer.ReadBigNumPositive(); - BigInteger g = buffer.ReadBigNumPositive(); - BigInteger pubKey = buffer.ReadBigNumPositive(); + BigInteger p = buffer.ReadMpintPositive(); + BigInteger q = buffer.ReadMpintPositive(); + BigInteger g = buffer.ReadMpintPositive(); + BigInteger pubKey = buffer.ReadMpintPositive(); result = new DsaPublicKeyParameters(pubKey, new DsaParameters(p, q, g)); } else if (magic.StartsWith(ECDSA)) { - string curveName = buffer.ReadString(); - DerObjectIdentifier oid = SshNamedCurves.GetByName(curveName); - X9ECParameters x9ECParameters = SshNamedCurves.GetParameters(oid) ?? - throw new InvalidOperationException("unable to find curve for " + magic + " using curve name " + curveName); - var curve = x9ECParameters.Curve; - byte[] pointRaw = buffer.ReadBlock(); - - result = new ECPublicKeyParameters( - curve.DecodePoint(pointRaw), - new ECNamedDomainParameters(oid, x9ECParameters)); + var curveName = buffer.ReadStringAscii(); + + var oid = SshNamedCurves.GetOid(curveName); + + X9ECParameters x9ECParameters = oid == null ? null : SshNamedCurves.GetByOid(oid); + if (x9ECParameters == null) + { + throw new InvalidOperationException( + "unable to find curve for " + magic + " using curve name " + curveName); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ReadOnlySpan pointEncoding = buffer.ReadBlockSpan(); +#else + byte[] pointEncoding = buffer.ReadBlock(); +#endif + var point = x9ECParameters.Curve.DecodePoint(pointEncoding); + + result = new ECPublicKeyParameters(point, new ECNamedDomainParameters(oid, x9ECParameters)); } else if (ED_25519.Equals(magic)) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ReadOnlySpan pubKeyBytes = buffer.ReadBlockSpan(); +#else byte[] pubKeyBytes = buffer.ReadBlock(); - if (pubKeyBytes.Length != Ed25519PublicKeyParameters.KeySize) - { - throw new InvalidOperationException("public key value of wrong length"); - } +#endif - result = new Ed25519PublicKeyParameters(pubKeyBytes, 0); + result = new Ed25519PublicKeyParameters(pubKeyBytes); } if (result == null) - { throw new ArgumentException("unable to parse key"); - } if (buffer.HasRemaining()) - { throw new ArgumentException("decoded key has trailing data"); - } return result; } diff --git a/crypto/src/crypto/util/SshBuffer.cs b/crypto/src/crypto/util/SshBuffer.cs index 094b85364..0df123388 100644 --- a/crypto/src/crypto/util/SshBuffer.cs +++ b/crypto/src/crypto/util/SshBuffer.cs @@ -1,4 +1,5 @@ using System; +using System.Text; using Org.BouncyCastle.Math; using Org.BouncyCastle.Utilities; @@ -16,9 +17,7 @@ namespace Org.BouncyCastle.Crypto.Utilities for (int i = 0; i != magic.Length; i++) { if (magic[i] != buffer[i]) - { throw new ArgumentException("magic-number incorrect"); - } } pos += magic.Length; @@ -32,47 +31,62 @@ namespace Org.BouncyCastle.Crypto.Utilities public int ReadU32() { if (pos > buffer.Length - 4) - { throw new ArgumentOutOfRangeException("4 bytes for U32 exceeds buffer."); - } - - int i = (buffer[pos++] & 0xFF) << 24; - i |= (buffer[pos++] & 0xFF) << 16; - i |= (buffer[pos++] & 0xFF) << 8; - i |= buffer[pos++] & 0xFF; + int i = (int)Pack.BE_To_UInt32(buffer, pos); + pos += 4; return i; } - public string ReadString() + public string ReadStringAscii() { - return Strings.FromByteArray(ReadBlock()); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Encoding.ASCII.GetString(ReadBlockSpan()); +#else + return Encoding.ASCII.GetString(ReadBlock()); +#endif + } + + public string ReadStringUtf8() + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Encoding.UTF8.GetString(ReadBlockSpan()); +#else + return Encoding.UTF8.GetString(ReadBlock()); +#endif } public byte[] ReadBlock() { int len = ReadU32(); if (len == 0) - { - return new byte[0]; - } - + return Arrays.EmptyBytes; if (pos > buffer.Length - len) - { - throw new ArgumentException("not enough data for block"); - } + throw new InvalidOperationException("not enough data for block"); int start = pos; pos += len; return Arrays.CopyOfRange(buffer, start, pos); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public ReadOnlySpan ReadBlockSpan() + { + int len = ReadU32(); + if (len == 0) + return ReadOnlySpan.Empty; + if (pos > buffer.Length - len) + throw new InvalidOperationException("not enough data for block"); + + int start = pos; pos += len; + return buffer.AsSpan(start, len); + } +#endif + public void SkipBlock() { int len = ReadU32(); if (pos > buffer.Length - len) - { - throw new ArgumentException("not enough data for block"); - } + throw new InvalidOperationException("not enough data for block"); pos += len; } @@ -86,20 +100,13 @@ namespace Org.BouncyCastle.Crypto.Utilities { int len = ReadU32(); if (len == 0) - { - return new byte[0]; - } - + return Arrays.EmptyBytes; if (pos > buffer.Length - len) - { - throw new ArgumentException("not enough data for block"); - } + throw new InvalidOperationException("not enough data for block"); int align = len % blockSize; if (0 != align) - { - throw new ArgumentException("missing padding"); - } + throw new InvalidOperationException("missing padding"); int start = pos; pos += len; int end = pos; @@ -116,9 +123,7 @@ namespace Org.BouncyCastle.Crypto.Utilities for (int i = 1, padPos = end; i <= padCount; ++i, ++padPos) { if (i != (buffer[padPos] & 0xFF)) - { - throw new ArgumentException("incorrect padding"); - } + throw new InvalidOperationException("incorrect padding"); } } } @@ -126,27 +131,32 @@ namespace Org.BouncyCastle.Crypto.Utilities return Arrays.CopyOfRange(buffer, start, end); } - public BigInteger ReadBigNumPositive() + public BigInteger ReadMpint() { int len = ReadU32(); - if (pos + len > buffer.Length) - { - throw new ArgumentException("not enough data for big num"); - } + if (pos > buffer.Length - len) + throw new InvalidOperationException("not enough data for big num"); + + if (len == 0) + return BigInteger.Zero; + if (len == 1 && buffer[pos] == 0) + throw new InvalidOperationException("Zero MUST be stored with length 0"); + if (len > 1 && buffer[pos] == (byte)-(buffer[pos + 1] >> 7)) + throw new InvalidOperationException("Unnecessary leading bytes MUST NOT be included"); int start = pos; pos += len; - byte[] d = Arrays.CopyOfRange(buffer, start, pos); - return new BigInteger(1, d); + return new BigInteger(buffer, start, len); } - public byte[] GetBuffer() + public BigInteger ReadMpintPositive() { - return Arrays.Clone(buffer); - } + BigInteger n = ReadMpint(); + if (n.SignValue < 0) + throw new InvalidOperationException("Expected a positive mpint"); - public bool HasRemaining() - { - return pos < buffer.Length; + return n; } + + public bool HasRemaining() => pos < buffer.Length; } } diff --git a/crypto/src/crypto/util/SshBuilder.cs b/crypto/src/crypto/util/SshBuilder.cs index 9f2f35360..d631448d5 100644 --- a/crypto/src/crypto/util/SshBuilder.cs +++ b/crypto/src/crypto/util/SshBuilder.cs @@ -1,8 +1,8 @@ using System; using System.IO; +using System.Text; using Org.BouncyCastle.Math; -using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Utilities { @@ -18,7 +18,7 @@ namespace Org.BouncyCastle.Crypto.Utilities bos.WriteByte(Convert.ToByte(value & 0xFF)); } - public void WriteBigNum(BigInteger n) + public void WriteMpint(BigInteger n) { WriteBlock(n.ToByteArray()); } @@ -26,14 +26,7 @@ namespace Org.BouncyCastle.Crypto.Utilities public void WriteBlock(byte[] value) { U32((uint)value.Length); - try - { - bos.Write(value, 0, value.Length); - } - catch (IOException e) - { - throw new InvalidOperationException(e.Message, e); - } + WriteBytes(value); } public void WriteBytes(byte[] value) @@ -48,9 +41,14 @@ namespace Org.BouncyCastle.Crypto.Utilities } } - public void WriteString(string str) + public void WriteStringAscii(string str) + { + WriteBlock(Encoding.ASCII.GetBytes(str)); + } + + public void WriteStringUtf8(string str) { - WriteBlock(Strings.ToByteArray(str)); + WriteBlock(Encoding.UTF8.GetBytes(str)); } public byte[] GetBytes() diff --git a/crypto/src/crypto/util/SshNamedCurves.cs b/crypto/src/crypto/util/SshNamedCurves.cs index d97c2476e..019a4d5f5 100644 --- a/crypto/src/crypto/util/SshNamedCurves.cs +++ b/crypto/src/crypto/util/SshNamedCurves.cs @@ -1,93 +1,107 @@ -using System.Collections.Generic; -using System.Linq; +using System; +using System.Collections.Generic; using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.X9; -using Org.BouncyCastle.Crypto.EC; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Crypto.Utilities { - public class SshNamedCurves + public static class SshNamedCurves { - private static readonly Dictionary OidMap = - new Dictionary - { - { "nistp256", SecObjectIdentifiers.SecP256r1 }, - { "nistp384", SecObjectIdentifiers.SecP384r1 }, - { "nistp521", SecObjectIdentifiers.SecP521r1 }, - { "nistk163", SecObjectIdentifiers.SecT163k1 }, - { "nistp192", SecObjectIdentifiers.SecP192r1 }, - { "nistp224", SecObjectIdentifiers.SecP224r1 }, - { "nistk233", SecObjectIdentifiers.SecT233k1 }, - { "nistb233", SecObjectIdentifiers.SecT233r1 }, - { "nistk283", SecObjectIdentifiers.SecT283k1 }, - { "nistk409", SecObjectIdentifiers.SecT409k1 }, - { "nistb409", SecObjectIdentifiers.SecT409r1 }, - { "nistt571", SecObjectIdentifiers.SecT571k1 } - }; + private static readonly Dictionary objIds = + new Dictionary(StringComparer.OrdinalIgnoreCase); + private static readonly Dictionary names = + new Dictionary(); + private static void DefineCurveAlias(string name, DerObjectIdentifier oid) + { + if (FindByOidLazy(oid) == null) + throw new InvalidOperationException(); - private static readonly Dictionary CurveNameToSSHName = - new Dictionary - { - {"secp256r1", "nistp256"}, - {"secp384r1", "nistp384"}, - {"secp521r1", "nistp521"}, - {"sect163k1", "nistk163"}, - {"secp192r1", "nistp192"}, - {"secp224r1", "nistp224"}, - {"sect233k1", "nistk233"}, - {"sect233r1", "nistb233"}, - {"sect283k1", "nistk283"}, - {"sect409k1", "nistk409"}, - {"sect409r1", "nistb409"}, - {"sect571k1", "nistt571"} - }; + objIds.Add(name, oid); + names.Add(oid, name); + } - private static readonly Dictionary CurveMap = - CustomNamedCurves.Names.ToDictionary(k => CustomNamedCurves.GetByNameLazy(k).Curve, v => v); + private static X9ECParametersHolder FindByOidLazy(DerObjectIdentifier oid) => + ECKeyPairGenerator.FindECCurveByOidLazy(oid); - private static readonly Dictionary OidToName = - OidMap.ToDictionary(k => k.Value, v => v.Key); + static SshNamedCurves() + { + DefineCurveAlias("nistp192", SecObjectIdentifiers.SecP192r1); + DefineCurveAlias("nistp224", SecObjectIdentifiers.SecP224r1); + DefineCurveAlias("nistp256", SecObjectIdentifiers.SecP256r1); + DefineCurveAlias("nistp384", SecObjectIdentifiers.SecP384r1); + DefineCurveAlias("nistp521", SecObjectIdentifiers.SecP521r1); + DefineCurveAlias("nistb233", SecObjectIdentifiers.SecT233r1); + DefineCurveAlias("nistb409", SecObjectIdentifiers.SecT409r1); + DefineCurveAlias("nistk163", SecObjectIdentifiers.SecT163k1); + DefineCurveAlias("nistk233", SecObjectIdentifiers.SecT233k1); + DefineCurveAlias("nistk283", SecObjectIdentifiers.SecT283k1); + DefineCurveAlias("nistk409", SecObjectIdentifiers.SecT409k1); + DefineCurveAlias("nistt571", SecObjectIdentifiers.SecT571k1); + } + /// Look up the for the curve with the given name. + /// The name of the curve. + public static X9ECParameters GetByName(string name) + { + DerObjectIdentifier oid = GetOid(name); + return oid == null ? null : GetByOid(oid); + } - public static DerObjectIdentifier GetByName(string sshName) + /// Look up an for the curve with the given name. + /// + /// Allows accessing the curve without necessarily triggering the creation of + /// the full . + /// + /// The name of the curve. + public static X9ECParametersHolder GetByNameLazy(string name) { - return OidMap[sshName]; + DerObjectIdentifier oid = GetOid(name); + return oid == null ? null : GetByOidLazy(oid); } - public static X9ECParameters GetParameters(string sshName) + /// Look up the for the curve with the given + /// OID. + /// The OID for the curve. + public static X9ECParameters GetByOid(DerObjectIdentifier oid) { - return NistNamedCurves.GetByOid(OidMap[sshName.ToLower()]); + return GetByOidLazy(oid)?.Parameters; } - public static X9ECParameters GetParameters(DerObjectIdentifier oid) + /// Look up an for the curve with the given + /// OID. + /// + /// Allows accessing the curve without necessarily triggering the creation of + /// the full . + /// + /// The OID for the curve. + public static X9ECParametersHolder GetByOidLazy(DerObjectIdentifier oid) { - return NistNamedCurves.GetByOid(oid); + return names.ContainsKey(oid) ? FindByOidLazy(oid) : null; } + /// Look up the name of the curve with the given OID. + /// The OID for the curve. public static string GetName(DerObjectIdentifier oid) { - return OidToName[oid]; + return CollectionUtilities.GetValueOrNull(names, oid); } - public static string GetNameForParameters(ECDomainParameters parameters) + /// Look up the OID of the curve with the given name. + /// The name of the curve. + public static DerObjectIdentifier GetOid(string name) { - if (parameters is ECNamedDomainParameters) - { - return GetName(((ECNamedDomainParameters)parameters).Name); - } - - return GetNameForParameters(parameters.Curve); + return CollectionUtilities.GetValueOrNull(objIds, name); } - public static string GetNameForParameters(ECCurve curve) + /// Enumerate the available curve names in this registry. + public static IEnumerable Names { - return CurveNameToSSHName[CurveMap[curve]]; + get { return CollectionUtilities.Proxy(objIds.Keys); } } } } diff --git a/crypto/test/src/crypto/test/OpenSshKeyParsingTest.cs b/crypto/test/src/crypto/test/OpenSshKeyParsingTest.cs index 46cac0f4c..02559b5c9 100644 --- a/crypto/test/src/crypto/test/OpenSshKeyParsingTest.cs +++ b/crypto/test/src/crypto/test/OpenSshKeyParsingTest.cs @@ -16,7 +16,7 @@ using Org.BouncyCastle.Utilities.IO.Pem; namespace Org.BouncyCastle.Crypto.Tests { [TestFixture] - public class OpenSshKeyParsingTests + public class OpenSshKeyParsingTest { private static SecureRandom secureRandom = new SecureRandom(); -- cgit 1.4.1