< Summary

Information
Class: Renci.SshNet.Security.EcdsaKey
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\Security\Cryptography\EcdsaKey.cs
Line coverage
72%
Covered lines: 168
Uncovered lines: 63
Coverable lines: 231
Total lines: 511
Line coverage: 72.7%
Branch coverage
66%
Covered branches: 56
Total branches: 84
Branch coverage: 66.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
ToString()100%1100%
get_HashAlgorithm()0%60%
get_HashAlgorithm()83.33%685.71%
get_KeyLength()100%1100%
get_DigitalSignature()100%2100%
get_Public()83.33%694.44%
set_Public(...)100%10%
get_PrivateKey()100%1100%
get_Ecdsa()100%1100%
.ctor()100%10%
.ctor(...)100%1100%
.ctor(...)50%872.41%
Import(...)100%2100%
GetCurveOid(...)83.33%685.71%
GetCurveName(...)83.33%685.71%
OidByteArrayToString(...)100%6100%
Dispose()100%10%
Dispose(...)50%444.44%
Finalize()100%1100%

File(s)

\home\appveyor\projects\ssh-net\src\Renci.SshNet\Security\Cryptography\EcdsaKey.cs

#LineLine coverage
 1using System;
 2#if NETFRAMEWORK
 3using System.Globalization;
 4using System.IO;
 5using System.Runtime.InteropServices;
 6#endif // NETFRAMEWORK
 7using System.Security.Cryptography;
 8using System.Text;
 9
 10using Renci.SshNet.Common;
 11using Renci.SshNet.Security.Cryptography;
 12
 13namespace Renci.SshNet.Security
 14{
 15    /// <summary>
 16    /// Contains ECDSA (ecdsa-sha2-nistp{256,384,521}) private and public key.
 17    /// </summary>
 18    public class EcdsaKey : Key, IDisposable
 19    {
 20#pragma warning disable SA1310 // Field names should not contain underscore
 21        private const string ECDSA_P256_OID_VALUE = "1.2.840.10045.3.1.7"; // Also called nistP256 or secP256r1
 22        private const string ECDSA_P384_OID_VALUE = "1.3.132.0.34"; // Also called nistP384 or secP384r1
 23        private const string ECDSA_P521_OID_VALUE = "1.3.132.0.35"; // Also called nistP521or secP521r1
 24#pragma warning restore SA1310 // Field names should not contain underscore
 25
 26        private EcdsaDigitalSignature _digitalSignature;
 27        private bool _isDisposed;
 28
 29#if NETFRAMEWORK
 30        private CngKey _key;
 31
 32        internal enum KeyBlobMagicNumber
 33        {
 34            BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345,
 35            BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345,
 36            BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 0x33534345,
 37            BCRYPT_ECDSA_PRIVATE_P384_MAGIC = 0x34534345,
 38            BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 0x35534345,
 39            BCRYPT_ECDSA_PRIVATE_P521_MAGIC = 0x36534345,
 40
 41            BCRYPT_ECDH_PUBLIC_P256_MAGIC = 0x314B4345,
 42            BCRYPT_ECDH_PRIVATE_P256_MAGIC = 0x324B4345,
 43            BCRYPT_ECDH_PUBLIC_P384_MAGIC = 0x334B4345,
 44            BCRYPT_ECDH_PRIVATE_P384_MAGIC = 0x344B4345,
 45            BCRYPT_ECDH_PUBLIC_P521_MAGIC = 0x354B4345,
 46            BCRYPT_ECDH_PRIVATE_P521_MAGIC = 0x364B4345,
 47
 48            BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC = 0x504B4345,
 49            BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC = 0x564B4345,
 50        }
 51
 52        [StructLayout(LayoutKind.Sequential)]
 53        internal struct BCRYPT_ECCKEY_BLOB
 54        {
 55            internal KeyBlobMagicNumber Magic;
 56            internal int CbKey;
 57        }
 58#endif
 59
 60        /// <summary>
 61        /// Gets the SSH name of the ECDSA Key.
 62        /// </summary>
 63        /// <returns>
 64        /// The SSH name of the ECDSA Key.
 65        /// </returns>
 66        public override string ToString()
 3967        {
 3968            return string.Format("ecdsa-sha2-nistp{0}", KeyLength);
 3969        }
 70
 71#if NETFRAMEWORK
 72        /// <summary>
 73        /// Gets the HashAlgorithm to use.
 74        /// </summary>
 75        public CngAlgorithm HashAlgorithm
 76        {
 77            get
 078            {
 079                switch (Ecdsa.KeySize)
 80                {
 81                    case 256:
 082                        return CngAlgorithm.Sha256;
 83                    case 384:
 084                        return CngAlgorithm.Sha384;
 85                    case 521:
 086                        return CngAlgorithm.Sha512;
 87                    default:
 088                        throw new SshException("Unknown KeySize: " + Ecdsa.KeySize.ToString(CultureInfo.InvariantCulture
 89                }
 090            }
 91        }
 92#else
 93        /// <summary>
 94        /// Gets the HashAlgorithm to use.
 95        /// </summary>
 96        public HashAlgorithmName HashAlgorithm
 97        {
 98            get
 399            {
 3100                switch (KeyLength)
 101                {
 102                    case 256:
 1103                        return HashAlgorithmName.SHA256;
 104                    case 384:
 1105                        return HashAlgorithmName.SHA384;
 106                    case 521:
 1107                        return HashAlgorithmName.SHA512;
 108                    default:
 0109                        return HashAlgorithmName.SHA256;
 110                }
 3111            }
 112        }
 113#endif
 114
 115        /// <summary>
 116        /// Gets the length of the key.
 117        /// </summary>
 118        /// <value>
 119        /// The length of the key.
 120        /// </value>
 121        public override int KeyLength
 122        {
 123            get
 42124            {
 42125                return Ecdsa.KeySize;
 42126            }
 127        }
 128
 129        /// <summary>
 130        /// Gets the digital signature.
 131        /// </summary>
 132        protected internal override DigitalSignature DigitalSignature
 133        {
 134            get
 39135            {
 39136                _digitalSignature ??= new EcdsaDigitalSignature(this);
 137
 39138                return _digitalSignature;
 39139            }
 140        }
 141
 142        /// <summary>
 143        /// Gets or sets the public.
 144        /// </summary>
 145        /// <value>
 146        /// The public.
 147        /// </value>
 148        public override BigInteger[] Public
 149        {
 150            get
 3151            {
 152                byte[] curve;
 153                byte[] qx;
 154                byte[] qy;
 155#if NETFRAMEWORK
 0156                var blob = _key.Export(CngKeyBlobFormat.EccPublicBlob);
 157
 158                KeyBlobMagicNumber magic;
 0159                using (var br = new BinaryReader(new MemoryStream(blob)))
 0160                {
 0161                    magic = (KeyBlobMagicNumber)br.ReadInt32();
 0162                    var cbKey = br.ReadInt32();
 0163                    qx = br.ReadBytes(cbKey);
 0164                    qy = br.ReadBytes(cbKey);
 0165                }
 166
 167#pragma warning disable IDE0010 // Add missing cases
 0168                switch (magic)
 169                {
 170                    case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC:
 0171                        curve = Encoding.ASCII.GetBytes("nistp256");
 0172                        break;
 173                    case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC:
 0174                        curve = Encoding.ASCII.GetBytes("nistp384");
 0175                        break;
 176                    case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC:
 0177                        curve = Encoding.ASCII.GetBytes("nistp521");
 0178                        break;
 179                    default:
 0180                        throw new SshException("Unexpected Curve Magic: " + magic);
 181                }
 182#pragma warning restore IDE0010 // Add missing cases
 183#else
 3184                var parameter = Ecdsa.ExportParameters(includePrivateParameters: false);
 3185                qx = parameter.Q.X;
 3186                qy = parameter.Q.Y;
 3187                switch (parameter.Curve.Oid.FriendlyName)
 188                {
 189                    case "ECDSA_P256":
 190                    case "nistP256":
 1191                        curve = Encoding.ASCII.GetBytes("nistp256");
 1192                        break;
 193                    case "ECDSA_P384":
 194                    case "nistP384":
 1195                        curve = Encoding.ASCII.GetBytes("nistp384");
 1196                        break;
 197                    case "ECDSA_P521":
 198                    case "nistP521":
 1199                        curve = Encoding.ASCII.GetBytes("nistp521");
 1200                        break;
 201                    default:
 0202                        throw new SshException("Unexpected Curve Name: " + parameter.Curve.Oid.FriendlyName);
 203                }
 204#endif
 205
 206                // Make ECPoint from x and y
 207                // Prepend 04 (uncompressed format) + qx-bytes + qy-bytes
 3208                var q = new byte[1 + qx.Length + qy.Length];
 3209                Buffer.SetByte(q, 0, 4);
 3210                Buffer.BlockCopy(qx, 0, q, 1, qx.Length);
 3211                Buffer.BlockCopy(qy, 0, q, qx.Length + 1, qy.Length);
 212
 213                // returns Curve-Name and x/y as ECPoint
 3214                return new[] { new BigInteger(curve.Reverse()), new BigInteger(q.Reverse()) };
 3215            }
 216            set
 0217            {
 0218                var curve_s = Encoding.ASCII.GetString(value[0].ToByteArray().Reverse());
 0219                var curve_oid = GetCurveOid(curve_s);
 220
 0221                var publickey = value[1].ToByteArray().Reverse();
 0222                Import(curve_oid, publickey, privatekey: null);
 0223            }
 224        }
 225
 226        /// <summary>
 227        /// Gets the PrivateKey Bytes.
 228        /// </summary>
 39229        public byte[] PrivateKey { get; private set; }
 230
 231        /// <summary>
 232        /// Gets the <see cref="ECDsa"/> object.
 233        /// </summary>
 87234        public ECDsa Ecdsa { get; private set; }
 235
 236        /// <summary>
 237        /// Initializes a new instance of the <see cref="EcdsaKey"/> class.
 238        /// </summary>
 0239        public EcdsaKey()
 0240        {
 0241        }
 242
 243        /// <summary>
 244        /// Initializes a new instance of the <see cref="EcdsaKey"/> class.
 245        /// </summary>
 246        /// <param name="curve">The curve name.</param>
 247        /// <param name="publickey">Value of publickey.</param>
 248        /// <param name="privatekey">Value of privatekey.</param>
 20249        public EcdsaKey(string curve, byte[] publickey, byte[] privatekey)
 20250        {
 20251            Import(GetCurveOid(curve), publickey, privatekey);
 20252        }
 253
 254        /// <summary>
 255        /// Initializes a new instance of the <see cref="EcdsaKey"/> class.
 256        /// </summary>
 257        /// <param name="data">DER encoded private key data.</param>
 19258        public EcdsaKey(byte[] data)
 19259        {
 19260            var der = new DerData(data);
 19261            _ = der.ReadBigInteger(); // skip version
 262
 263            // PrivateKey
 19264            var privatekey = der.ReadOctetString().TrimLeadingZeros();
 265
 266            // Construct
 19267            var s0 = der.ReadByte();
 19268            if ((s0 & 0xe0) != 0xa0)
 0269            {
 0270                throw new SshException(string.Format("UnexpectedDER: wanted constructed tag (0xa0-0xbf), got: {0:X}", s0
 271            }
 272
 19273            var tag = s0 & 0x1f;
 19274            if (tag != 0)
 0275            {
 0276                throw new SshException(string.Format("expected tag 0 in DER privkey, got: {0}", tag));
 277            }
 278
 19279            var construct = der.ReadBytes(der.ReadLength()); // object length
 280
 281            // curve OID
 19282            var curve_der = new DerData(construct, construct: true);
 19283            var curve = curve_der.ReadObject();
 284
 285            // Construct
 19286            s0 = der.ReadByte();
 19287            if ((s0 & 0xe0) != 0xa0)
 0288            {
 0289                throw new SshException(string.Format("UnexpectedDER: wanted constructed tag (0xa0-0xbf), got: {0:X}", s0
 290            }
 291
 19292            tag = s0 & 0x1f;
 19293            if (tag != 1)
 0294            {
 0295                throw new SshException(string.Format("expected tag 1 in DER privkey, got: {0}", tag));
 296            }
 297
 19298            construct = der.ReadBytes(der.ReadLength()); // object length
 299
 300            // PublicKey
 19301            var pubkey_der = new DerData(construct, construct: true);
 19302            var pubkey = pubkey_der.ReadBitString().TrimLeadingZeros();
 303
 19304            Import(OidByteArrayToString(curve), pubkey, privatekey);
 19305        }
 306
 307        private void Import(string curve_oid, byte[] publickey, byte[] privatekey)
 39308        {
 309#if NETFRAMEWORK
 310            KeyBlobMagicNumber curve_magic;
 311
 12312            switch (GetCurveName(curve_oid))
 313            {
 314                case "nistp256":
 4315                    if (privatekey != null)
 4316                    {
 4317                        curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P256_MAGIC;
 4318                    }
 319                    else
 0320                    {
 0321                        curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
 0322                    }
 323
 4324                    break;
 325                case "nistp384":
 4326                    if (privatekey != null)
 4327                    {
 4328                        curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P384_MAGIC;
 4329                    }
 330                    else
 0331                    {
 0332                        curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC;
 0333                    }
 334
 4335                    break;
 336                case "nistp521":
 4337                    if (privatekey != null)
 4338                    {
 4339                        curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P521_MAGIC;
 4340                    }
 341                    else
 0342                    {
 0343                        curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC;
 0344                    }
 345
 4346                    break;
 347                default:
 0348                    throw new SshException("Unknown: " + curve_oid);
 349            }
 350
 351            // ECPoint as BigInteger(2)
 12352            var cord_size = (publickey.Length - 1) / 2;
 12353            var qx = new byte[cord_size];
 12354            Buffer.BlockCopy(publickey, 1, qx, 0, qx.Length);
 355
 12356            var qy = new byte[cord_size];
 12357            Buffer.BlockCopy(publickey, cord_size + 1, qy, 0, qy.Length);
 358
 12359            if (privatekey != null)
 12360            {
 12361                privatekey = privatekey.Pad(cord_size);
 12362                PrivateKey = privatekey;
 12363            }
 364
 12365            var headerSize = Marshal.SizeOf(typeof(BCRYPT_ECCKEY_BLOB));
 12366            var blobSize = headerSize + qx.Length + qy.Length;
 12367            if (privatekey != null)
 12368            {
 12369                blobSize += privatekey.Length;
 12370            }
 371
 12372            var blob = new byte[blobSize];
 12373            using (var bw = new BinaryWriter(new MemoryStream(blob)))
 12374            {
 12375                bw.Write((int)curve_magic);
 12376                bw.Write(cord_size);
 12377                bw.Write(qx); // q.x
 12378                bw.Write(qy); // q.y
 12379                if (privatekey != null)
 12380                {
 12381                    bw.Write(privatekey); // d
 12382                }
 12383            }
 384
 12385            _key = CngKey.Import(blob, privatekey is null ? CngKeyBlobFormat.EccPublicBlob : CngKeyBlobFormat.EccPrivate
 386
 12387            Ecdsa = new ECDsaCng(_key);
 388#else
 27389            var curve = ECCurve.CreateFromValue(curve_oid);
 27390            var parameter = new ECParameters
 27391            {
 27392                Curve = curve
 27393            };
 394
 395            // ECPoint as BigInteger(2)
 27396            var cord_size = (publickey.Length - 1) / 2;
 27397            var qx = new byte[cord_size];
 27398            Buffer.BlockCopy(publickey, 1, qx, 0, qx.Length);
 399
 27400            var qy = new byte[cord_size];
 27401            Buffer.BlockCopy(publickey, cord_size + 1, qy, 0, qy.Length);
 402
 27403            parameter.Q.X = qx;
 27404            parameter.Q.Y = qy;
 405
 27406            if (privatekey != null)
 27407            {
 27408                parameter.D = privatekey.TrimLeadingZeros().Pad(cord_size);
 27409                PrivateKey = parameter.D;
 27410            }
 411
 27412            Ecdsa = ECDsa.Create(parameter);
 413#endif
 39414        }
 415
 416        private static string GetCurveOid(string curve_s)
 20417        {
 20418            switch (curve_s.ToUpperInvariant())
 419            {
 420                case "NISTP256":
 6421                    return ECDSA_P256_OID_VALUE;
 422                case "NISTP384":
 7423                    return ECDSA_P384_OID_VALUE;
 424                case "NISTP521":
 7425                    return ECDSA_P521_OID_VALUE;
 426                default:
 0427                    throw new SshException("Unexpected Curve Name: " + curve_s);
 428            }
 20429        }
 430
 431#if NETFRAMEWORK
 432        private static string GetCurveName(string oid)
 12433        {
 12434            switch (oid)
 435            {
 436                case ECDSA_P256_OID_VALUE:
 4437                    return "nistp256";
 438                case ECDSA_P384_OID_VALUE:
 4439                    return "nistp384";
 440                case ECDSA_P521_OID_VALUE:
 4441                    return "nistp521";
 442                default:
 0443                    throw new SshException("Unexpected OID: " + oid);
 444            }
 12445        }
 446#endif // NETFRAMEWORK
 447
 448        private static string OidByteArrayToString(byte[] oid)
 19449        {
 19450            var retVal = new StringBuilder();
 451
 218452            for (var i = 0; i < oid.Length; i++)
 90453            {
 90454                if (i == 0)
 19455                {
 19456                    var b = oid[0] % 40;
 19457                    var a = (oid[0] - b) / 40;
 19458                    _ = retVal.AppendFormat("{0}.{1}", a, b);
 19459                }
 460                else
 71461                {
 71462                    if (oid[i] < 128)
 45463                    {
 45464                        _ = retVal.AppendFormat(".{0}", oid[i]);
 45465                    }
 466                    else
 26467                    {
 26468                        _ = retVal.AppendFormat(".{0}", ((oid[i] - 128) * 128) + oid[i + 1]);
 26469                        i++;
 26470                    }
 71471                }
 90472            }
 473
 19474            return retVal.ToString();
 19475        }
 476
 477        /// <summary>
 478        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
 479        /// </summary>
 480        public void Dispose()
 0481        {
 0482            Dispose(disposing: true);
 0483            GC.SuppressFinalize(this);
 0484        }
 485
 486        /// <summary>
 487        /// Releases unmanaged and - optionally - managed resources.
 488        /// </summary>
 489        /// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langwor
 490        protected virtual void Dispose(bool disposing)
 39491        {
 39492            if (_isDisposed)
 0493            {
 0494                return;
 495            }
 496
 39497            if (disposing)
 0498            {
 0499                _isDisposed = true;
 0500            }
 39501        }
 502
 503        /// <summary>
 504        /// Finalizes an instance of the <see cref="EcdsaKey"/> class.
 505        /// </summary>
 506        ~EcdsaKey()
 78507        {
 39508            Dispose(disposing: false);
 78509        }
 510    }
 511}