3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 using System.Diagnostics;
10 using System.Security;
11 using System.Security.Permissions;
12 using System.Diagnostics.Contracts;
13 using Microsoft.Win32.SafeHandles;
15 namespace System.Security.Cryptography {
17 /// Wrapper for NCrypt's implementation of elliptic curve DSA
19 [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
20 public sealed class ECDsaCng : ECDsa {
22 public override byte[] SignHash(byte[] hash) {
23 throw new NotImplementedException();
26 public override bool VerifyHash(byte[] hash, byte[] signature) {
27 throw new NotImplementedException();
30 private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(256, 384, 128), new KeySizes(521, 521, 0) };
33 private CngAlgorithm m_hashAlgorithm = CngAlgorithm.Sha256;
39 public ECDsaCng() : this(521) {
40 Contract.Ensures(LegalKeySizesValue != null);
43 public ECDsaCng(int keySize) {
44 Contract.Ensures(LegalKeySizesValue != null);
46 if (!NCryptNative.NCryptSupported) {
47 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
50 LegalKeySizesValue = s_legalKeySizes;
54 [SecuritySafeCritical]
55 public ECDsaCng(CngKey key) {
56 Contract.Ensures(LegalKeySizesValue != null);
57 Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDsa);
60 throw new ArgumentNullException("key");
62 if (key.AlgorithmGroup != CngAlgorithmGroup.ECDsa) {
63 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey), "key");
66 if (!NCryptNative.NCryptSupported) {
67 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
70 LegalKeySizesValue = s_legalKeySizes;
72 // Make a copy of the key so that we continue to work if it gets disposed before this algorithm
74 // This requires an assert for UnmanagedCode since we'll need to access the raw handles of the key
75 // and the handle constructor of CngKey. The assert is safe since ECDsaCng will never expose the
76 // key handles to calling code (without first demanding UnmanagedCode via the Handle property of
79 // We also need to dispose of the key handle since CngKey.Handle returns a duplicate
80 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
81 using (SafeNCryptKeyHandle keyHandle = key.Handle) {
82 Key = CngKey.Open(keyHandle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
84 CodeAccessPermission.RevertAssert();
86 KeySize = m_key.KeySize;
90 /// Hash algorithm to use when generating a signature over arbitrary data
92 public CngAlgorithm HashAlgorithm {
94 Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
95 return m_hashAlgorithm;
99 Contract.Ensures(m_hashAlgorithm != null);
102 throw new ArgumentNullException("value");
105 m_hashAlgorithm = value;
110 /// Key to use for signing
114 Contract.Ensures(Contract.Result<CngKey>() != null);
115 Contract.Ensures(Contract.Result<CngKey>().AlgorithmGroup == CngAlgorithmGroup.ECDsa);
116 Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDsa);
118 // If the size of the key no longer matches our stored value, then we need to replace it with
119 // a new key of the correct size.
120 if (m_key != null && m_key.KeySize != KeySize) {
126 // Map the current key size to a CNG algorithm name
127 CngAlgorithm algorithm = null;
130 algorithm = CngAlgorithm.ECDsaP256;
134 algorithm = CngAlgorithm.ECDsaP384;
138 algorithm = CngAlgorithm.ECDsaP521;
142 Debug.Assert(false, "Illegal key size set");
146 m_key = CngKey.Create(algorithm);
153 Contract.Requires(value != null);
154 Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDsa);
156 if (value.AlgorithmGroup != CngAlgorithmGroup.ECDsa) {
157 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey));
165 // We do not duplicate the handle because the only time the user has access to the key itself
166 // to dispose underneath us is when they construct via the CngKey constructor, which does a
167 // copy. Otherwise all key lifetimes are controlled directly by the ECDsaCng class.
171 KeySize = m_key.KeySize;
176 /// Clean up the algorithm
178 protected override void Dispose(bool disposing) {
185 base.Dispose(disposing);
194 // There is currently not a standard XML format for ECC keys, so we will not implement the default
195 // To/FromXmlString so that we're not tied to one format when a standard one does exist. Instead we'll
196 // use an overload which allows the user to specify the format they'd like to serialize into.
198 // See code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
199 // the currently supported format.
202 public override void FromXmlString(string xmlString) {
203 throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
206 public void FromXmlString(string xml, ECKeyXmlFormat format) {
208 throw new ArgumentNullException("xml");
210 if (format != ECKeyXmlFormat.Rfc4050) {
211 throw new ArgumentOutOfRangeException("format");
214 Key = Rfc4050KeyFormatter.FromXml(xml);
218 // Signature generation
221 public byte[] SignData(byte[] data) {
222 Contract.Ensures(Contract.Result<byte[]>() != null);
225 throw new ArgumentNullException("data");
228 return SignData(data, 0, data.Length);
231 [SecuritySafeCritical]
232 public byte[] SignData(byte[] data, int offset, int count) {
233 Contract.Ensures(Contract.Result<byte[]>() != null);
236 throw new ArgumentNullException("data");
238 if (offset < 0 || offset > data.Length) {
239 throw new ArgumentOutOfRangeException("offset");
241 if (count < 0 || count > data.Length - offset) {
242 throw new ArgumentOutOfRangeException("count");
245 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
246 hashAlgorithm.HashCore(data, offset, count);
247 byte[] hashValue = hashAlgorithm.HashFinal();
249 return SignHash(hashValue);
253 [SecuritySafeCritical]
254 public byte[] SignData(Stream data) {
255 Contract.Ensures(Contract.Result<byte[]>() != null);
258 throw new ArgumentNullException("data");
261 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
262 hashAlgorithm.HashStream(data);
263 byte[] hashValue = hashAlgorithm.HashFinal();
265 return SignHash(hashValue);
269 [SecuritySafeCritical]
270 public override byte[] SignHash(byte[] hash) {
272 throw new ArgumentNullException("hash");
275 // Make sure we're allowed to sign using this key
276 KeyContainerPermission permission = Key.BuildKeyContainerPermission(KeyContainerPermissionFlags.Sign);
277 if (permission != null) {
281 // Now that know we have permission to use this key for signing, pull the key value out, which
282 // will require unmanaged code permission
283 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
285 // This looks odd, but the key handle is actually a duplicate so we need to dispose it
286 using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
287 CodeAccessPermission.RevertAssert();
289 return NCryptNative.SignHash(keyHandle, hash);
296 // See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
297 // code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
298 // XML serialization of elliptic curve keys
301 public override string ToXmlString(bool includePrivateParameters) {
302 throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
305 public string ToXmlString(ECKeyXmlFormat format) {
306 Contract.Ensures(Contract.Result<string>() != null);
308 if (format != ECKeyXmlFormat.Rfc4050) {
309 throw new ArgumentOutOfRangeException("format");
312 return Rfc4050KeyFormatter.ToXml(Key);
316 // Signature verification
319 public bool VerifyData(byte[] data, byte[] signature) {
321 throw new ArgumentNullException("data");
324 return VerifyData(data, 0, data.Length, signature);
327 [SecuritySafeCritical]
328 public bool VerifyData(byte[] data, int offset, int count, byte[] signature) {
330 throw new ArgumentNullException("data");
332 if (offset < 0 || offset > data.Length) {
333 throw new ArgumentOutOfRangeException("offset");
335 if (count < 0 || count > data.Length - offset) {
336 throw new ArgumentOutOfRangeException("count");
338 if (signature == null) {
339 throw new ArgumentNullException("signature");
342 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
343 hashAlgorithm.HashCore(data, offset, count);
344 byte[] hashValue = hashAlgorithm.HashFinal();
346 return VerifyHash(hashValue, signature);
350 [SecuritySafeCritical]
351 public bool VerifyData(Stream data, byte[] signature) {
353 throw new ArgumentNullException("data");
355 if (signature == null) {
356 throw new ArgumentNullException("signature");
359 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
360 hashAlgorithm.HashStream(data);
361 byte[] hashValue = hashAlgorithm.HashFinal();
363 return VerifyHash(hashValue, signature);
367 [SecuritySafeCritical]
368 public override bool VerifyHash(byte[] hash, byte[] signature) {
370 throw new ArgumentNullException("hash");
372 if (signature == null) {
373 throw new ArgumentNullException("signature");
376 // We need to get the raw key handle to verify the signature. Asserting here is safe since verifiation
377 // is not a protected operation, and we do not expose the handle to the user code.
378 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
380 // This looks odd, but Key.Handle is really a duplicate so we need to dispose it
381 using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
382 CodeAccessPermission.RevertAssert();
384 return NCryptNative.VerifySignature(keyHandle, hash, signature);
389 /// Helper property to get the NCrypt key handle
391 private SafeNCryptKeyHandle KeyHandle {
392 [SecuritySafeCritical]
393 get { return Key.Handle; }
396 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
397 // we're sealed and the base should have checked this before calling us
398 Debug.Assert(data != null);
399 Debug.Assert(offset >= 0 && offset <= data.Length);
400 Debug.Assert(count >= 0 && count <= data.Length - offset);
401 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
403 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
404 hasher.HashCore(data, offset, count);
405 return hasher.HashFinal();
409 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
410 // we're sealed and the base should have checked this before calling us
411 Debug.Assert(data != null);
412 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
414 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
415 hasher.HashStream(data);
416 return hasher.HashFinal();