win_crypto_ng/asymmetric/
mod.rs

1//! Asymmetric algorithms
2//!
3//! Asymmetric algorithms (also known as public-key algorithms) use pairs of
4//! keys: *public key*, which can be known by others, and *private key*, which
5//! is known only to the owner. The most common usages include encryption and
6//! digital signing.
7
8use crate::helpers::WindowsString;
9use crate::helpers::{AlgoHandle, Handle, KeyHandle};
10use crate::helpers::{Blob, BlobLayout};
11use crate::key_blob::{BlobType, ErasedKeyBlob, *};
12use crate::property::{AlgorithmName, EccCurveName};
13use crate::Result;
14use std::borrow::Borrow;
15use std::convert::TryFrom;
16use std::marker::PhantomData;
17use std::ptr::null_mut;
18use winapi::shared::bcrypt::*;
19
20use builder::KeyPair;
21use ecc::{Curve, NamedCurve};
22use ecc::{Curve25519, NistP256, NistP384, NistP521};
23
24pub mod agreement;
25pub mod builder;
26pub mod ecc;
27pub mod signature;
28
29/// Asymmetric algorithm identifiers.
30#[derive(Debug, Clone, Copy, PartialEq)]
31pub enum AsymmetricAlgorithmId {
32    /// The Diffie-Hellman key exchange algorithm.
33    ///
34    /// Standard: PKCS #3
35    Dh,
36    /// The digital signature algorithm.
37    ///
38    /// Standard: FIPS 186-2
39    ///
40    /// **Windows 8**: Beginning with Windows 8, this algorithm supports
41    /// FIPS 186-3. Keys less than or equal to 1024 bits adhere to FIPS 186-2
42    /// and keys greater than 1024 to FIPS 186-3.
43    Dsa,
44    /// Generic prime elliptic curve Diffie-Hellman key exchange algorithm.
45    ///
46    /// Standard: SP800-56A, FIPS 186-2 (Curves P-{256, 384, 521}).
47    Ecdh(NamedCurve),
48    /// Generic prime elliptic curve digital signature algorithm.
49    ///
50    /// Standard: ANSI X9.62, FIPS 186-2 (Curves P-{256, 384, 521}).
51    Ecdsa(NamedCurve),
52    /// The RSA public key algorithm.
53    ///
54    /// Standard: PKCS #1 v1.5 and v2.0.
55    Rsa,
56}
57
58impl AsymmetricAlgorithmId {
59    pub fn to_str(&self) -> &str {
60        match self {
61            Self::Dh => BCRYPT_DH_ALGORITHM,
62            Self::Dsa => BCRYPT_DSA_ALGORITHM,
63            Self::Ecdh(NamedCurve::NistP256) => BCRYPT_ECDH_P256_ALGORITHM,
64            Self::Ecdh(NamedCurve::NistP384) => BCRYPT_ECDH_P384_ALGORITHM,
65            Self::Ecdh(NamedCurve::NistP521) => BCRYPT_ECDH_P521_ALGORITHM,
66            Self::Ecdh(..) => BCRYPT_ECDH_ALGORITHM,
67            Self::Ecdsa(NamedCurve::NistP256) => BCRYPT_ECDSA_P256_ALGORITHM,
68            Self::Ecdsa(NamedCurve::NistP384) => BCRYPT_ECDSA_P384_ALGORITHM,
69            Self::Ecdsa(NamedCurve::NistP521) => BCRYPT_ECDSA_P521_ALGORITHM,
70            Self::Ecdsa(..) => BCRYPT_ECDSA_ALGORITHM,
71            Self::Rsa => BCRYPT_RSA_ALGORITHM,
72        }
73    }
74
75    pub fn key_bits(self) -> Option<u32> {
76        match self {
77            Self::Ecdh(curve) | Self::Ecdsa(curve) => Some(curve.key_bits()),
78            _ => None,
79        }
80    }
81
82    pub fn is_key_bits_supported(self, key_bits: u32) -> bool {
83        match (self, key_bits) {
84            | (Self::Dh, 512..=4096)
85            | (Self::Rsa, 512..=16384)
86            // Prior to Windows 8, only values <= 1024 are supported,
87            // after that it's <= 3072.
88            // TODO: Check version using winapi::um::winbase::VerifyVersionInfoW
89            | (Self::Dsa, 512..=3072) if key_bits % 64 == 0 => true,
90            | (Self::Ecdh(curve), bits)
91            | (Self::Ecdsa(curve), bits) if curve.key_bits() == bits => true,
92            _ => false,
93        }
94    }
95}
96
97impl<'a> TryFrom<&'a str> for AsymmetricAlgorithmId {
98    type Error = &'a str;
99
100    fn try_from(val: &'a str) -> Result<AsymmetricAlgorithmId, Self::Error> {
101        match val {
102            BCRYPT_DH_ALGORITHM => Ok(Self::Dh),
103            BCRYPT_DSA_ALGORITHM => Ok(Self::Dsa),
104            BCRYPT_ECDH_P256_ALGORITHM => Ok(Self::Ecdh(NamedCurve::NistP256)),
105            BCRYPT_ECDH_P384_ALGORITHM => Ok(Self::Ecdh(NamedCurve::NistP384)),
106            BCRYPT_ECDH_P521_ALGORITHM => Ok(Self::Ecdh(NamedCurve::NistP521)),
107            BCRYPT_ECDSA_P256_ALGORITHM => Ok(Self::Ecdsa(NamedCurve::NistP256)),
108            BCRYPT_ECDSA_P384_ALGORITHM => Ok(Self::Ecdsa(NamedCurve::NistP384)),
109            BCRYPT_ECDSA_P521_ALGORITHM => Ok(Self::Ecdsa(NamedCurve::NistP521)),
110            BCRYPT_RSA_ALGORITHM => Ok(Self::Rsa),
111            // TODO: Make curves optional in {Ecdh, Ecdsa}?
112            val => Err(val),
113        }
114    }
115}
116
117/// Asymmetric algorithm provider.
118pub struct AsymmetricAlgorithm {
119    handle: AlgoHandle,
120}
121
122impl AsymmetricAlgorithm {
123    /// Open an asymmetric algorithm provider
124    ///
125    /// # Examples
126    ///
127    /// ```
128    /// # use win_crypto_ng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
129    /// let algo = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa);
130    ///
131    /// assert!(algo.is_ok());
132    /// ```
133    pub fn open(id: AsymmetricAlgorithmId) -> Result<Self> {
134        let handle = AlgoHandle::open(id.to_str())?;
135
136        // The provider for elliptic algorithms using NIST P-{256,384,521}
137        // curves is separate from the generic one and does not support setting
138        // properties
139        match id {
140            AsymmetricAlgorithmId::Ecdh(NamedCurve::NistP256)
141            | AsymmetricAlgorithmId::Ecdh(NamedCurve::NistP384)
142            | AsymmetricAlgorithmId::Ecdh(NamedCurve::NistP521)
143            | AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP256)
144            | AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP384)
145            | AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP521) => {}
146            AsymmetricAlgorithmId::Ecdh(curve) | AsymmetricAlgorithmId::Ecdsa(curve) => {
147                let property = WindowsString::from(curve.to_str());
148
149                handle.set_property::<EccCurveName>(property.as_slice_with_nul())?;
150            }
151            _ => {}
152        }
153
154        Ok(Self { handle })
155    }
156
157    ///
158    /// # Examples
159    /// ```
160    /// # use win_crypto_ng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
161    /// let algo = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa).unwrap();
162    /// assert_eq!(algo.id(), Ok(AsymmetricAlgorithmId::Rsa));
163    /// ```
164    pub fn id(&self) -> Result<AsymmetricAlgorithmId> {
165        self.handle
166            .get_property_unsized::<AlgorithmName>()
167            .map(|name| {
168                WindowsString::from_bytes_with_nul(name.as_ref().into())
169                    .expect("API to return 0-terminated wide string")
170            })
171            .and_then(|name| {
172                AsymmetricAlgorithmId::try_from(name.to_string().as_str())
173                    .map_err(|_| crate::Error::InvalidHandle)
174            })
175    }
176}
177
178/// Marker trait for an asymmetric algorithm.
179pub trait Algorithm {
180    fn id(&self) -> AsymmetricAlgorithmId;
181}
182
183/// Marker type representing the ECDSA (Elliptic Curve Digital Signature Algorithm).
184pub struct Ecdsa<C: Curve>(pub C);
185impl<C: Curve> Algorithm for Ecdsa<C> {
186    #[inline(always)]
187    fn id(&self) -> AsymmetricAlgorithmId {
188        AsymmetricAlgorithmId::Ecdsa(self.0.as_curve())
189    }
190}
191
192/// Marker type representing the ECDH (Elliptic Curve Diffie-Hellman) algorithm.
193pub struct Ecdh<C: Curve>(pub C);
194impl<C: Curve> Algorithm for Ecdh<C> {
195    #[inline(always)]
196    fn id(&self) -> AsymmetricAlgorithmId {
197        AsymmetricAlgorithmId::Ecdh(self.0.as_curve())
198    }
199}
200
201/// Marker type representing the DH (Diffie-Hellman) algorithm.
202pub struct Dh;
203impl Algorithm for Dh {
204    #[inline(always)]
205    fn id(&self) -> AsymmetricAlgorithmId {
206        AsymmetricAlgorithmId::Dh
207    }
208}
209
210/// Marker type representing the DSA (Digital Signature Algorithm).
211pub struct Dsa;
212impl Algorithm for Dsa {
213    #[inline(always)]
214    fn id(&self) -> AsymmetricAlgorithmId {
215        AsymmetricAlgorithmId::Dsa
216    }
217}
218
219/// Marker type representing the RSA (Rivest-Shamir-Adleman) algorithm.
220pub struct Rsa;
221impl Algorithm for Rsa {
222    #[inline(always)]
223    fn id(&self) -> AsymmetricAlgorithmId {
224        AsymmetricAlgorithmId::Rsa
225    }
226}
227
228/// Marker trait used to denote whether a key holds public or private parts.
229pub trait Parts {}
230/// Marker type used to denote private key parts.
231pub struct Private {}
232impl Parts for Private {}
233/// Marker type used to denote public key parts.
234pub struct Public {}
235impl Parts for Public {}
236
237impl Algorithm for AsymmetricAlgorithmId {
238    fn id(&self) -> AsymmetricAlgorithmId {
239        *self
240    }
241}
242
243/// Asymmetric key handle.
244///
245/// Can be specified as that of which type is known at run-time (erased) or
246/// with a concrete algorithm parameter.
247///
248/// ## Type-safety
249/// At the time of writing, all of the relevant crypto operations are
250/// implemented for only keys whose algorithm is statically known. This means
251/// that the keys using the dynamic `AsymmetricAlgorithmId` need to be
252/// re-imported in order to statically verify the algorithm used.
253///
254/// ```
255/// use win_crypto_ng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId, AsymmetricKey};
256/// use win_crypto_ng::asymmetric::{Rsa, Private, Import, Export};
257///
258/// let dynamic: AsymmetricKey<AsymmetricAlgorithmId, Private> =
259///     AsymmetricKey::builder(AsymmetricAlgorithmId::Rsa).key_bits(512).build().unwrap();
260/// let dynamic_blob = dynamic.export().unwrap();
261///
262/// let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa).unwrap();
263/// let rsa_blob = dynamic_blob.try_into().unwrap_or_else(|_| panic!());
264/// let rsa_key = AsymmetricKey::<Rsa, Private>::import(Rsa, &provider, &rsa_blob).unwrap();
265/// // Can now additional impls for `AsymmetricKey<Rsa, Private>`, e.g.
266/// // rsa_key.decrypt(..);
267/// ```
268///
269/// ## Generation
270/// ```
271/// use win_crypto_ng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId, AsymmetricKey};
272/// use win_crypto_ng::asymmetric::{Rsa, Private};
273///
274/// // Handle to an asymmetric key whose algorithm ID is known at run-time
275/// let handle: AsymmetricKey<AsymmetricAlgorithmId, Private> =
276///     AsymmetricKey::builder(AsymmetricAlgorithmId::Rsa).key_bits(512).build().unwrap();
277///
278/// // Handle to an asymmetric key whose algorithm is known to be RSA
279/// let handle: AsymmetricKey<Rsa, Private> =
280///     AsymmetricKey::builder(Rsa).key_bits(512).build().unwrap();
281/// ```
282pub struct AsymmetricKey<A: Algorithm = AsymmetricAlgorithmId, P: Parts = Public>(
283    KeyHandle,
284    A,
285    PhantomData<P>,
286);
287
288impl<A: Algorithm, P: Parts> AsymmetricKey<A, P> {
289    /// Returns the asymmetric algorithm ID known at run-time.
290    pub fn id(&self) -> AsymmetricAlgorithmId {
291        Algorithm::id(&self.1)
292    }
293}
294
295impl<A: Algorithm> AsymmetricKey<A, Private> {
296    /// Views the key pair as having only public key parts.
297    pub fn as_public(&self) -> &AsymmetricKey<A, Public> {
298        // NOTE: This assumes that Private is always a key pair
299        unsafe { &*(self as *const _ as *const AsymmetricKey<A, Public>) }
300    }
301}
302
303impl AsymmetricKey<Rsa, Private> {
304    /// Export the RSA key to "full" raw data format. Additionally includes
305    /// coefficient and (private) exponents.
306    pub fn export_full(&self) -> Result<Box<Blob<RsaKeyFullPrivateBlob>>> {
307        KeyPair::export(self.0.handle, BlobType::RsaFullPrivate)?
308            .try_into()
309            .map_err(|_| crate::Error::BadData)
310    }
311}
312
313/// Import asymmetric key using the raw key data format.
314pub trait Import<'a, A: Algorithm, P: Parts> {
315    type Blob: AsRef<Blob<ErasedKeyBlob>> + 'a;
316    fn import(
317        algo: A,
318        provider: &AsymmetricAlgorithm,
319        blob: Self::Blob,
320    ) -> Result<AsymmetricKey<A, P>> {
321        KeyPair::import(provider, blob.as_ref(), true)
322            .map(|pair| AsymmetricKey(pair.0, algo, PhantomData))
323    }
324}
325
326macro_rules! import_blobs {
327    ($(($algorithm: ty, $parts: ident, $blob: ty)),*$(,)?) => {
328        $(
329        impl<'a> Import<'a, $algorithm, $parts> for AsymmetricKey<$algorithm, $parts> {
330            type Blob = $blob;
331        }
332        )*
333    };
334}
335
336import_blobs!(
337    (AsymmetricAlgorithmId, Public, &'a Blob<ErasedKeyBlob>),
338    (AsymmetricAlgorithmId, Private, &'a Blob<ErasedKeyBlob>),
339    (Dh, Public, &'a Blob<DhKeyPublicBlob>),
340    (Dh, Private, &'a Blob<DhKeyPrivateBlob>),
341    (Dsa, Public, DsaPublicBlob),
342    (Dsa, Private, DsaPrivateBlob),
343    (Ecdh<NistP256>, Public, &'a Blob<EccKeyPublicBlob>),
344    (Ecdh<NistP256>, Private, &'a Blob<EccKeyPrivateBlob>),
345    (Ecdh<NistP384>, Public, &'a Blob<EccKeyPublicBlob>),
346    (Ecdh<NistP384>, Private, &'a Blob<EccKeyPrivateBlob>),
347    (Ecdh<NistP521>, Public, &'a Blob<EccKeyPublicBlob>),
348    (Ecdh<NistP521>, Private, &'a Blob<EccKeyPrivateBlob>),
349    (Ecdh<Curve25519>, Public, &'a Blob<EccKeyPublicBlob>),
350    (Ecdh<Curve25519>, Private, &'a Blob<EccKeyPrivateBlob>),
351    (Ecdsa<NistP256>, Public, &'a Blob<EccKeyPublicBlob>),
352    (Ecdsa<NistP256>, Private, &'a Blob<EccKeyPrivateBlob>),
353    (Ecdsa<NistP384>, Public, &'a Blob<EccKeyPublicBlob>),
354    (Ecdsa<NistP384>, Private, &'a Blob<EccKeyPrivateBlob>),
355    (Ecdsa<NistP521>, Public, &'a Blob<EccKeyPublicBlob>),
356    (Ecdsa<NistP521>, Private, &'a Blob<EccKeyPrivateBlob>),
357    (Rsa, Public, &'a Blob<RsaKeyPublicBlob>),
358    (Rsa, Private, &'a Blob<RsaKeyPrivateBlob>),
359);
360
361// TODO: Come up with an ergonomic high-level API for key importing from parts
362impl AsymmetricKey<Rsa, Private> {
363    pub fn import_from_parts(
364        provider: &AsymmetricAlgorithm,
365        parts: &RsaKeyPrivatePayload,
366    ) -> Result<Self> {
367        let key_bits = parts.modulus.len() * 8;
368        if key_bits % 64 != 0 || !(512..=16384).contains(&key_bits) {
369            return Err(crate::Error::InvalidParameter);
370        }
371
372        let header = BCRYPT_RSAKEY_BLOB {
373            BitLength: key_bits as u32,
374            Magic: BCRYPT_RSAPRIVATE_MAGIC,
375            cbModulus: parts.modulus.len() as u32,
376            cbPublicExp: parts.pub_exp.len() as u32,
377            cbPrime1: parts.prime1.len() as u32,
378            cbPrime2: parts.prime2.len() as u32,
379        };
380        let blob = Blob::<RsaKeyPrivateBlob>::clone_from_parts(&header, parts);
381
382        <Self as Import<_, _>>::import(Rsa, provider, &blob)
383    }
384}
385
386impl AsymmetricKey<Rsa, Public> {
387    pub fn import_from_parts(
388        provider: &AsymmetricAlgorithm,
389        parts: &RsaKeyPublicPayload,
390    ) -> Result<Self> {
391        let key_bits = parts.modulus.len() * 8;
392        if key_bits % 64 != 0 || !(512..=16384).contains(&key_bits) {
393            return Err(crate::Error::InvalidParameter);
394        }
395
396        let header = BCRYPT_RSAKEY_BLOB {
397            BitLength: key_bits as u32,
398            Magic: BCRYPT_RSAPUBLIC_MAGIC,
399            cbModulus: parts.modulus.len() as u32,
400            cbPublicExp: parts.pub_exp.len() as u32,
401            cbPrime1: 0,
402            cbPrime2: 0,
403        };
404        let blob = Blob::<RsaKeyPublicBlob>::clone_from_parts(&header, parts);
405
406        <Self as Import<_, _>>::import(Rsa, provider, &blob)
407    }
408}
409
410impl AsymmetricKey<Ecdsa<NistP256>, Private> {
411    pub fn import_from_parts(
412        provider: &AsymmetricAlgorithm,
413        parts: &EccKeyPrivatePayload,
414    ) -> Result<Self> {
415        let key_len = NistP256.key_bits() / 8;
416        if [parts.x, parts.y, parts.d]
417            .iter()
418            .any(|v| v.len() != key_len as usize)
419        {
420            return Err(crate::Error::InvalidParameter);
421        }
422
423        let blob = Blob::<EccKeyPrivateBlob>::clone_from_parts(
424            &BCRYPT_ECCKEY_BLOB {
425                dwMagic: BCRYPT_ECDSA_PRIVATE_P256_MAGIC,
426                cbKey: key_len,
427            },
428            parts,
429        );
430
431        <Self as Import<_, _>>::import(Ecdsa(NistP256), provider, &blob)
432    }
433}
434
435impl AsymmetricKey<Ecdsa<NistP256>, Public> {
436    pub fn import_from_parts(
437        provider: &AsymmetricAlgorithm,
438        parts: &EccKeyPublicPayload,
439    ) -> Result<Self> {
440        let key_len = NistP256.key_bits() / 8;
441        if [parts.x, parts.y]
442            .iter()
443            .any(|v| v.len() != key_len as usize)
444        {
445            return Err(crate::Error::InvalidParameter);
446        }
447
448        let blob = Blob::<EccKeyPublicBlob>::clone_from_parts(
449            &BCRYPT_ECCKEY_BLOB {
450                dwMagic: BCRYPT_ECDSA_PUBLIC_P256_MAGIC,
451                cbKey: key_len,
452            },
453            parts,
454        );
455
456        <Self as Import<_, _>>::import(Ecdsa(NistP256), provider, &blob)
457    }
458}
459
460impl AsymmetricKey<Ecdsa<NistP384>, Private> {
461    pub fn import_from_parts(
462        provider: &AsymmetricAlgorithm,
463        parts: &EccKeyPrivatePayload,
464    ) -> Result<Self> {
465        let key_len = NistP384.key_bits() / 8;
466        if [parts.x, parts.y, parts.d]
467            .iter()
468            .any(|v| v.len() != key_len as usize)
469        {
470            return Err(crate::Error::InvalidParameter);
471        }
472
473        let blob = Blob::<EccKeyPrivateBlob>::clone_from_parts(
474            &BCRYPT_ECCKEY_BLOB {
475                dwMagic: BCRYPT_ECDSA_PRIVATE_P384_MAGIC,
476                cbKey: key_len,
477            },
478            parts,
479        );
480
481        <Self as Import<_, _>>::import(Ecdsa(NistP384), provider, &blob)
482    }
483}
484
485impl AsymmetricKey<Ecdsa<NistP384>, Public> {
486    pub fn import_from_parts(
487        provider: &AsymmetricAlgorithm,
488        parts: &EccKeyPublicPayload,
489    ) -> Result<Self> {
490        let key_len = NistP384.key_bits() / 8;
491        if [parts.x, parts.y]
492            .iter()
493            .any(|v| v.len() != key_len as usize)
494        {
495            return Err(crate::Error::InvalidParameter);
496        }
497
498        let blob = Blob::<EccKeyPublicBlob>::clone_from_parts(
499            &BCRYPT_ECCKEY_BLOB {
500                dwMagic: BCRYPT_ECDSA_PUBLIC_P384_MAGIC,
501                cbKey: key_len,
502            },
503            parts,
504        );
505
506        <Self as Import<_, _>>::import(Ecdsa(NistP384), provider, &blob)
507    }
508}
509
510impl AsymmetricKey<Ecdsa<NistP521>, Private> {
511    pub fn import_from_parts(
512        provider: &AsymmetricAlgorithm,
513        parts: &EccKeyPrivatePayload,
514    ) -> Result<Self> {
515        let key_len = (NistP521.key_bits() + 7) / 8;
516        if [parts.x, parts.y, parts.d]
517            .iter()
518            .any(|v| v.len() != key_len as usize)
519        {
520            return Err(crate::Error::InvalidParameter);
521        }
522
523        let blob = Blob::<EccKeyPrivateBlob>::clone_from_parts(
524            &BCRYPT_ECCKEY_BLOB {
525                dwMagic: BCRYPT_ECDSA_PRIVATE_P521_MAGIC,
526                cbKey: key_len,
527            },
528            parts,
529        );
530
531        <Self as Import<_, _>>::import(Ecdsa(NistP521), provider, &blob)
532    }
533}
534
535impl AsymmetricKey<Ecdsa<NistP521>, Public> {
536    pub fn import_from_parts(
537        provider: &AsymmetricAlgorithm,
538        parts: &EccKeyPublicPayload,
539    ) -> Result<Self> {
540        let key_len = (NistP521.key_bits() + 7) / 8;
541        if [parts.x, parts.y]
542            .iter()
543            .any(|v| v.len() != key_len as usize)
544        {
545            return Err(crate::Error::InvalidParameter);
546        }
547
548        let blob = Blob::<EccKeyPublicBlob>::clone_from_parts(
549            &BCRYPT_ECCKEY_BLOB {
550                dwMagic: BCRYPT_ECDSA_PUBLIC_P521_MAGIC,
551                cbKey: key_len,
552            },
553            parts,
554        );
555
556        <Self as Import<_, _>>::import(Ecdsa(NistP521), provider, &blob)
557    }
558}
559
560impl AsymmetricKey<Ecdh<Curve25519>, Private> {
561    pub fn import_from_parts(provider: &AsymmetricAlgorithm, private: &[u8]) -> Result<Self> {
562        let key_len = (Curve25519.key_bits() + 7) / 8;
563        if private.len() != key_len as usize {
564            return Err(crate::Error::InvalidParameter);
565        }
566
567        let blob = Blob::<EccKeyPrivateBlob>::clone_from_parts(
568            &BCRYPT_ECCKEY_BLOB {
569                dwMagic: BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC,
570                cbKey: key_len,
571            },
572            &EccKeyPrivatePayload {
573                x: &[0u8; 32],
574                y: &[0u8; 32],
575                d: private,
576            },
577        );
578
579        <Self as Import<_, _>>::import(Ecdh(Curve25519), provider, &blob)
580    }
581}
582
583impl AsymmetricKey<Ecdh<Curve25519>, Public> {
584    pub fn import_from_parts(provider: &AsymmetricAlgorithm, public: &[u8]) -> Result<Self> {
585        let key_len = (Curve25519.key_bits() + 7) / 8;
586        if public.len() != key_len as usize {
587            return Err(crate::Error::InvalidParameter);
588        }
589
590        let blob = Blob::<EccKeyPublicBlob>::clone_from_parts(
591            &BCRYPT_ECCKEY_BLOB {
592                dwMagic: BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC,
593                cbKey: key_len,
594            },
595            &EccKeyPublicPayload {
596                x: public,
597                y: &[0u8; 32],
598            },
599        );
600
601        <Self as Import<_, _>>::import(Ecdh(Curve25519), provider, &blob)
602    }
603}
604
605impl AsymmetricKey<Ecdh<NistP256>, Private> {
606    pub fn import_from_parts(
607        provider: &AsymmetricAlgorithm,
608        parts: &EccKeyPrivatePayload,
609    ) -> Result<Self> {
610        let key_len = NistP256.key_bits() / 8;
611        if [parts.x, parts.y, parts.d]
612            .iter()
613            .any(|v| v.len() != key_len as usize)
614        {
615            return Err(crate::Error::InvalidParameter);
616        }
617
618        let blob = Blob::<EccKeyPrivateBlob>::clone_from_parts(
619            &BCRYPT_ECCKEY_BLOB {
620                dwMagic: BCRYPT_ECDH_PRIVATE_P256_MAGIC,
621                cbKey: key_len,
622            },
623            parts,
624        );
625
626        <Self as Import<_, _>>::import(Ecdh(NistP256), provider, &blob)
627    }
628}
629
630impl AsymmetricKey<Ecdh<NistP256>, Public> {
631    pub fn import_from_parts(
632        provider: &AsymmetricAlgorithm,
633        parts: &EccKeyPublicPayload,
634    ) -> Result<Self> {
635        let key_len = NistP256.key_bits() / 8;
636        if [parts.x, parts.y]
637            .iter()
638            .any(|v| v.len() != key_len as usize)
639        {
640            return Err(crate::Error::InvalidParameter);
641        }
642
643        let blob = Blob::<EccKeyPublicBlob>::clone_from_parts(
644            &BCRYPT_ECCKEY_BLOB {
645                dwMagic: BCRYPT_ECDH_PUBLIC_P256_MAGIC,
646                cbKey: key_len,
647            },
648            parts,
649        );
650
651        <Self as Import<_, _>>::import(Ecdh(NistP256), provider, &blob)
652    }
653}
654
655impl AsymmetricKey<Ecdh<NistP384>, Private> {
656    pub fn import_from_parts(
657        provider: &AsymmetricAlgorithm,
658        parts: &EccKeyPrivatePayload,
659    ) -> Result<Self> {
660        let key_len = NistP384.key_bits() / 8;
661        if [parts.x, parts.y, parts.d]
662            .iter()
663            .any(|v| v.len() != key_len as usize)
664        {
665            return Err(crate::Error::InvalidParameter);
666        }
667
668        let blob = Blob::<EccKeyPrivateBlob>::clone_from_parts(
669            &BCRYPT_ECCKEY_BLOB {
670                dwMagic: BCRYPT_ECDH_PRIVATE_P384_MAGIC,
671                cbKey: key_len,
672            },
673            parts,
674        );
675
676        <Self as Import<_, _>>::import(Ecdh(NistP384), provider, &blob)
677    }
678}
679
680impl AsymmetricKey<Ecdh<NistP384>, Public> {
681    pub fn import_from_parts(
682        provider: &AsymmetricAlgorithm,
683        parts: &EccKeyPublicPayload,
684    ) -> Result<Self> {
685        let key_len = NistP384.key_bits() / 8;
686        if [parts.x, parts.y]
687            .iter()
688            .any(|v| v.len() != key_len as usize)
689        {
690            return Err(crate::Error::InvalidParameter);
691        }
692
693        let blob = Blob::<EccKeyPublicBlob>::clone_from_parts(
694            &BCRYPT_ECCKEY_BLOB {
695                dwMagic: BCRYPT_ECDH_PUBLIC_P384_MAGIC,
696                cbKey: key_len,
697            },
698            parts,
699        );
700
701        <Self as Import<_, _>>::import(Ecdh(NistP384), provider, &blob)
702    }
703}
704
705impl AsymmetricKey<Ecdh<NistP521>, Private> {
706    pub fn import_from_parts(
707        provider: &AsymmetricAlgorithm,
708        parts: &EccKeyPrivatePayload,
709    ) -> Result<Self> {
710        let key_len = (NistP521.key_bits() + 7) / 8;
711        if [parts.x, parts.y, parts.d]
712            .iter()
713            .any(|v| v.len() != key_len as usize)
714        {
715            return Err(crate::Error::InvalidParameter);
716        }
717
718        let blob = Blob::<EccKeyPrivateBlob>::clone_from_parts(
719            &BCRYPT_ECCKEY_BLOB {
720                dwMagic: BCRYPT_ECDH_PRIVATE_P521_MAGIC,
721                cbKey: key_len,
722            },
723            parts,
724        );
725
726        <Self as Import<_, _>>::import(Ecdh(NistP521), provider, &blob)
727    }
728}
729
730impl AsymmetricKey<Ecdh<NistP521>, Public> {
731    pub fn import_from_parts(
732        provider: &AsymmetricAlgorithm,
733        parts: &EccKeyPublicPayload,
734    ) -> Result<Self> {
735        let key_len = (NistP521.key_bits() + 7) / 8;
736        if [parts.x, parts.y]
737            .iter()
738            .any(|v| v.len() != key_len as usize)
739        {
740            return Err(crate::Error::InvalidParameter);
741        }
742
743        let blob = Blob::<EccKeyPublicBlob>::clone_from_parts(
744            &BCRYPT_ECCKEY_BLOB {
745                dwMagic: BCRYPT_ECDH_PUBLIC_P521_MAGIC,
746                cbKey: key_len,
747            },
748            parts,
749        );
750
751        <Self as Import<_, _>>::import(Ecdh(NistP521), provider, &blob)
752    }
753}
754
755/// Export asymmetric key to the raw key data format.
756///
757/// # Example
758/// ```
759/// use win_crypto_ng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
760/// use win_crypto_ng::asymmetric::{Algorithm, Rsa, Private, AsymmetricKey};
761/// use win_crypto_ng::asymmetric::Export;
762///
763/// let pair = AsymmetricKey::builder(Rsa).key_bits(1024).build().unwrap();
764/// let blob = pair.as_public().export().unwrap();
765/// dbg!(blob.as_bytes());
766///
767/// let public = blob;
768/// let pub_exp = public.pub_exp();
769/// let modulus = public.modulus();
770///
771/// let private = pair.export().unwrap();
772/// assert_eq!(pub_exp, private.pub_exp());
773/// assert_eq!(modulus, private.modulus());
774/// ```
775pub trait Export<A: Algorithm, P: Parts>: Borrow<AsymmetricKey<A, P>> {
776    type Blob: KeyBlob + BlobLayout;
777
778    #[doc(hidden)]
779    fn blob_type(&self) -> BlobType;
780
781    fn export(&self) -> Result<Box<Blob<Self::Blob>>> {
782        let key = self.borrow();
783        let blob_type = self.blob_type();
784
785        let blob = KeyPair::export(key.0.handle, blob_type)?;
786        blob.try_into().map_err(|_| crate::Error::BadData)
787    }
788}
789
790macro_rules! export_blobs {
791    ($(($type: ty, $parts: ty, $blob: ty, $blob_type: expr)),*$(,)?) => {
792        $(
793        impl<'a> Export<$type, $parts> for AsymmetricKey<$type, $parts> {
794            type Blob = $blob;
795
796            fn blob_type(&self) -> BlobType {
797                $blob_type
798            }
799        }
800        )*
801    };
802}
803
804#[rustfmt::skip]
805export_blobs!(
806    (AsymmetricAlgorithmId, Public, ErasedKeyBlob, BlobType::PublicKey),
807    (AsymmetricAlgorithmId, Private, ErasedKeyBlob, BlobType::PrivateKey),
808    (Dh, Public, DhKeyPublicBlob, BlobType::DhPublic),
809    (Dh, Private, DhKeyPrivateBlob, BlobType::DhPrivate),
810    (Dsa, Public, DsaKeyPublicBlob, BlobType::DsaPublic),
811    (Dsa, Private, DsaKeyPrivateBlob, BlobType::DsaPrivate),
812    (Ecdh<NistP256>, Public, EccKeyPublicBlob, BlobType::EccPublic),
813    (Ecdh<NistP256>, Private, EccKeyPrivateBlob, BlobType::EccPrivate),
814    (Ecdh<NistP384>, Public, EccKeyPublicBlob, BlobType::EccPublic),
815    (Ecdh<NistP384>, Private, EccKeyPrivateBlob, BlobType::EccPrivate),
816    (Ecdh<NistP521>, Public, EccKeyPublicBlob, BlobType::EccPublic),
817    (Ecdh<NistP521>, Private, EccKeyPrivateBlob, BlobType::EccPrivate),
818    (Ecdsa<NistP256>, Public, EccKeyPublicBlob, BlobType::EccPublic),
819    (Ecdsa<NistP256>, Private, EccKeyPrivateBlob, BlobType::EccPrivate),
820    (Ecdsa<NistP384>, Public, EccKeyPublicBlob, BlobType::EccPublic),
821    (Ecdsa<NistP384>, Private, EccKeyPrivateBlob, BlobType::EccPrivate),
822    (Ecdsa<NistP521>, Public, EccKeyPublicBlob, BlobType::EccPublic),
823    (Ecdsa<NistP521>, Private, EccKeyPrivateBlob, BlobType::EccPrivate),
824    (Ecdh<Curve25519>, Public, EccKeyPublicBlob, BlobType::EccPublic),
825    (Ecdh<Curve25519>, Private, EccKeyPrivateBlob, BlobType::EccPrivate),
826    (Rsa, Public, RsaKeyPublicBlob, BlobType::RsaPublic),
827    (Rsa, Private, RsaKeyPrivateBlob, BlobType::RsaPrivate),
828);
829
830/// Raw public DSA key data blob.
831pub enum DsaPublicBlob {
832    /// Used with keys having ≤ 1024 bit length.
833    V1(Box<Blob<DsaKeyPublicBlob>>),
834    /// Used with keys having > 1024 bit length.
835    V2(Box<Blob<DsaKeyPublicV2Blob>>),
836}
837
838impl AsRef<Blob<ErasedKeyBlob>> for DsaPublicBlob {
839    fn as_ref(&self) -> &Blob<ErasedKeyBlob> {
840        match self {
841            DsaPublicBlob::V1(v1) => v1.as_erased(),
842            DsaPublicBlob::V2(v2) => v2.as_erased(),
843        }
844    }
845}
846
847/// Raw private DSA key data blob.
848///
849/// For keys of ≤ 1024 bit length, use the `V1` variant. Otherwise, use `V2`.
850pub enum DsaPrivateBlob {
851    V1(Box<Blob<DsaKeyPrivateBlob>>),
852    V2(Box<Blob<DsaKeyPrivateV2Blob>>),
853}
854
855impl AsRef<Blob<ErasedKeyBlob>> for DsaPrivateBlob {
856    fn as_ref(&self) -> &Blob<ErasedKeyBlob> {
857        match self {
858            DsaPrivateBlob::V1(v1) => v1.as_erased(),
859            DsaPrivateBlob::V2(v2) => v2.as_erased(),
860        }
861    }
862}
863
864/// OAEP (Optimal Asymmetric Encryption Padding) data.
865#[derive(Clone, Debug)]
866pub struct OaepPadding {
867    pub algorithm: crate::hash::HashAlgorithmId,
868    pub label: Vec<u8>,
869}
870
871/// Supported encryption padding schemes.
872#[derive(Clone, Debug)]
873pub enum EncryptionPadding {
874    /// OAEP (Optimal Asymmetric Encryption Padding) scheme.
875    Oaep(OaepPadding),
876    /// PKCS #1 padding scheme.
877    Pkcs1,
878}
879
880struct OaepPaddingInfo<'a> {
881    _borrowed: &'a OaepPadding,
882    // Lifetime marker for borrowed hash algorithm identifier string
883    // FIXME: Just use &'static [u16] for alg ID once winapi 0.4 is released
884    value: BCRYPT_OAEP_PADDING_INFO,
885}
886
887impl OaepPadding {
888    fn to_ffi_args(&self, out: &mut WindowsString) -> OaepPaddingInfo {
889        *out = WindowsString::from(self.algorithm.to_str());
890        OaepPaddingInfo {
891            _borrowed: self,
892            value: BCRYPT_OAEP_PADDING_INFO {
893                pszAlgId: out.as_ptr(),
894                pbLabel: self.label.as_ptr() as *mut _,
895                cbLabel: self.label.len() as u32,
896            },
897        }
898    }
899}
900
901impl EncryptionPadding {
902    fn to_ffi_args<'a>(&'a self, out: &'a mut WindowsString) -> (Option<OaepPaddingInfo<'a>>, u32) {
903        match self {
904            Self::Oaep(oaep_padding) => (Some(oaep_padding.to_ffi_args(out)), BCRYPT_PAD_OAEP),
905            Self::Pkcs1 => (None, BCRYPT_PAD_PKCS1),
906        }
907    }
908}
909
910impl<P: Parts> AsymmetricKey<Rsa, P> {
911    /// Encrypt a message using the RSA key.
912    pub fn encrypt(&self, padding: Option<EncryptionPadding>, data: &[u8]) -> Result<Box<[u8]>> {
913        let mut out = WindowsString::new();
914        let padding = padding.as_ref().map(|x| x.to_ffi_args(&mut out));
915
916        let (pad_info, flags) = padding.as_ref().unwrap_or(&(None, 0));
917        let pad_info = pad_info
918            .as_ref()
919            .map(|pad| &pad.value as *const BCRYPT_OAEP_PADDING_INFO as *mut _);
920
921        let mut encrypted_len = 0;
922        unsafe {
923            crate::Error::check(BCryptEncrypt(
924                self.0.as_ptr(),
925                data.as_ptr() as _,
926                data.len() as _,
927                pad_info.unwrap_or_else(null_mut),
928                null_mut(),
929                0,
930                null_mut(),
931                0,
932                &mut encrypted_len,
933                *flags,
934            ))?;
935
936            let mut output = vec![0u8; encrypted_len as usize];
937
938            crate::Error::check(BCryptEncrypt(
939                self.0.as_ptr(),
940                data.as_ptr() as _,
941                data.len() as _,
942                pad_info.unwrap_or_else(null_mut),
943                null_mut(),
944                0,
945                output.as_mut_ptr(),
946                output.len() as u32,
947                &mut encrypted_len,
948                *flags,
949            ))
950            .map(|_| output.into_boxed_slice())
951        }
952    }
953}
954
955impl AsymmetricKey<Rsa, Private> {
956    /// Decrypt a message using the RSA key.
957    pub fn decrypt(&self, padding: Option<EncryptionPadding>, data: &[u8]) -> Result<Box<[u8]>> {
958        let mut out = WindowsString::new();
959        let padding = padding.as_ref().map(|x| x.to_ffi_args(&mut out));
960
961        let (pad_info, flags) = padding.as_ref().unwrap_or(&(None, 0));
962        let pad_info = pad_info
963            .as_ref()
964            .map(|pad| &pad.value as *const BCRYPT_OAEP_PADDING_INFO as *mut _);
965
966        let mut encrypted_len = 0;
967        unsafe {
968            crate::Error::check(BCryptDecrypt(
969                self.0.as_ptr(),
970                data.as_ptr() as _,
971                data.len() as _,
972                pad_info.unwrap_or_else(null_mut),
973                null_mut(),
974                0,
975                null_mut(),
976                0,
977                &mut encrypted_len,
978                *flags,
979            ))?;
980
981            let mut output = vec![0u8; encrypted_len as usize];
982
983            crate::Error::check(BCryptDecrypt(
984                self.0.as_ptr(),
985                data.as_ptr() as _,
986                data.len() as _,
987                pad_info.unwrap_or_else(null_mut),
988                null_mut(),
989                0,
990                output.as_mut_ptr(),
991                output.len() as u32,
992                &mut encrypted_len,
993                *flags,
994            ))
995            .map(|_| output.into_boxed_slice())
996        }
997    }
998}
999
1000#[cfg(test)]
1001mod tests {
1002    use super::*;
1003
1004    #[test]
1005    fn import_export() -> Result<()> {
1006        let dynamic = AsymmetricKey::builder(AsymmetricAlgorithmId::Rsa)
1007            .key_bits(1024)
1008            .build()?;
1009        let blob = dynamic.export()?;
1010        let blob = blob.try_into().unwrap_or_else(|_| panic!());
1011
1012        let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa)?;
1013        let imported = AsymmetricKey::<_, Private>::import(Rsa, &provider, &blob)?;
1014        let imported_blob = imported.export()?;
1015
1016        assert_eq!(blob.modulus(), imported_blob.modulus());
1017        assert_eq!(blob.pub_exp(), imported_blob.pub_exp());
1018        assert_eq!(blob.prime1(), imported_blob.prime1());
1019
1020        AsymmetricKey::<Rsa, Private>::import_from_parts(
1021            &provider,
1022            &RsaKeyPrivatePayload {
1023                pub_exp: blob.pub_exp(),
1024                modulus: blob.modulus(),
1025                prime1: blob.prime1(),
1026                prime2: blob.prime2(),
1027            },
1028        )?;
1029
1030        let key = AsymmetricKey::builder(Ecdsa(NistP521)).build()?;
1031        let blob = key.export()?;
1032        dbg!(blob.x().len());
1033        dbg!(blob.y().len());
1034        dbg!(blob.d().len());
1035
1036        let key = AsymmetricKey::builder(Ecdh(Curve25519)).build()?;
1037        let blob = key.export()?;
1038        dbg!(blob.x().len());
1039        dbg!(blob.y().len());
1040        dbg!(blob.d().len());
1041        Ok(())
1042    }
1043
1044    #[test]
1045    fn encrypt_decrypt() {
1046        let key = AsymmetricKey::builder(Rsa).key_bits(1024).build().unwrap();
1047
1048        let plaintext = b"This is an important message.";
1049
1050        let ciphertext = key.encrypt(None, &*plaintext);
1051        // Can't encrypt incomplete blocks without any padding
1052        assert!(ciphertext.is_err());
1053
1054        let padding = Some(EncryptionPadding::Pkcs1);
1055        let ciphertext = key.encrypt(padding.clone(), &*plaintext).unwrap();
1056        assert_eq!(ciphertext.len(), 1024 / 8);
1057        let decoded = key.decrypt(padding, ciphertext.as_ref()).unwrap();
1058        assert_eq!(plaintext, decoded.as_ref());
1059
1060        let padding = Some(EncryptionPadding::Oaep(OaepPadding {
1061            algorithm: crate::hash::HashAlgorithmId::Sha256,
1062            label: Vec::from(b"some data" as &[_]),
1063        }));
1064        let ciphertext = key.encrypt(padding.clone(), &*plaintext).unwrap();
1065        assert_eq!(ciphertext.len(), 1024 / 8);
1066        let decoded = key.decrypt(padding.clone(), ciphertext.as_ref()).unwrap();
1067        assert_eq!(plaintext, decoded.as_ref());
1068
1069        // Check if private key can decrypt what's been encrypted with public one
1070        let blob = key.as_public().export().unwrap();
1071        let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa).unwrap();
1072        let public_key = AsymmetricKey::<Rsa, Public>::import(Rsa, &provider, &blob).unwrap();
1073
1074        let padding = Some(EncryptionPadding::Pkcs1);
1075        let ciphertext = public_key.encrypt(padding.clone(), &*plaintext).unwrap();
1076        assert_eq!(ciphertext.len(), 1024 / 8);
1077        let decoded = key.decrypt(padding, ciphertext.as_ref()).unwrap();
1078        assert_eq!(plaintext, decoded.as_ref());
1079    }
1080
1081    #[test]
1082    fn send() {
1083        use crate::helpers::assert_send;
1084        assert_send::<AsymmetricAlgorithm>();
1085        assert_send::<AsymmetricKey>();
1086        assert_send::<AsymmetricKey<Rsa, Private>>();
1087        assert_send::<AsymmetricKey<Ecdh<Curve25519>, Private>>();
1088    }
1089}