1 //! Implementation of asymmetric cryptography using Windows CNG API.
2 #![allow(unused_variables)]
3 
4 use std::time::SystemTime;
5 use std::convert::TryInto;
6 
7 use crate::{Error, Result};
8 
9 use crate::crypto::asymmetric::{Decryptor, KeyPair, Signer};
10 use crate::crypto::mem::Protected;
11 use crate::crypto::mpi;
12 use crate::crypto::SessionKey;
13 use crate::packet::key::{Key4, SecretParts};
14 use crate::packet::{self, key, Key};
15 use crate::types::{PublicKeyAlgorithm, SymmetricAlgorithm};
16 use crate::types::{Curve, HashAlgorithm};
17 
18 use num_bigint_dig::{traits::ModInverse, BigInt, BigUint};
19 use win_crypto_ng as cng;
20 
21 const CURVE25519_SIZE: usize = 32;
22 
23 impl Signer for KeyPair {
public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole>24     fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
25         KeyPair::public(self)
26     }
27 
sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8]) -> Result<mpi::Signature>28     fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8]) -> Result<mpi::Signature> {
29         use cng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
30         use cng::asymmetric::{AsymmetricKey, Private, Rsa};
31         use cng::asymmetric::signature::{Signer, SignaturePadding};
32         use cng::key_blob::RsaKeyPrivatePayload;
33         use cng::key_blob::EccKeyPrivatePayload;
34         use cng::asymmetric::ecc::NamedCurve;
35 
36         #[allow(deprecated)]
37         self.secret().map(|secret| {
38             Ok(match (self.public().pk_algo(), self.public().mpis(), secret) {
39                 (PublicKeyAlgorithm::RSAEncryptSign,
40                     &mpi::PublicKey::RSA { ref e, ref n },
41                     &mpi::SecretKeyMaterial::RSA { ref p, ref q, ref d, .. }) |
42                 (PublicKeyAlgorithm::RSASign,
43                 &mpi::PublicKey::RSA { ref e, ref n },
44                 &mpi::SecretKeyMaterial::RSA { ref p, ref q, ref d, .. }) => {
45                     let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa)?;
46                     let key = AsymmetricKey::<Rsa, Private>::import_from_parts(
47                         &provider,
48                         &RsaKeyPrivatePayload {
49                             modulus: n.value(),
50                             pub_exp: e.value(),
51                             prime1: p.value(),
52                             prime2: q.value(),
53                         }
54                     )?;
55 
56                     // As described in [Section 5.2.2 and 5.2.3 of RFC 4880],
57                     // to verify the signature, we need to encode the
58                     // signature data in a PKCS1-v1.5 packet.
59                     //
60                     //   [Section 5.2.2 and 5.2.3 of RFC 4880]:
61                     //   https://tools.ietf.org/html/rfc4880#section-5.2.2
62                     let hash = hash_algo.try_into()?;
63                     let padding = SignaturePadding::pkcs1(hash);
64                     let sig = key.sign(digest, Some(padding))?;
65 
66                     mpi::Signature::RSA { s: mpi::MPI::new(&*sig) }
67                 },
68                 (PublicKeyAlgorithm::ECDSA,
69                 mpi::PublicKey::ECDSA { curve, q },
70                 mpi::SecretKeyMaterial::ECDSA { scalar }) =>
71                 {
72                     let (x, y) = q.decode_point(curve)?;
73 
74                     // It's expected for the private key to be exactly 32/48/66
75                     // (respective curve field size) bytes long but OpenPGP
76                     // allows leading zeros to be stripped.
77                     // Padding has to be unconditional; otherwise we have a
78                     // secret-dependent branch.
79                     let curve_bits = curve.bits().ok_or_else(||
80                         Error::UnsupportedEllipticCurve(curve.clone())
81                     )?;
82                     let curve_bytes = (curve_bits + 7) / 8;
83                     let mut secret = Protected::from(vec![0u8; curve_bytes]);
84                     let missing = curve_bytes.saturating_sub(scalar.value().len());
85                     secret.as_mut()[missing..].copy_from_slice(scalar.value());
86 
87                     use cng::asymmetric::{ecc::{NistP256, NistP384, NistP521}, Ecdsa};
88 
89                     // TODO: Improve CNG public API
90                     let sig = match curve {
91                         Curve::NistP256 => {
92                             let provider = AsymmetricAlgorithm::open(
93                                 AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP256)
94                             )?;
95                             let key = AsymmetricKey::<Ecdsa<NistP256>, Private>::import_from_parts(
96                                 &provider,
97                                 &EccKeyPrivatePayload { x, y, d: &secret }
98                             )?;
99                             key.sign(digest, None)?
100                         },
101                         Curve::NistP384 => {
102                             let provider = AsymmetricAlgorithm::open(
103                                 AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP384)
104                             )?;
105                             let key = AsymmetricKey::<Ecdsa<NistP384>, Private>::import_from_parts(
106                                 &provider,
107                                 &EccKeyPrivatePayload { x, y, d: &secret }
108                             )?;
109                             key.sign(digest, None)?
110                         },
111                         Curve::NistP521 => {
112                             let provider = AsymmetricAlgorithm::open(
113                                 AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP521)
114                             )?;
115                             let key = AsymmetricKey::<Ecdsa<NistP521>, Private>::import_from_parts(
116                                 &provider,
117                                 &EccKeyPrivatePayload { x, y, d: &secret }
118                             )?;
119                             key.sign(digest, None)?
120                         },
121                         _ => return Err(
122                             Error::UnsupportedEllipticCurve(curve.clone()).into()),
123                     };
124 
125                     // CNG outputs a P1363 formatted signature - r || s
126                     let (r, s) = sig.split_at(sig.len() / 2);
127                     mpi::Signature::ECDSA {
128                         r: mpi::MPI::new(r),
129                         s: mpi::MPI::new(s),
130                     }
131                 },
132                 (
133                     PublicKeyAlgorithm::EdDSA,
134                     mpi::PublicKey::EdDSA { curve, q },
135                     mpi::SecretKeyMaterial::EdDSA { scalar },
136                 ) => {
137                     // CNG doesn't support EdDSA, use ed25519-dalek instead
138                     use ed25519_dalek::{Keypair, Signer};
139                     use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH};
140 
141                     let (public, ..) = q.decode_point(&Curve::Ed25519)?;
142 
143                     // It's expected for the private key to be exactly
144                     // SECRET_KEY_LENGTH bytes long but OpenPGP allows leading
145                     // zeros to be stripped.
146                     // Padding has to be unconditional; otherwise we have a
147                     // secret-dependent branch.
148                     let missing = SECRET_KEY_LENGTH.saturating_sub(scalar.value().len());
149                     let mut keypair = Protected::from(
150                         vec![0u8; SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH]
151                     );
152                     keypair.as_mut()[missing..SECRET_KEY_LENGTH].copy_from_slice(scalar.value());
153                     keypair.as_mut()[SECRET_KEY_LENGTH..].copy_from_slice(&public);
154                     let pair = Keypair::from_bytes(&keypair).unwrap();
155 
156                     let sig = pair.sign(digest).to_bytes();
157 
158                     // https://tools.ietf.org/html/rfc8032#section-5.1.6
159                     let (r, s) = sig.split_at(sig.len() / 2);
160                     mpi::Signature::EdDSA {
161                         r: mpi::MPI::new(r),
162                         s: mpi::MPI::new(s),
163                     }
164                 },
165                 (PublicKeyAlgorithm::DSA,
166                     mpi:: PublicKey::DSA { y, p, q, g },
167                     mpi::SecretKeyMaterial::DSA { x },
168                 ) => {
169                     use win_crypto_ng::key_blob::{DsaKeyPrivateV2Payload, DsaKeyPrivateV2Blob};
170                     use win_crypto_ng::key_blob::{DsaKeyPrivatePayload, DsaKeyPrivateBlob};
171                     use win_crypto_ng::asymmetric::{Dsa, DsaPrivateBlob};
172                     use win_crypto_ng::helpers::Blob;
173 
174                     if y.value().len() > 3072 / 8 {
175                         return Err(Error::InvalidOperation(
176                             "DSA keys are supported up to 3072-bits".to_string()).into()
177                         );
178                     }
179 
180                     enum Version { V1, V2 }
181                     // 1024-bit DSA keys are handled differently
182                     let version = if y.value().len() <= 128 { Version::V1 } else { Version::V2 };
183 
184                     let blob: DsaPrivateBlob = match version {
185                         Version::V1 => {
186                             let mut group = [0; 20];
187                             assert!(q.value().len() >= 20);
188                             &mut group[..q.value().len()].copy_from_slice(q.value());
189 
190                             DsaPrivateBlob::V1(Blob::<DsaKeyPrivateBlob>::clone_from_parts(
191                                 &winapi::shared::bcrypt::BCRYPT_DSA_KEY_BLOB {
192                                     dwMagic: winapi::shared::bcrypt::BCRYPT_DSA_PUBLIC_MAGIC,
193                                     cbKey: y.value().len() as u32,
194                                     Count: [0; 4], // unused
195                                     Seed: [0; 20], // unused
196                                     q: group,
197                                 },
198                                 &DsaKeyPrivatePayload {
199                                     modulus: p.value(),
200                                     generator: g.value(),
201                                     public: y.value(),
202                                     priv_exp: x.value(),
203                                 },
204                             ))
205                         },
206                         Version::V2 => {
207                             // https://github.com/dotnet/runtime/blob/67d74fca70d4670ad503e23dba9d6bc8a1b5909e/src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs#L276-L282
208                             let hash = match q.value().len() {
209                                 20 => 0,
210                                 32 => 1,
211                                 64 => 2,
212                                 _ => return Err(Error::InvalidOperation(
213                                     "CNG accepts DSA q with length of either length of 20, 32 or 64".into())
214                                     .into()),
215                             };
216 
217                             // We don't use counter/seed values so set them to 0.
218                             // CNG pre-checks that the seed is at least |Q| long,
219                             // so we can't use an empty buffer here.
220                             let (count, seed) = ([0x0; 4], vec![0x0; q.value().len()]);
221 
222                             DsaPrivateBlob::V2(Blob::<DsaKeyPrivateV2Blob>::clone_from_parts(
223                                 &winapi::shared::bcrypt::BCRYPT_DSA_KEY_BLOB_V2 {
224                                     dwMagic: winapi::shared::bcrypt::BCRYPT_DSA_PRIVATE_MAGIC_V2,
225                                     Count: count,
226                                     // Size of the prime number q.
227                                     // Currently, if the key is less than 128
228                                     // bits, q is 20 bytes long.
229                                     // If the key exceeds 256 bits, q is 32 bytes long.
230                                     cbGroupSize: std::cmp::min(q.value().len(), 32) as u32,
231                                     cbKey: y.value().len() as u32,
232                                     cbSeedLength: seed.len() as u32,
233                                     hashAlgorithm: hash,
234                                     standardVersion: 1, // FIPS 186-3
235 
236                                 },
237                                 &DsaKeyPrivateV2Payload {
238                                     seed: &seed,
239                                     group: q.value(),
240                                     modulus: p.value(),
241                                     generator: g.value(),
242                                     public: y.value(),
243                                     priv_exp: x.value(),
244                                 },
245                             ))
246                         },
247                     };
248 
249                     use win_crypto_ng::asymmetric::{Import};
250 
251                     let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Dsa)?;
252                     let pair = AsymmetricKey::<Dsa, Private>::import(
253                         Dsa,
254                         &provider,
255                         blob
256                     )?;
257 
258                     // CNG accepts only hash and Q of equal length. Either trim the
259                     // digest or pad it with zeroes (since it's treated as a
260                     // big-endian number).
261                     // See https://github.com/dotnet/runtime/blob/67d74fca70d4670ad503e23dba9d6bc8a1b5909e/src/libraries/Common/src/System/Security/Cryptography/DSACng.SignVerify.cs#L148.
262                     let mut _digest = vec![];
263                     let digest = match std::cmp::Ord::cmp(&q.value().len(), &digest.len()) {
264                         std::cmp::Ordering::Equal => digest,
265                         std::cmp::Ordering::Less => &digest[..q.value().len()],
266                         std::cmp::Ordering::Greater => {
267                             let pad = vec![0; q.value().len() - digest.len()];
268                             _digest = [pad.as_ref(), digest].concat();
269                             &_digest
270                         }
271                     };
272                     assert_eq!(q.value().len(), digest.len());
273 
274                     let sig = pair.sign(digest, None)?;
275 
276                     // https://tools.ietf.org/html/rfc8032#section-5.1.6
277                     let (r, s) = sig.split_at(sig.len() / 2);
278                     mpi::Signature::DSA {
279                         r: mpi::MPI::new(r),
280                         s: mpi::MPI::new(s),
281                     }
282                 },
283                 (pk_algo, _, _) => Err(Error::InvalidOperation(format!(
284                     "unsupported combination of algorithm {:?}, key {:?}, \
285                      and secret key {:?}",
286                     pk_algo, self.public(), self.secret())))?,
287             })
288         })
289     }
290 }
291 
292 impl Decryptor for KeyPair {
public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole>293     fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
294         KeyPair::public(self)
295     }
296 
297     /// Creates a signature over the `digest` produced by `hash_algo`.
decrypt( &mut self, ciphertext: &mpi::Ciphertext, plaintext_len: Option<usize>, ) -> Result<SessionKey>298     fn decrypt(
299         &mut self,
300         ciphertext: &mpi::Ciphertext,
301         plaintext_len: Option<usize>,
302     ) -> Result<SessionKey> {
303         use crate::PublicKeyAlgorithm::*;
304 
305         self.secret().map(
306             |secret| Ok(match (self.public().mpis(), secret, ciphertext)
307         {
308             (mpi::PublicKey::RSA { ref e, ref n },
309              mpi::SecretKeyMaterial::RSA { ref p, ref q, ref d, .. },
310              mpi::Ciphertext::RSA { ref c }) => {
311                 use cng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
312                 use cng::asymmetric::{AsymmetricKey, Private, Rsa};
313                 use cng::asymmetric::EncryptionPadding;
314                 use cng::key_blob::RsaKeyPrivatePayload;
315 
316                 let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa)?;
317                 let key = AsymmetricKey::<Rsa, Private>::import_from_parts(
318                     &provider,
319                     &RsaKeyPrivatePayload {
320                         modulus: n.value(),
321                         pub_exp: e.value(),
322                         prime1: p.value(),
323                         prime2: q.value(),
324                     }
325                 )?;
326 
327                 let decrypted = key.decrypt(Some(EncryptionPadding::Pkcs1), c.value())?;
328 
329                 SessionKey::from(decrypted)
330             }
331 
332             (mpi::PublicKey::ElGamal { .. },
333              mpi::SecretKeyMaterial::ElGamal { .. },
334              mpi::Ciphertext::ElGamal { .. }) =>
335                 return Err(
336                     Error::UnsupportedPublicKeyAlgorithm(ElGamalEncrypt).into()),
337 
338             (mpi::PublicKey::ECDH{ .. },
339              mpi::SecretKeyMaterial::ECDH { .. },
340              mpi::Ciphertext::ECDH { .. }) =>
341                 crate::crypto::ecdh::decrypt(self.public(), secret, ciphertext)?,
342 
343             (public, secret, ciphertext) =>
344                 return Err(Error::InvalidOperation(format!(
345                     "unsupported combination of key pair {:?}/{:?} \
346                      and ciphertext {:?}",
347                     public, secret, ciphertext)).into()),
348         }))
349     }
350 }
351 
352 impl<P: key::KeyParts, R: key::KeyRole> Key<P, R> {
353     /// Encrypts the given data with this key.
encrypt(&self, data: &SessionKey) -> Result<mpi::Ciphertext>354     pub fn encrypt(&self, data: &SessionKey) -> Result<mpi::Ciphertext> {
355         use cng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
356         use cng::asymmetric::{AsymmetricKey, Public, Rsa};
357         use cng::key_blob::RsaKeyPublicPayload;
358 
359         use PublicKeyAlgorithm::*;
360 
361         #[allow(deprecated)]
362         match self.pk_algo() {
363             RSAEncryptSign | RSAEncrypt => {
364                 // Extract the public recipient.
365                 match self.mpis() {
366                     mpi::PublicKey::RSA { e, n } => {
367                         // The ciphertext has the length of the modulus.
368                         let ciphertext_len = n.value().len();
369                         if data.len() + 11 > ciphertext_len {
370                             return Err(Error::InvalidArgument(
371                                 "Plaintext data too large".into()).into());
372                         }
373 
374                         let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa)?;
375                         let key = AsymmetricKey::<Rsa, Public>::import_from_parts(
376                             &provider,
377                             &RsaKeyPublicPayload {
378                                 modulus: n.value(),
379                                 pub_exp: e.value(),
380                             }
381                         )?;
382 
383                         let padding = win_crypto_ng::asymmetric::EncryptionPadding::Pkcs1;
384                         let ciphertext = key.encrypt(Some(padding), data)?;
385 
386                         Ok(mpi::Ciphertext::RSA {
387                             c: mpi::MPI::new(ciphertext.as_ref()),
388                         })
389                     },
390                     pk => {
391                         Err(Error::MalformedPacket(
392                             format!(
393                                 "Key: Expected RSA public key, got {:?}",
394                                 pk)).into())
395                     },
396                 }
397             },
398             ECDH => crate::crypto::ecdh::encrypt(self.parts_as_public(), data),
399             algo => Err(Error::UnsupportedPublicKeyAlgorithm(algo).into()),
400         }
401     }
402 
403     /// Verifies the given signature.
verify(&self, sig: &packet::Signature, digest: &[u8]) -> Result<()>404     pub fn verify(&self, sig: &packet::Signature, digest: &[u8]) -> Result<()> {
405         use cng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
406         use cng::asymmetric::{AsymmetricKey, Public, Rsa};
407         use cng::asymmetric::ecc::NamedCurve;
408         use cng::asymmetric::signature::{Verifier, SignaturePadding};
409         use cng::key_blob::RsaKeyPublicPayload;
410 
411         use PublicKeyAlgorithm::*;
412 
413         #[allow(deprecated)]
414         let ok = match (sig.pk_algo(), self.mpis(), sig.mpis()) {
415             (RSASign,        mpi::PublicKey::RSA { e, n }, mpi::Signature::RSA { s }) |
416             (RSAEncryptSign, mpi::PublicKey::RSA { e, n }, mpi::Signature::RSA { s }) => {
417                 // CNG accepts only full-size signatures. Since for RSA it's a
418                 // big-endian number, just left-pad with zeroes as necessary.
419                 let sig_diff = n.value().len().saturating_sub(s.value().len());
420                 let mut _s: Vec<u8> = vec![];
421                 let s = if sig_diff > 0 {
422                     _s = [&vec![0u8; sig_diff], s.value()].concat();
423                     &_s
424                 } else {
425                     s.value()
426                 };
427 
428                 // CNG supports RSA keys that are at least 512 bit long.
429                 // Since it just checks the MPI length rather than data itself,
430                 // just pad it with zeroes as necessary.
431                 let mut _n: Vec<u8> = vec![];
432                 let missing_size = 512usize.saturating_sub(n.value().len());
433                 let n = if missing_size > 0 {
434                     _n = [&vec![0u8; missing_size], n.value()].concat();
435                     &_n
436                 } else {
437                     n.value()
438                 };
439 
440                 let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa)?;
441                 let key = AsymmetricKey::<Rsa, Public>::import_from_parts(
442                     &provider,
443                     &RsaKeyPublicPayload {
444                         modulus: n,
445                         pub_exp: e.value(),
446                     }
447                 )?;
448 
449                 // As described in [Section 5.2.2 and 5.2.3 of RFC 4880],
450                 // to verify the signature, we need to encode the
451                 // signature data in a PKCS1-v1.5 packet.
452                 //
453                 //   [Section 5.2.2 and 5.2.3 of RFC 4880]:
454                 //   https://tools.ietf.org/html/rfc4880#section-5.2.2
455                 let hash = sig.hash_algo().try_into()?;
456                 let padding = SignaturePadding::pkcs1(hash);
457 
458                 key.verify(digest, s, Some(padding)).map(|_| true)?
459             },
460             (DSA, mpi:: PublicKey::DSA { y, p, q, g }, mpi::Signature::DSA { r, s }) => {
461                 use win_crypto_ng::key_blob::{DsaKeyPublicPayload, DsaKeyPublicBlob};
462                 use win_crypto_ng::key_blob::{DsaKeyPublicV2Payload, DsaKeyPublicV2Blob};
463                 use win_crypto_ng::asymmetric::{Dsa, DsaPublicBlob};
464                 use win_crypto_ng::helpers::Blob;
465 
466                 if y.value().len() > 3072 / 8 {
467                     return Err(Error::InvalidOperation(
468                         "DSA keys are supported up to 3072-bits".to_string()).into()
469                     );
470                 }
471 
472                 // CNG expects full-sized signatures
473                 let field_sz = q.value().len();
474                 let r = [&vec![0u8; field_sz - r.value().len()], r.value()].concat();
475                 let s = [&vec![0u8; field_sz - s.value().len()], s.value()].concat();
476                 let signature = [r, s].concat();
477 
478                 enum Version { V1, V2 }
479                 // 1024-bit DSA keys are handled differently
480                 let version = if y.value().len() <= 128 { Version::V1 } else { Version::V2 };
481 
482                 let blob: DsaPublicBlob = match version {
483                     Version::V1 => {
484                         let mut group = [0; 20];
485                         assert!(q.value().len() >= 20);
486                         &mut group[..q.value().len()].copy_from_slice(q.value());
487 
488                         DsaPublicBlob::V1(Blob::<DsaKeyPublicBlob>::clone_from_parts(
489                             &winapi::shared::bcrypt::BCRYPT_DSA_KEY_BLOB {
490                                 dwMagic: winapi::shared::bcrypt::BCRYPT_DSA_PUBLIC_MAGIC,
491                                 cbKey: y.value().len() as u32,
492                                 Count: [0; 4], // unused
493                                 Seed: [0; 20], // unused
494                                 q: group,
495                             },
496                             &DsaKeyPublicPayload {
497                                 modulus: p.value(),
498                                 generator: g.value(),
499                                 public: y.value(),
500                             },
501                         ))
502                     },
503                     Version::V2 => {
504                         // https://github.com/dotnet/runtime/blob/67d74fca70d4670ad503e23dba9d6bc8a1b5909e/src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs#L276-L282
505                         let hash = match q.value().len() {
506                             20 => 0,
507                             32 => 1,
508                             64 => 2,
509                             _ => return Err(Error::InvalidOperation(
510                                 "CNG accepts DSA q with length of either length of 20, 32 or 64".into())
511                                 .into()),
512                         };
513 
514                         // We don't use counter/seed values so set them to 0.
515                         // CNG pre-checks that the seed is at least |Q| long,
516                         // so we can't use an empty buffer here.
517                         let (count, seed) = ([0x0; 4], vec![0x0; q.value().len()]);
518 
519                         DsaPublicBlob::V2(Blob::<DsaKeyPublicV2Blob>::clone_from_parts(
520                             &winapi::shared::bcrypt::BCRYPT_DSA_KEY_BLOB_V2 {
521                                 dwMagic: winapi::shared::bcrypt::BCRYPT_DSA_PUBLIC_MAGIC_V2,
522                                 Count: count,
523                                 // Size of the prime number q .
524                                 // Currently, if the key is less than 128
525                                 // bits, q is 20 bytes long.
526                                 // If the key exceeds 256 bits, q is 32 bytes long.
527                                 cbGroupSize: q.value().len() as u32,
528                                 cbKey: y.value().len() as u32,
529                                 // https://csrc.nist.gov/csrc/media/publications/fips/186/3/archive/2009-06-25/documents/fips_186-3.pdf
530                                 // Length of the seed used to generate the
531                                 // prime number q.
532                                 cbSeedLength: seed.len() as u32,
533                                 hashAlgorithm: hash,
534                                 standardVersion: 1, // FIPS 186-3
535 
536                             },
537                             &DsaKeyPublicV2Payload {
538                                 seed: &seed,
539                                 group: q.value(),
540                                 modulus: p.value(),
541                                 generator: g.value(),
542                                 public: y.value(),
543                             },
544                         ))
545                     },
546                 };
547 
548                 use win_crypto_ng::asymmetric::Import;
549                 let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Dsa)?;
550                 let key = AsymmetricKey::<Dsa, Public>::import(
551                     Dsa,
552                     &provider,
553                     blob
554                 )?;
555 
556                 // CNG accepts only hash and Q of equal length. Either trim the
557                 // digest or pad it with zeroes (since it's treated as a
558                 // big-endian number).
559                 // See https://github.com/dotnet/runtime/blob/67d74fca70d4670ad503e23dba9d6bc8a1b5909e/src/libraries/Common/src/System/Security/Cryptography/DSACng.SignVerify.cs#L148.
560                 let mut _digest = vec![];
561                 let digest = match std::cmp::Ord::cmp(&q.value().len(), &digest.len()) {
562                     std::cmp::Ordering::Equal => digest,
563                     std::cmp::Ordering::Less => &digest[..q.value().len()],
564                     std::cmp::Ordering::Greater => {
565                         let pad = vec![0; q.value().len() - digest.len()];
566                         _digest = [pad.as_ref(), digest].concat();
567                         &_digest
568                     }
569                 };
570 
571                 key.verify(digest, &signature, None).map(|_| true)?
572             },
573             (ECDSA, mpi::PublicKey::ECDSA { curve, q }, mpi::Signature::ECDSA { s, r }) =>
574             {
575                 let (x, y) = q.decode_point(curve)?;
576                 // CNG expects full-sized signatures
577                 let field_sz = x.len();
578                 let r = [&vec![0u8; field_sz - r.value().len()], r.value()].concat();
579                 let s = [&vec![0u8; field_sz - s.value().len()], s.value()].concat();
580                 let signature = [r, s].concat();
581 
582                 use cng::key_blob::EccKeyPublicPayload;
583                 use cng::asymmetric::{ecc::{NistP256, NistP384, NistP521}, Ecdsa};
584 
585                 // TODO: Improve CNG public API
586                 match curve {
587                     Curve::NistP256 => {
588                         let provider = AsymmetricAlgorithm::open(
589                             AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP256)
590                         )?;
591                         let key = AsymmetricKey::<Ecdsa<NistP256>, Public>::import_from_parts(
592                             &provider,
593                             &EccKeyPublicPayload { x, y }
594                         )?;
595                         key.verify(digest, &signature, None).map(|_| true)?
596                     },
597                     Curve::NistP384 => {
598                         let provider = AsymmetricAlgorithm::open(
599                             AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP384)
600                         )?;
601                         let key = AsymmetricKey::<Ecdsa<NistP384>, Public>::import_from_parts(
602                             &provider,
603                             &EccKeyPublicPayload { x, y }
604                         )?;
605                         key.verify(digest, &signature, None).map(|_| true)?
606                     },
607                     Curve::NistP521 => {
608                         let provider = AsymmetricAlgorithm::open(
609                             AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP521)
610                         )?;
611                         let key = AsymmetricKey::<Ecdsa<NistP521>, Public>::import_from_parts(
612                             &provider,
613                             &EccKeyPublicPayload { x, y }
614                         )?;
615                         key.verify(digest, &signature, None).map(|_| true)?
616                     },
617                     _ => return Err(
618                         Error::UnsupportedEllipticCurve(curve.clone()).into()),
619                 }
620             },
621             (EdDSA, mpi::PublicKey::EdDSA { curve, q }, mpi::Signature::EdDSA { r, s }) => {
622                     // CNG doesn't support EdDSA, use ed25519-dalek instead
623                     use ed25519_dalek::{PublicKey, Signature, SIGNATURE_LENGTH};
624                     use ed25519_dalek::{Verifier};
625 
626                     let (public, ..) = q.decode_point(&Curve::Ed25519)?;
627                     assert_eq!(public.len(), 32);
628 
629                     let key = PublicKey::from_bytes(public).map_err(|e| {
630                         Error::InvalidKey(e.to_string())
631                     })?;
632 
633                     // ed25519 expects full-sized signatures but OpenPGP allows
634                     // for stripped leading zeroes, pad each part with zeroes.
635                     let (r, s) = (r.value(), s.value());
636                     let signature = [
637                         [&vec![0u8; (SIGNATURE_LENGTH / 2) - r.len()], r].concat(),
638                         [&vec![0u8; (SIGNATURE_LENGTH / 2) - s.len()], s].concat(),
639                     ].concat();
640                     assert_eq!(signature.len(), SIGNATURE_LENGTH);
641                     let mut sig_bytes = [0u8; 64];
642                     &mut sig_bytes[..].copy_from_slice(&*signature);
643 
644                     let signature = Signature::from(sig_bytes);
645 
646                     key.verify(digest, &signature)
647                     .map(|_| true)
648                     .map_err(|e| Error::BadSignature(e.to_string()))?
649             },
650             _ => return Err(Error::MalformedPacket(format!(
651                 "unsupported combination of algorithm {}, key {} and \
652                  signature {:?}.",
653                 sig.pk_algo(), self.pk_algo(), sig.mpis())).into()),
654         };
655 
656         if ok {
657             Ok(())
658         } else {
659             Err(Error::ManipulatedMessage.into())
660         }
661     }
662 }
663 
664 impl<R> Key4<SecretParts, R>
665 where
666     R: key::KeyRole,
667 {
668     /// Creates a new OpenPGP secret key packet for an existing X25519 key.
669     ///
670     /// The ECDH key will use hash algorithm `hash` and symmetric
671     /// algorithm `sym`.  If one or both are `None` secure defaults
672     /// will be used.  The key will have its creation date set to
673     /// `ctime` or the current time if `None` is given.
import_secret_cv25519<H, S, T>( private_key: &[u8], hash: H, sym: S, ctime: T, ) -> Result<Self> where H: Into<Option<HashAlgorithm>>, S: Into<Option<SymmetricAlgorithm>>, T: Into<Option<SystemTime>>,674     pub fn import_secret_cv25519<H, S, T>(
675         private_key: &[u8],
676         hash: H,
677         sym: S,
678         ctime: T,
679     ) -> Result<Self>
680     where
681         H: Into<Option<HashAlgorithm>>,
682         S: Into<Option<SymmetricAlgorithm>>,
683         T: Into<Option<SystemTime>>,
684     {
685         use cng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId, Ecdh, Private};
686         use cng::asymmetric::{AsymmetricKey, Export};
687         use cng::asymmetric::ecc::{Curve25519, NamedCurve};
688 
689         let provider = AsymmetricAlgorithm::open(
690             AsymmetricAlgorithmId::Ecdh(NamedCurve::Curve25519)
691         )?;
692         let key = AsymmetricKey::<Ecdh<Curve25519>, Private>::import_from_parts(
693             &provider,
694             private_key
695         )?;
696         let blob = key.export()?;
697 
698         // Mark MPI as compressed point with 0x40 prefix. See
699         // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07#section-13.2.
700         let mut public = [0u8; 1 + CURVE25519_SIZE];
701         public[0] = 0x40;
702         &mut public[1..].copy_from_slice(blob.x());
703 
704         // Reverse the scalar.  See
705         // https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html.
706         let mut private = blob.d().to_vec();
707         private.reverse();
708 
709         Self::with_secret(
710             ctime.into().unwrap_or_else(SystemTime::now),
711             PublicKeyAlgorithm::ECDH,
712             mpi::PublicKey::ECDH {
713                 curve: Curve::Cv25519,
714                 hash: hash.into().unwrap_or(HashAlgorithm::SHA512),
715                 sym: sym.into().unwrap_or(SymmetricAlgorithm::AES256),
716                 q: mpi::MPI::new(&public),
717             },
718             mpi::SecretKeyMaterial::ECDH { scalar: private.into() }.into()
719         )
720     }
721 
722     /// Creates a new OpenPGP secret key packet for an existing Ed25519 key.
723     ///
724     /// The key will have it's creation date set to `ctime` or the current time
725     /// if `None` is given.
import_secret_ed25519<T>(private_key: &[u8], ctime: T) -> Result<Self> where T: Into<Option<SystemTime>>,726     pub fn import_secret_ed25519<T>(private_key: &[u8], ctime: T) -> Result<Self>
727     where
728         T: Into<Option<SystemTime>>,
729     {
730         // CNG doesn't support EdDSA, use ed25519-dalek instead
731         use ed25519_dalek::{PublicKey, SecretKey};
732 
733         let private = SecretKey::from_bytes(private_key).map_err(|e| {
734             Error::InvalidKey(e.to_string())
735         })?;
736 
737         // Mark MPI as compressed point with 0x40 prefix. See
738         // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07#section-13.2.
739         let mut public = [0u8; 1 + CURVE25519_SIZE];
740         public[0] = 0x40;
741         &mut public[1..].copy_from_slice(Into::<PublicKey>::into(&private).as_bytes());
742 
743         Self::with_secret(
744             ctime.into().unwrap_or_else(SystemTime::now),
745             PublicKeyAlgorithm::EdDSA,
746             mpi::PublicKey::EdDSA {
747                 curve: Curve::Ed25519,
748                 q: mpi::MPI::new(&public)
749             },
750             mpi::SecretKeyMaterial::EdDSA {
751                 scalar: mpi::MPI::new(&private_key).into(),
752             }.into()
753         )
754     }
755 
756     /// Creates a new OpenPGP public key packet for an existing RSA key.
757     ///
758     /// The RSA key will use public exponent `e` and modulo `n`. The key will
759     /// have it's creation date set to `ctime` or the current time if `None`
760     /// is given.
import_secret_rsa<T>(d: &[u8], p: &[u8], q: &[u8], ctime: T) -> Result<Self> where T: Into<Option<SystemTime>>,761     pub fn import_secret_rsa<T>(d: &[u8], p: &[u8], q: &[u8], ctime: T) -> Result<Self>
762     where
763         T: Into<Option<SystemTime>>,
764     {
765         // RFC 4880: `p < q`
766         let (p, q) = if p < q { (p, q) } else { (q, p) };
767 
768         // CNG can't compute the public key from the private one, so do it ourselves
769         let big_p = BigUint::from_bytes_be(p);
770         let big_q = BigUint::from_bytes_be(q);
771         let n = big_p.clone() * big_q.clone();
772 
773         let big_d = BigUint::from_bytes_be(d);
774         let big_phi = (big_p.clone() - 1u32) * (big_q.clone() - 1u32);
775         let e = big_d.mod_inverse(big_phi) // e ≡ d⁻¹ (mod ��)
776             .and_then(|x: BigInt| x.to_biguint())
777             .ok_or_else(|| Error::MalformedMPI("RSA: `d` and `(p-1)(q-1)` aren't coprime".into()))?;
778 
779         let u: BigUint = big_p.mod_inverse(big_q) // RFC 4880: u ≡ p⁻¹ (mod q)
780             .and_then(|x: BigInt| x.to_biguint())
781             .ok_or_else(|| Error::MalformedMPI("RSA: `p` and `q` aren't coprime".into()))?;
782 
783         Self::with_secret(
784             ctime.into().unwrap_or_else(SystemTime::now),
785             PublicKeyAlgorithm::RSAEncryptSign,
786             mpi::PublicKey::RSA {
787                 e: mpi::MPI::new(&e.to_bytes_be()),
788                 n: mpi::MPI::new(&n.to_bytes_be()),
789             },
790             mpi::SecretKeyMaterial::RSA {
791                 d: mpi::MPI::new(d).into(),
792                 p: mpi::MPI::new(p).into(),
793                 q: mpi::MPI::new(q).into(),
794                 u: mpi::MPI::new(&u.to_bytes_be()).into(),
795             }.into()
796         )
797     }
798 
799     /// Generates a new RSA key with a public modulos of size `bits`.
generate_rsa(bits: usize) -> Result<Self>800     pub fn generate_rsa(bits: usize) -> Result<Self> {
801         use win_crypto_ng::asymmetric::{AsymmetricKey, Rsa};
802 
803         let blob = AsymmetricKey::builder(Rsa)
804             .key_bits(bits as u32)
805             .build()?
806             .export_full()?;
807 
808         let public = mpi::PublicKey::RSA {
809             e: mpi::MPI::new(blob.pub_exp()).into(),
810             n: mpi::MPI::new(blob.modulus()).into(),
811         };
812 
813         let p = mpi::MPI::new(blob.prime1());
814         let q = mpi::MPI::new(blob.prime2());
815         // RSA prime generation in CNG returns them in arbitrary order but
816         // RFC 4880 expects `p < q`
817         let (p, q) = if p < q { (p, q) } else { (q, p) };
818         // CNG `coeff` is `prime1`^-1 mod `prime2` so adjust for possible p,q reorder
819         let big_p = BigUint::from_bytes_be(p.value());
820         let big_q = BigUint::from_bytes_be(q.value());
821         let u = big_p.mod_inverse(big_q) // RFC 4880: u ≡ p⁻¹ (mod q)
822             .and_then(|x: BigInt| x.to_biguint())
823             .expect("CNG to generate a valid RSA key (where p, q are coprime)");
824 
825         let private = mpi::SecretKeyMaterial::RSA {
826             p: p.into(),
827             q: q.into(),
828             d: mpi::MPI::new(blob.priv_exp()).into(),
829             u: mpi::MPI::new(&u.to_bytes_be()).into(),
830         };
831 
832         Self::with_secret(
833             SystemTime::now(),
834             PublicKeyAlgorithm::RSAEncryptSign,
835             public,
836             private.into()
837         )
838     }
839 
840     /// Generates a new ECC key over `curve`.
841     ///
842     /// If `for_signing` is false a ECDH key, if it's true either a
843     /// EdDSA or ECDSA key is generated.  Giving `for_signing == true`
844     /// and `curve == Cv25519` will produce an error.  Similar for
845     /// `for_signing == false` and `curve == Ed25519`.
846     /// signing/encryption
generate_ecc(for_signing: bool, curve: Curve) -> Result<Self>847     pub fn generate_ecc(for_signing: bool, curve: Curve) -> Result<Self> {
848         use crate::PublicKeyAlgorithm::*;
849 
850         use cng::asymmetric::{ecc, Export};
851         use cng::asymmetric::{AsymmetricKey, AsymmetricAlgorithmId, Ecdh};
852 
853         let (algo, public, private) = match (curve.clone(), for_signing) {
854             (Curve::NistP256, ..) | (Curve::NistP384, ..) | (Curve::NistP521, ..) => {
855                 let (cng_curve, hash) = match curve {
856                     Curve::NistP256 => (ecc::NamedCurve::NistP256, HashAlgorithm::SHA256),
857                     Curve::NistP384 => (ecc::NamedCurve::NistP384, HashAlgorithm::SHA384),
858                     Curve::NistP521 => (ecc::NamedCurve::NistP521, HashAlgorithm::SHA512),
859                     _ => unreachable!()
860                 };
861 
862                 let ecc_algo = if for_signing {
863                     AsymmetricAlgorithmId::Ecdsa(cng_curve)
864                 } else {
865                     AsymmetricAlgorithmId::Ecdh(cng_curve)
866                 };
867 
868                 let blob = AsymmetricKey::builder(ecc_algo).build()?.export()?;
869                 let blob = match blob.try_into::<cng::key_blob::EccKeyPrivateBlob>() {
870                     Ok(blob) => blob,
871                     // Dynamic algorithm specified is either ECDSA or ECDH so
872                     // exported blob should be of appropriate type
873                     Err(..) => unreachable!()
874                 };
875                 let field_sz = cng_curve.key_bits() as usize;
876 
877                 let q = mpi::MPI::new_point(blob.x(), blob.y(), field_sz);
878                 let scalar = mpi::MPI::new(blob.d());
879 
880                 if for_signing {
881                     (
882                         ECDSA,
883                         mpi::PublicKey::ECDSA { curve, q },
884                         mpi::SecretKeyMaterial::ECDSA { scalar: scalar.into() },
885                     )
886                 } else {
887                     let sym = SymmetricAlgorithm::AES256;
888                     (
889                         ECDH,
890                         mpi::PublicKey::ECDH { curve, q, hash, sym },
891                         mpi::SecretKeyMaterial::ECDH { scalar: scalar.into() },
892                     )
893                 }
894             },
895             (Curve::Cv25519, false) => {
896                 let blob = AsymmetricKey::builder(Ecdh(ecc::Curve25519)).build()?.export()?;
897 
898                 // Mark MPI as compressed point with 0x40 prefix. See
899                 // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07#section-13.2.
900                 let mut public = [0u8; 1 + CURVE25519_SIZE];
901                 public[0] = 0x40;
902                 &mut public[1..].copy_from_slice(blob.x());
903 
904                 // Reverse the scalar.  See
905                 // https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html.
906                 let mut private: Protected = blob.d().into();
907                 private.reverse();
908 
909                 (
910                     ECDH,
911                     mpi::PublicKey::ECDH {
912                         curve,
913                         q: mpi::MPI::new(&public),
914                         hash: HashAlgorithm::SHA256,
915                         sym: SymmetricAlgorithm::AES256,
916                     },
917                     mpi::SecretKeyMaterial::ECDH { scalar: private.into() }
918                 )
919             },
920             (Curve::Ed25519, true) => {
921                 // CNG doesn't support EdDSA, use ed25519-dalek instead
922                 use ed25519_dalek::Keypair;
923 
924                 let mut rng = cng::random::RandomNumberGenerator::system_preferred();
925                 let Keypair { public, secret } = Keypair::generate(&mut rng);
926 
927                 let secret: Protected = secret.as_bytes().as_ref().into();
928 
929                 // Mark MPI as compressed point with 0x40 prefix. See
930                 // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07#section-13.2.
931                 let mut compressed_public = [0u8; 1 + CURVE25519_SIZE];
932                 compressed_public[0] = 0x40;
933                 &mut compressed_public[1..].copy_from_slice(public.as_bytes());
934 
935                 (
936                     EdDSA,
937                     mpi::PublicKey::EdDSA { curve, q: mpi::MPI::new(&compressed_public) },
938                     mpi::SecretKeyMaterial::EdDSA { scalar: secret.into() },
939                 )
940             },
941             // TODO: Support Brainpool curves
942             (curve, ..) => {
943                 return Err(Error::UnsupportedEllipticCurve(curve).into());
944             }
945         };
946 
947         Self::with_secret(SystemTime::now(), algo, public, private.into())
948     }
949 }
950