openpgp_card/ocard/
algorithm.rs

1// SPDX-FileCopyrightText: 2021-2023 Heiko Schaefer <heiko@schaefer.name>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Data structures that specify algorithms to use on an OpenPGP card.
5//!
6//! [`AlgorithmAttributes`] (and its components) model "Algorithm Attributes"
7//! as described in the OpenPGP card specification.
8//!
9//! [`AlgoSimple`] offers a shorthand for specifying an algorithm,
10//! specifically for key generation on the card.
11
12use std::convert::TryFrom;
13use std::fmt;
14
15use crate::ocard::crypto::EccType;
16use crate::ocard::keys;
17use crate::ocard::{oid, KeyType, Transaction};
18use crate::Error;
19
20/// A shorthand way to specify algorithms (e.g. for key generation).
21#[derive(Clone, Copy, Debug)]
22pub enum AlgoSimple {
23    RSA1k,
24    RSA2k,
25    RSA3k,
26    RSA4k,
27    NIST256,
28    NIST384,
29    NIST521,
30    Curve25519,
31}
32
33impl TryFrom<&str> for AlgoSimple {
34    type Error = Error;
35
36    fn try_from(algo: &str) -> Result<Self, Self::Error> {
37        use AlgoSimple::*;
38
39        Ok(match algo {
40            "RSA1k" => RSA1k,
41            "RSA2k" => RSA2k,
42            "RSA3k" => RSA3k,
43            "RSA4k" => RSA4k,
44            "NIST256" => NIST256,
45            "NIST384" => NIST384,
46            "NIST521" => NIST521,
47            "Curve25519" => Curve25519,
48            _ => return Err(Error::UnsupportedAlgo(format!("unexpected algo {algo}"))),
49        })
50    }
51}
52
53impl AlgoSimple {
54    /// Get algorithm attributes for slot `key_type` from this AlgoSimple.
55    ///
56    /// AlgoSimple doesn't specify card specific details (such as bit-size
57    /// of e for RSA, and import format).
58    /// This function determines these values based on information from the
59    /// card behind `tx`.
60    pub fn matching_algorithm_attributes(
61        &self,
62        tx: &mut Transaction,
63        key_type: KeyType,
64    ) -> Result<AlgorithmAttributes, Error> {
65        let ard = tx.application_related_data()?;
66        let algorithm_attributes = ard.algorithm_attributes(key_type)?;
67
68        let algo_info = tx.algorithm_information_cached().ok().flatten();
69
70        self.determine_algo_attributes(key_type, algorithm_attributes, algo_info)
71    }
72
73    /// Get corresponding EccType by KeyType (except for Curve25519)
74    fn ecc_type(key_type: KeyType) -> EccType {
75        match key_type {
76            KeyType::Signing | KeyType::Authentication | KeyType::Attestation => EccType::ECDSA,
77            KeyType::Decryption => EccType::ECDH,
78        }
79    }
80
81    /// Get corresponding EccType by KeyType for Curve25519
82    fn ecc_type_25519(key_type: KeyType) -> EccType {
83        match key_type {
84            KeyType::Signing | KeyType::Authentication | KeyType::Attestation => EccType::EdDSA,
85            KeyType::Decryption => EccType::ECDH,
86        }
87    }
88
89    /// Get corresponding Curve by KeyType for 25519 (Ed25519 vs Cv25519)
90    fn curve_for_25519(key_type: KeyType) -> Curve {
91        match key_type {
92            KeyType::Signing | KeyType::Authentication | KeyType::Attestation => Curve::Ed25519,
93            KeyType::Decryption => Curve::Curve25519,
94        }
95    }
96
97    /// Return the appropriate Algo for this AlgoSimple.
98    ///
99    /// This mapping depends on the actual card in use
100    /// (e.g.: the size of "e", in RSA can differ;
101    /// or a different `import_format` can be selected).
102    ///
103    /// These card-specific settings are derived from `algorithm_attributes` and `algo_info`.
104    pub(crate) fn determine_algo_attributes(
105        &self,
106        key_type: KeyType,
107        algorithm_attributes: AlgorithmAttributes,
108        algo_info: Option<AlgorithmInformation>,
109    ) -> Result<AlgorithmAttributes, Error> {
110        let algo = match self {
111            Self::RSA1k => AlgorithmAttributes::Rsa(keys::determine_rsa_attrs(
112                1024,
113                key_type,
114                algorithm_attributes,
115                algo_info,
116            )?),
117            Self::RSA2k => AlgorithmAttributes::Rsa(keys::determine_rsa_attrs(
118                2048,
119                key_type,
120                algorithm_attributes,
121                algo_info,
122            )?),
123            Self::RSA3k => AlgorithmAttributes::Rsa(keys::determine_rsa_attrs(
124                3072,
125                key_type,
126                algorithm_attributes,
127                algo_info,
128            )?),
129            Self::RSA4k => AlgorithmAttributes::Rsa(keys::determine_rsa_attrs(
130                4096,
131                key_type,
132                algorithm_attributes,
133                algo_info,
134            )?),
135            Self::NIST256 => AlgorithmAttributes::Ecc(keys::determine_ecc_attrs(
136                Curve::NistP256r1.oid(),
137                Self::ecc_type(key_type),
138                key_type,
139                algo_info,
140            )?),
141            Self::NIST384 => AlgorithmAttributes::Ecc(keys::determine_ecc_attrs(
142                Curve::NistP384r1.oid(),
143                Self::ecc_type(key_type),
144                key_type,
145                algo_info,
146            )?),
147            Self::NIST521 => AlgorithmAttributes::Ecc(keys::determine_ecc_attrs(
148                Curve::NistP521r1.oid(),
149                Self::ecc_type(key_type),
150                key_type,
151                algo_info,
152            )?),
153            Self::Curve25519 => AlgorithmAttributes::Ecc(keys::determine_ecc_attrs(
154                Self::curve_for_25519(key_type).oid(),
155                Self::ecc_type_25519(key_type),
156                key_type,
157                algo_info,
158            )?),
159        };
160
161        Ok(algo)
162    }
163}
164
165/// "Algorithm Information" enumerates which algorithms the current card supports
166/// [Spec section 4.4.3.11]
167///
168/// Modern OpenPGP cards (starting with version v3.4) provide a list of
169/// algorithms they support for each key slot.
170/// The Algorithm Information list specifies which [`AlgorithmAttributes`]
171/// can be used on that card (for key generation or key import).
172#[derive(Debug, Clone, Eq, PartialEq)]
173pub struct AlgorithmInformation(pub(crate) Vec<(KeyType, AlgorithmAttributes)>);
174
175/// Algorithm Attributes [Spec section 4.4.3.9]
176///
177/// [`AlgorithmAttributes`] describes the algorithm settings for a key on the card.
178///
179/// This setting specifies the data format of:
180/// - Key import
181/// - Key generation
182/// - Export of public key data from the card (e.g. after key generation)
183#[derive(Clone, Eq, PartialEq)]
184pub enum AlgorithmAttributes {
185    Rsa(RsaAttributes),
186    Ecc(EccAttributes),
187    Unknown(Vec<u8>),
188}
189
190impl fmt::Debug for AlgorithmAttributes {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        match self {
193            Self::Rsa(rsa) => {
194                write!(
195                    f,
196                    "RSA {} [e {}{}]",
197                    rsa.len_n,
198                    rsa.len_e,
199                    if rsa.import_format != 0 {
200                        format!(", format {}", rsa.import_format)
201                    } else {
202                        "".to_string()
203                    }
204                )
205            }
206            Self::Ecc(ecc) => {
207                write!(
208                    f,
209                    "{:?} ({:?}){}",
210                    ecc.ecc_type,
211                    ecc.curve,
212                    if ecc.import_format == Some(0xff) {
213                        " with pub"
214                    } else {
215                        ""
216                    }
217                )
218            }
219            Self::Unknown(u) => {
220                write!(f, "Unknown: {u:?}")
221            }
222        }
223    }
224}
225
226impl fmt::Display for AlgorithmAttributes {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        match self {
229            Self::Rsa(rsa) => {
230                write!(f, "RSA {}", rsa.len_n)
231            }
232            Self::Ecc(ecc) => {
233                write!(f, "{:?} ({:?})", ecc.ecc_type, ecc.curve)
234            }
235            Self::Unknown(u) => {
236                write!(f, "Unknown: {u:?}")
237            }
238        }
239    }
240}
241
242impl AlgorithmAttributes {
243    /// Get a DO representation of the Algo, for setting algorithm
244    /// attributes on the card.
245    pub(crate) fn to_data_object(&self) -> Result<Vec<u8>, Error> {
246        match self {
247            AlgorithmAttributes::Rsa(rsa) => Self::rsa_algo_attrs(rsa),
248            AlgorithmAttributes::Ecc(ecc) => Self::ecc_algo_attrs(ecc.oid(), ecc.ecc_type()),
249            _ => Err(Error::UnsupportedAlgo(format!("Unexpected Algo {self:?}"))),
250        }
251    }
252
253    /// Helper: generate `data` for algorithm attributes with RSA
254    fn rsa_algo_attrs(algo_attrs: &RsaAttributes) -> Result<Vec<u8>, Error> {
255        // Algorithm ID (01 = RSA (Encrypt or Sign))
256        let mut algo_attributes = vec![0x01];
257
258        // Length of modulus n in bit
259        algo_attributes.extend(algo_attrs.len_n().to_be_bytes());
260
261        // Length of public exponent e in bit
262        algo_attributes.push(0x00);
263        algo_attributes.push(algo_attrs.len_e() as u8);
264
265        algo_attributes.push(algo_attrs.import_format());
266
267        Ok(algo_attributes)
268    }
269
270    /// Helper: generate `data` for algorithm attributes with ECC
271    fn ecc_algo_attrs(oid: &[u8], ecc_type: EccType) -> Result<Vec<u8>, Error> {
272        let algo_id = match ecc_type {
273            EccType::EdDSA => 0x16,
274            EccType::ECDH => 0x12,
275            EccType::ECDSA => 0x13,
276        };
277
278        let mut algo_attributes = vec![algo_id];
279        algo_attributes.extend(oid);
280        // Leave Import-Format unset, for default (pg. 35)
281
282        Ok(algo_attributes)
283    }
284}
285
286/// RSA specific attributes of [`AlgorithmAttributes`]
287#[derive(Debug, Clone, Eq, PartialEq)]
288pub struct RsaAttributes {
289    len_n: u16,
290    len_e: u16,
291    import_format: u8,
292}
293
294impl RsaAttributes {
295    pub fn new(len_n: u16, len_e: u16, import_format: u8) -> Self {
296        Self {
297            len_n,
298            len_e,
299            import_format,
300        }
301    }
302
303    pub fn len_n(&self) -> u16 {
304        self.len_n
305    }
306
307    pub fn len_e(&self) -> u16 {
308        self.len_e
309    }
310
311    pub fn import_format(&self) -> u8 {
312        self.import_format
313    }
314}
315
316/// ECC specific attributes of [`AlgorithmAttributes`]
317#[derive(Debug, Clone, Eq, PartialEq)]
318pub struct EccAttributes {
319    ecc_type: EccType,
320    curve: Curve,
321    import_format: Option<u8>,
322}
323
324impl EccAttributes {
325    pub fn new(ecc_type: EccType, curve: Curve, import_format: Option<u8>) -> Self {
326        Self {
327            ecc_type,
328            curve,
329            import_format,
330        }
331    }
332
333    pub fn ecc_type(&self) -> EccType {
334        self.ecc_type
335    }
336
337    pub fn curve(&self) -> &Curve {
338        &self.curve
339    }
340
341    pub fn oid(&self) -> &[u8] {
342        self.curve.oid()
343    }
344
345    pub fn import_format(&self) -> Option<u8> {
346        self.import_format
347    }
348}
349
350/// Enum for naming ECC curves, and mapping them to/from their OIDs.
351#[derive(Debug, Clone, Eq, PartialEq)]
352pub enum Curve {
353    NistP256r1,
354    NistP384r1,
355    NistP521r1,
356    BrainpoolP256r1,
357    BrainpoolP384r1,
358    BrainpoolP512r1,
359    Secp256k1,
360    Ed25519,
361    Curve25519,
362    Ed448,
363    X448,
364
365    Unknown(Vec<u8>),
366}
367
368impl Curve {
369    pub fn oid(&self) -> &[u8] {
370        use Curve::*;
371        match self {
372            NistP256r1 => oid::NIST_P256R1,
373            NistP384r1 => oid::NIST_P384R1,
374            NistP521r1 => oid::NIST_P521R1,
375            BrainpoolP256r1 => oid::BRAINPOOL_P256R1,
376            BrainpoolP384r1 => oid::BRAINPOOL_P384R1,
377            BrainpoolP512r1 => oid::BRAINPOOL_P512R1,
378            Secp256k1 => oid::SECP256K1,
379            Ed25519 => oid::ED25519,
380            Curve25519 => oid::CV25519,
381            Ed448 => oid::ED448,
382            X448 => oid::X448,
383
384            Unknown(oid) => oid,
385        }
386    }
387}
388
389impl TryFrom<&[u8]> for Curve {
390    type Error = Error;
391
392    fn try_from(oid: &[u8]) -> Result<Self, Self::Error> {
393        use Curve::*;
394
395        let curve = match oid {
396            oid::NIST_P256R1 => NistP256r1,
397            oid::NIST_P384R1 => NistP384r1,
398            oid::NIST_P521R1 => NistP521r1,
399
400            oid::BRAINPOOL_P256R1 => BrainpoolP256r1,
401            oid::BRAINPOOL_P384R1 => BrainpoolP384r1,
402            oid::BRAINPOOL_P512R1 => BrainpoolP512r1,
403
404            oid::SECP256K1 => Secp256k1,
405
406            oid::ED25519 => Ed25519,
407            oid::CV25519 => Curve25519,
408
409            oid::ED448 => Ed448,
410            oid::X448 => X448,
411
412            _ => Unknown(oid.to_vec()),
413        };
414
415        Ok(curve)
416    }
417}