openpgp_card_rpgp/
private.rs

1// SPDX-FileCopyrightText: Wiktor Kwapisiewicz <wiktor@metacode.biz>
2// SPDX-FileCopyrightText: Heiko Schaefer <heiko@schaefer.name>
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5use openpgp_card::ocard::crypto::{CardUploadableKey, EccKey, EccType, PrivateKeyMaterial, RSAKey};
6use openpgp_card::ocard::data::{Fingerprint, KeyGenerationTime};
7use openpgp_card::Error;
8use pgp::crypto::ecc_curve::ECCCurve;
9use pgp::types::{
10    EcdhPublicParams, EcdsaPublicParams, EddsaLegacyPublicParams, KeyDetails, PlainSecretParams,
11    PublicKeyTrait, PublicParams, SecretParams,
12};
13use rsa::traits::{PrivateKeyParts, PublicKeyParts};
14use rsa::BigUint;
15
16/// Shared type for rPGP "secret key" packets (primary or subkey)
17enum Sec {
18    Key(pgp::packet::SecretKey),
19    SubKey(pgp::packet::SecretSubkey),
20}
21
22impl Sec {
23    fn public_params(&self) -> &PublicParams {
24        match self {
25            Sec::Key(sk) => sk.public_key().public_params(),
26            Sec::SubKey(ssk) => ssk.public_key().public_params(),
27        }
28    }
29
30    fn secret_params(&self) -> &SecretParams {
31        match self {
32            Sec::Key(sk) => sk.secret_params(),
33            Sec::SubKey(ssk) => ssk.secret_params(),
34        }
35    }
36}
37
38/// Private key material that can be uploaded to a card slot.
39///
40/// The data in an `UploadableKey` corresponds to an OpenPGP secret key packet.
41pub struct UploadableKey {
42    key: Sec,
43    unlocked: Option<PlainSecretParams>,
44}
45
46impl From<pgp::packet::SecretKey> for UploadableKey {
47    fn from(value: pgp::packet::SecretKey) -> Self {
48        Self {
49            key: Sec::Key(value),
50            unlocked: None,
51        }
52    }
53}
54
55impl From<pgp::packet::SecretSubkey> for UploadableKey {
56    fn from(value: pgp::packet::SecretSubkey) -> Self {
57        Self {
58            key: Sec::SubKey(value),
59            unlocked: None,
60        }
61    }
62}
63
64impl UploadableKey {
65    pub fn is_locked(&self) -> bool {
66        match self.key.secret_params() {
67            SecretParams::Plain(_) => false,
68            SecretParams::Encrypted(_) => true,
69        }
70    }
71
72    /// Returns:
73    /// - `Ok(false)` for keys that are not password protected
74    /// - `Ok(true)` for protected keys that were successfully unlocked
75    /// - `Err` when the password did not unlock the key (in this case, the key cannot be imported to a card).
76    pub fn try_unlock(&mut self, pw: &str) -> Result<bool, Error> {
77        match self.key.secret_params() {
78            SecretParams::Plain(_) => Ok(false),
79            SecretParams::Encrypted(esp) => {
80                match &self.key {
81                    // FIXME: this duplication is unfortunate
82                    Sec::Key(sk) => {
83                        if let Ok(psp) = esp.unlock(&pw.into(), sk.public_key(), None) {
84                            self.unlocked = Some(psp);
85                            return Ok(true);
86                        }
87                    }
88                    Sec::SubKey(ssk) => {
89                        if let Ok(psp) = esp.unlock(&pw.into(), ssk.public_key(), None) {
90                            self.unlocked = Some(psp);
91                            return Ok(true);
92                        }
93                    }
94                }
95
96                Err(Error::InternalError("Could not unlock key".to_string()))
97            }
98        }
99    }
100}
101
102impl CardUploadableKey for UploadableKey {
103    fn private_key(&self) -> Result<PrivateKeyMaterial, Error> {
104        fn to_privatekeymaterial(
105            psp: &PlainSecretParams,
106            pp: &PublicParams,
107        ) -> Result<PrivateKeyMaterial, Error> {
108            match (psp, pp) {
109                (PlainSecretParams::RSA(secret), PublicParams::RSA(public)) => {
110                    let (d, p, q, _u) = secret.to_bytes();
111
112                    let rsa_key = Rsa::new(
113                        public.key.e().to_bytes_be(),
114                        d,
115                        public.key.n().to_bytes_be(),
116                        p,
117                        q,
118                    )?;
119                    Ok(PrivateKeyMaterial::R(Box::new(rsa_key)))
120                }
121                (PlainSecretParams::ECDSA(s), PublicParams::ECDSA(p)) => {
122                    let (curve, public, private) = match (p, s) {
123                        (
124                            EcdsaPublicParams::P256 { key },
125                            pgp::crypto::ecdsa::SecretKey::P256(secret),
126                        ) => (
127                            ECCCurve::P256,
128                            key.to_sec1_bytes(),
129                            secret.to_bytes().to_vec(),
130                        ),
131                        (
132                            EcdsaPublicParams::P384 { key },
133                            pgp::crypto::ecdsa::SecretKey::P384(secret),
134                        ) => (
135                            ECCCurve::P384,
136                            key.to_sec1_bytes(),
137                            secret.to_bytes().to_vec(),
138                        ),
139                        (
140                            EcdsaPublicParams::P521 { key },
141                            pgp::crypto::ecdsa::SecretKey::P521(secret),
142                        ) => (
143                            ECCCurve::P521,
144                            key.to_sec1_bytes(),
145                            secret.to_bytes().to_vec(),
146                        ),
147                        (
148                            EcdsaPublicParams::Secp256k1 { .. },
149                            pgp::crypto::ecdsa::SecretKey::Secp256k1(_),
150                        ) => {
151                            return Err(Error::UnsupportedAlgo(
152                                "ECDSA with curve Secp256k1 is unsupported".to_string(),
153                            ))
154                        }
155                        _ => {
156                            return Err(Error::UnsupportedAlgo(format!(
157                                "ECDSA with unsupported algorithm(s) {p:?} / {s:?}"
158                            )))
159                        }
160                    };
161
162                    let ecc = Ecc::new(curve, private, public.to_vec(), EccType::ECDSA);
163                    Ok(PrivateKeyMaterial::E(Box::new(ecc)))
164                }
165                (
166                    PlainSecretParams::Ed25519Legacy(m),
167                    PublicParams::EdDSALegacy(EddsaLegacyPublicParams::Ed25519 { key }),
168                ) => {
169                    let mut public = Vec::with_capacity(33);
170                    public.push(0x40);
171                    public.extend_from_slice(key.as_bytes());
172
173                    let ecc = Ecc::new(
174                        ECCCurve::Ed25519.clone(),
175                        m.as_bytes().to_vec(),
176                        public,
177                        EccType::EdDSA,
178                    );
179                    Ok(PrivateKeyMaterial::E(Box::new(ecc)))
180                }
181                (
182                    PlainSecretParams::ECDH(pgp::crypto::ecdh::SecretKey::Curve25519(secret)),
183                    PublicParams::ECDH(EcdhPublicParams::Curve25519 { p, .. }),
184                ) => {
185                    // Behold: the sad reversed x25519 wire format in OpenPGP
186                    let reversed_private = secret.to_bytes_rev();
187
188                    let mut public = Vec::with_capacity(33);
189                    public.push(0x40);
190                    public.extend_from_slice(p.as_ref());
191
192                    let ecc = Ecc::new(
193                        ECCCurve::Curve25519.clone(),
194                        reversed_private.to_vec(),
195                        public.to_vec(),
196                        EccType::ECDH,
197                    );
198                    Ok(PrivateKeyMaterial::E(Box::new(ecc)))
199                }
200                (
201                    PlainSecretParams::ECDH(pgp::crypto::ecdh::SecretKey::P256 { secret }),
202                    PublicParams::ECDH(EcdhPublicParams::P256 { p, .. }),
203                ) => {
204                    let ecc = Ecc::new(
205                        ECCCurve::P256.clone(),
206                        secret.to_bytes().to_vec(),
207                        p.to_sec1_bytes().to_vec(),
208                        EccType::ECDH,
209                    );
210                    Ok(PrivateKeyMaterial::E(Box::new(ecc)))
211                }
212                (
213                    PlainSecretParams::ECDH(pgp::crypto::ecdh::SecretKey::P384 { secret }),
214                    PublicParams::ECDH(EcdhPublicParams::P384 { p, .. }),
215                ) => {
216                    let ecc = Ecc::new(
217                        ECCCurve::P384.clone(),
218                        secret.to_bytes().to_vec(),
219                        p.to_sec1_bytes().to_vec(),
220                        EccType::ECDH,
221                    );
222                    Ok(PrivateKeyMaterial::E(Box::new(ecc)))
223                }
224                (
225                    PlainSecretParams::ECDH(pgp::crypto::ecdh::SecretKey::P521 { secret }),
226                    PublicParams::ECDH(EcdhPublicParams::P521 { p, .. }),
227                ) => {
228                    let ecc = Ecc::new(
229                        ECCCurve::P521.clone(),
230                        secret.to_bytes().to_vec(),
231                        p.to_sec1_bytes().to_vec(),
232                        EccType::ECDH,
233                    );
234                    Ok(PrivateKeyMaterial::E(Box::new(ecc)))
235                }
236
237                _ => Err(Error::UnsupportedAlgo(format!(
238                    "Unsupported key material {pp:?}"
239                ))),
240            }
241        }
242
243        let pp = self.key.public_params();
244
245        let psp = match self.key.secret_params() {
246            SecretParams::Plain(psp) => psp,
247            SecretParams::Encrypted(_) => {
248                if let Some(psp) = &self.unlocked {
249                    psp
250                } else {
251                    return Err(Error::InternalError(
252                        "Secret key packet wasn't unlocked".to_string(),
253                    ));
254                }
255            }
256        };
257
258        to_privatekeymaterial(psp, pp)
259    }
260
261    fn timestamp(&self) -> KeyGenerationTime {
262        let ts = match &self.key {
263            Sec::Key(sk) => sk.public_key().created_at(),
264            Sec::SubKey(ssk) => ssk.public_key().created_at(),
265        };
266
267        let ts = ts.timestamp() as u32;
268        ts.into()
269    }
270
271    fn fingerprint(&self) -> Result<Fingerprint, Error> {
272        let fp = match &self.key {
273            Sec::Key(sk) => sk.fingerprint(),
274            Sec::SubKey(ssk) => ssk.fingerprint(),
275        };
276
277        Fingerprint::try_from(fp.as_bytes())
278    }
279}
280
281struct Rsa {
282    e: Vec<u8>,
283    n: Vec<u8>,
284    p: Vec<u8>,
285    q: Vec<u8>,
286    pq: Vec<u8>,
287    dp1: Vec<u8>,
288    dq1: Vec<u8>,
289}
290
291impl Rsa {
292    fn new(e: Vec<u8>, d: Vec<u8>, n: Vec<u8>, p: Vec<u8>, q: Vec<u8>) -> Result<Self, Error> {
293        let key = rsa::RsaPrivateKey::from_components(
294            BigUint::from_bytes_be(&n),
295            BigUint::from_bytes_be(&e),
296            BigUint::from_bytes_be(&d),
297            vec![BigUint::from_bytes_be(&p), BigUint::from_bytes_be(&q)],
298        )
299        .map_err(|e| Error::InternalError(format!("rsa error {e:?}")))?;
300
301        let pq = key
302            .qinv()
303            .ok_or_else(|| Error::InternalError("pq value missing".into()))?
304            .to_biguint()
305            .ok_or_else(|| Error::InternalError("conversion to bigunit failed".into()))?
306            .to_bytes_be();
307
308        let dp1 = key
309            .dp()
310            .ok_or_else(|| Error::InternalError("dp1 value missing".into()))?
311            .to_bytes_be();
312
313        let dq1 = key
314            .dq()
315            .ok_or_else(|| Error::InternalError("dq1 value missing".into()))?
316            .to_bytes_be();
317
318        Ok(Self {
319            e,
320            n,
321            p,
322            q,
323            pq,
324            dp1,
325            dq1,
326        })
327    }
328}
329
330impl RSAKey for Rsa {
331    fn e(&self) -> &[u8] {
332        self.e.as_ref()
333    }
334
335    fn p(&self) -> &[u8] {
336        self.p.as_ref()
337    }
338
339    fn q(&self) -> &[u8] {
340        self.q.as_ref()
341    }
342
343    fn pq(&self) -> Box<[u8]> {
344        Box::from(self.pq.as_ref())
345    }
346
347    fn dp1(&self) -> Box<[u8]> {
348        Box::from(self.dp1.as_ref())
349    }
350
351    fn dq1(&self) -> Box<[u8]> {
352        Box::from(self.dq1.as_ref())
353    }
354
355    fn n(&self) -> &[u8] {
356        self.n.as_ref()
357    }
358}
359
360/// ECC-specific data-structure to hold private (sub)key material for upload
361/// with the `openpgp-card` crate.
362struct Ecc {
363    curve: ECCCurve,
364    private: Vec<u8>,
365    public: Vec<u8>,
366    ecc_type: EccType,
367
368    oid: Vec<u8>,
369}
370
371impl Ecc {
372    fn new(curve: ECCCurve, private: Vec<u8>, public: Vec<u8>, ecc_type: EccType) -> Self {
373        let oid = curve.oid();
374
375        Ecc {
376            curve,
377            private,
378            public,
379            ecc_type,
380            oid,
381        }
382    }
383}
384
385fn pad(mut v: Vec<u8>, len: usize) -> Vec<u8> {
386    while v.len() < len {
387        v.insert(0, 0)
388    }
389
390    v
391}
392
393impl EccKey for Ecc {
394    fn oid(&self) -> &[u8] {
395        &self.oid
396    }
397
398    fn private(&self) -> Vec<u8> {
399        match self.curve {
400            ECCCurve::P256 => pad(self.private.clone(), 0x20),
401            ECCCurve::P384 => pad(self.private.clone(), 0x30),
402            ECCCurve::P521 => pad(self.private.clone(), 0x42),
403            ECCCurve::Curve25519 | ECCCurve::Ed25519 => pad(self.private.clone(), 0x20),
404            _ => self.private.clone(),
405        }
406    }
407
408    fn public(&self) -> Vec<u8> {
409        // FIXME: padding?
410        self.public.clone()
411    }
412
413    fn ecc_type(&self) -> EccType {
414        self.ecc_type
415    }
416}