Skip to main content

sshcerts/ssh/
privkey.rs

1use std::convert::TryInto;
2use std::fmt;
3use std::fs::File;
4use std::io::{self, Read};
5use std::path::Path;
6
7use ring::signature::Ed25519KeyPair;
8use ring::{rand, signature, signature::KeyPair};
9
10use zeroize::Zeroize;
11
12use crate::{
13    error::Error,
14    ssh::{
15        keytype::{Curve, KeyType, KeyTypeKind},
16        reader::Reader,
17        writer::Writer,
18        CurveKind, EcdsaPublicKey, Ed25519PublicKey, PublicKey, PublicKeyKind, RsaPublicKey,
19    },
20    utils::format_signature_for_ssh,
21    Result,
22};
23
24#[cfg(feature = "rsa-signing")]
25use num_bigint::{BigInt, BigUint, Sign};
26
27#[cfg(feature = "rsa-signing")]
28use simple_asn1::{ASN1Block, ASN1Class, ToASN1};
29
30#[cfg(feature = "encrypted-keys")]
31use aes::{
32    cipher::{generic_array::GenericArray, NewCipher, StreamCipher},
33    Aes256Ctr,
34};
35
36#[cfg(feature = "encrypted-keys")]
37use bcrypt_pbkdf::bcrypt_pbkdf;
38
39const SSH_SK_USER_PRESENCE_REQD: u8 = 0x01;
40
41/// Whether a hardware-backed key requires a user touch for signing.
42#[derive(Debug, PartialEq, Eq, Clone, Copy)]
43pub enum TouchRequirement {
44    /// Signing requires a user touch.
45    Required,
46
47    /// Signing does not require a user touch.
48    NotRequired,
49
50    /// The touch requirement could not be determined.
51    Unknown,
52}
53
54impl TouchRequirement {
55    /// Returns true when signing is known to require touch.
56    pub fn is_required(self) -> bool {
57        matches!(self, TouchRequirement::Required)
58    }
59}
60
61fn touch_requirement_from_flags(flags: u8) -> TouchRequirement {
62    if (flags & SSH_SK_USER_PRESENCE_REQD) != 0 {
63        TouchRequirement::Required
64    } else {
65        TouchRequirement::NotRequired
66    }
67}
68
69/// RSA private key.
70#[derive(Debug, PartialEq, Eq, Clone, Zeroize)]
71pub struct RsaPrivateKey {
72    /// Modulus of key.
73    pub n: Vec<u8>,
74
75    /// Public key exponent
76    pub e: Vec<u8>,
77
78    /// Private key exponent.
79    pub d: Vec<u8>,
80
81    /// CRT coefficient q^(-1) mod p.
82    pub coefficient: Vec<u8>,
83
84    /// Prime factor p of n
85    pub p: Vec<u8>,
86
87    /// Prime factor q of n
88    pub q: Vec<u8>,
89
90    /// Exponent using p
91    pub exp: Option<Vec<u8>>,
92
93    /// Exponent using q
94    pub exq: Option<Vec<u8>>,
95}
96
97/// ECDSA private key.
98#[derive(Debug, PartialEq, Eq, Clone, Zeroize)]
99pub struct EcdsaPrivateKey {
100    /// The curve being used.
101    pub curve: Curve,
102
103    /// The private key.
104    pub key: Vec<u8>,
105}
106
107/// Hardware backed ECDSA private key.
108#[derive(Debug, PartialEq, Eq, Clone, Zeroize)]
109pub struct EcdsaSkPrivateKey {
110    /// Flags set on the private key
111    pub flags: u8,
112
113    /// The private key handle
114    pub handle: Vec<u8>,
115
116    /// Space reserved for future use
117    pub reserved: Vec<u8>,
118
119    /// Pin to use with external device when doing signing
120    /// operations
121    pub pin: Option<String>,
122
123    /// Path to hardware device to use for signing. If not
124    /// provided, the system will choose one (either randomly
125    /// or by user selection)
126    pub device_path: Option<String>,
127}
128
129/// ED25519 private key.
130#[derive(Debug, PartialEq, Eq, Clone, Zeroize)]
131pub struct Ed25519PrivateKey {
132    /// The private key.
133    pub key: Vec<u8>,
134}
135
136/// Hardware backed Ed25519 private key.
137#[derive(Debug, PartialEq, Eq, Clone, Zeroize)]
138pub struct Ed25519SkPrivateKey {
139    /// Flags set on the private key
140    pub flags: u8,
141
142    /// The private key handle
143    pub handle: Vec<u8>,
144
145    /// Space reserved for future use
146    pub reserved: Vec<u8>,
147
148    /// Pin to use with external device when doing signing
149    /// operations
150    pub pin: Option<String>,
151
152    /// Path to hardware device to use for signing. If not
153    /// provided, the system will choose one (either randomly
154    /// or by user selection)
155    pub device_path: Option<String>,
156}
157
158impl EcdsaSkPrivateKey {
159    /// Returns the touch requirement for this hardware-backed key.
160    pub fn touch_requirement(&self) -> TouchRequirement {
161        touch_requirement_from_flags(self.flags)
162    }
163}
164
165impl Ed25519SkPrivateKey {
166    /// Returns the touch requirement for this hardware-backed key.
167    pub fn touch_requirement(&self) -> TouchRequirement {
168        touch_requirement_from_flags(self.flags)
169    }
170}
171
172/// A type which represents the different kinds a public key can be.
173#[derive(Debug, PartialEq, Eq, Clone, Zeroize)]
174pub enum PrivateKeyKind {
175    /// Represents an RSA prviate key.
176    Rsa(RsaPrivateKey),
177
178    /// Represents an ECDSA private key.
179    Ecdsa(EcdsaPrivateKey),
180
181    /// Represents an ECDSA private key stored in a hardware device
182    EcdsaSk(EcdsaSkPrivateKey),
183
184    /// Represents an Ed25519 private key.
185    Ed25519(Ed25519PrivateKey),
186
187    /// Represents an Ed25519 private key stored in a hardware device
188    Ed25519Sk(Ed25519SkPrivateKey),
189}
190
191/// A type which represents an OpenSSH private key.
192#[derive(Debug, PartialEq, Eq, Clone)]
193pub struct PrivateKey {
194    /// Key type.
195    pub key_type: KeyType,
196
197    /// The kind of public key.
198    pub kind: PrivateKeyKind,
199
200    /// The corresponding public key
201    pub pubkey: PublicKey,
202
203    /// This is the magic value used to ensure decoding happens correctly.
204    /// We store it so that we can guarantee reserialization of unencrypted
205    /// keys is bytes for byte.
206    pub magic: u32,
207
208    /// Associated comment. It appears, unlike public keys, private keys must
209    /// have comments to be valid.
210    pub comment: String,
211}
212
213#[cfg(feature = "rsa-signing")]
214impl ToASN1 for RsaPrivateKey {
215    type Error = Error;
216
217    fn to_asn1_class(&self, _class: ASN1Class) -> std::result::Result<Vec<ASN1Block>, Error> {
218        Ok(vec![ASN1Block::Sequence(
219            0,
220            [
221                vec![ASN1Block::Integer(0, BigInt::new(Sign::Plus, vec![0]))],
222                vec![ASN1Block::Integer(
223                    0,
224                    BigInt::from_bytes_be(Sign::Plus, &self.n),
225                )],
226                vec![ASN1Block::Integer(
227                    0,
228                    BigInt::from_bytes_be(Sign::Plus, &self.e),
229                )],
230                vec![ASN1Block::Integer(
231                    0,
232                    BigInt::from_bytes_be(Sign::Plus, &self.d),
233                )],
234                vec![ASN1Block::Integer(
235                    0,
236                    BigInt::from_bytes_be(Sign::Plus, &self.p),
237                )],
238                vec![ASN1Block::Integer(
239                    0,
240                    BigInt::from_bytes_be(Sign::Plus, &self.q),
241                )],
242                vec![ASN1Block::Integer(
243                    0,
244                    BigInt::from_bytes_be(Sign::Plus, self.exp.as_ref().unwrap()),
245                )],
246                vec![ASN1Block::Integer(
247                    0,
248                    BigInt::from_bytes_be(Sign::Plus, self.exq.as_ref().unwrap()),
249                )],
250                vec![ASN1Block::Integer(
251                    0,
252                    BigInt::from_bytes_be(Sign::Plus, &self.coefficient),
253                )],
254                Vec::new(),
255            ]
256            .concat(),
257        )])
258    }
259}
260
261impl super::SSHCertificateSigner for PrivateKey {
262    fn sign(&self, buffer: &[u8]) -> Option<Vec<u8>> {
263        let rng = rand::SystemRandom::new();
264
265        match &self.kind {
266            #[cfg(feature = "rsa-signing")]
267            PrivateKeyKind::Rsa(key) => {
268                let asn_privkey = match simple_asn1::der_encode(key) {
269                    Ok(apk) => apk,
270                    Err(_) => return None,
271                };
272
273                let keypair = match signature::RsaKeyPair::from_der(&asn_privkey) {
274                    Ok(kp) => kp,
275                    Err(_) => return None,
276                };
277
278                let mut signature = vec![0; keypair.public().modulus_len()];
279
280                keypair
281                    .sign(&signature::RSA_PKCS1_SHA512, &rng, buffer, &mut signature)
282                    .ok()?;
283
284                format_signature_for_ssh(&self.pubkey, &signature)
285            }
286            #[cfg(not(feature = "rsa-signing"))]
287            PrivateKeyKind::Rsa(_) => return None,
288            PrivateKeyKind::Ecdsa(key) => {
289                let alg = match key.curve.kind {
290                    CurveKind::Nistp256 => &signature::ECDSA_P256_SHA256_ASN1_SIGNING,
291                    CurveKind::Nistp384 => &signature::ECDSA_P384_SHA384_ASN1_SIGNING,
292                    CurveKind::Nistp521 => return None,
293                };
294
295                let pubkey = match &self.pubkey.kind {
296                    PublicKeyKind::Ecdsa(key) => &key.key,
297                    _ => return None,
298                };
299
300                let key = if key.key[0] == 0x0_u8 {
301                    &key.key[1..]
302                } else {
303                    &key.key
304                };
305                let key_pair = match signature::EcdsaKeyPair::from_private_key_and_public_key(
306                    alg, key, pubkey, &rng,
307                ) {
308                    Ok(kp) => kp,
309                    Err(_) => return None,
310                };
311
312                format_signature_for_ssh(&self.pubkey, key_pair.sign(&rng, buffer).ok()?.as_ref())
313            }
314            PrivateKeyKind::Ed25519(key) => {
315                let public_key = match &self.pubkey.kind {
316                    PublicKeyKind::Ed25519(key) => &key.key,
317                    _ => return None,
318                };
319
320                let key_pair =
321                    match Ed25519KeyPair::from_seed_and_public_key(&key.key[..32], public_key) {
322                        Ok(kp) => kp,
323                        Err(_) => return None,
324                    };
325
326                format_signature_for_ssh(&self.pubkey, key_pair.sign(buffer).as_ref())
327            }
328            #[cfg(any(feature = "fido-support", feature = "fido-support-mozilla"))]
329            PrivateKeyKind::Ed25519Sk(_) | PrivateKeyKind::EcdsaSk(_) => {
330                crate::fido::signing::sign_with_private_key(&self, buffer)
331            }
332            #[cfg(not(any(feature = "fido-support", feature = "fido-support-mozilla")))]
333            PrivateKeyKind::Ed25519Sk(_) | PrivateKeyKind::EcdsaSk(_) => None,
334        }
335    }
336}
337impl PrivateKey {
338    /// Create a private key from an existing reader of decrypted private bytes
339    pub fn read_private_key(reader: &mut Reader<'_>) -> Result<Self> {
340        let key_type = reader.read_string()?;
341        let kt = KeyType::from_name(&key_type)?;
342
343        let (kind, pubkey) = match kt.kind {
344            KeyTypeKind::Rsa => {
345                let n = reader.read_positive_mpint()?;
346                let e = reader.read_positive_mpint()?;
347                let d = reader.read_positive_mpint()?;
348                let coefficient = reader.read_positive_mpint()?;
349                let p = reader.read_positive_mpint()?;
350                let q = reader.read_positive_mpint()?;
351
352                #[cfg(feature = "rsa-signing")]
353                let exp = Some(
354                    BigUint::from_bytes_be(&d)
355                        .modpow(
356                            &BigUint::from_slice(&[0x1]),
357                            &(BigUint::from_bytes_be(&p) - 1_u8),
358                        )
359                        .to_bytes_be(),
360                );
361                #[cfg(not(feature = "rsa-signing"))]
362                let exp = None;
363
364                #[cfg(feature = "rsa-signing")]
365                let exq = Some(
366                    BigUint::from_bytes_be(&d)
367                        .modpow(
368                            &BigUint::from_slice(&[0x1]),
369                            &(BigUint::from_bytes_be(&q) - 1_u8),
370                        )
371                        .to_bytes_be(),
372                );
373                #[cfg(not(feature = "rsa-signing"))]
374                let exq = None;
375
376                (
377                    PrivateKeyKind::Rsa(RsaPrivateKey {
378                        n: n.clone(),
379                        e: e.clone(),
380                        d,
381                        coefficient,
382                        p,
383                        q,
384                        exp,
385                        exq,
386                    }),
387                    PublicKey {
388                        key_type: kt.clone(),
389                        kind: PublicKeyKind::Rsa(RsaPublicKey { e, n }),
390                        comment: None,
391                    },
392                )
393            }
394            KeyTypeKind::Ecdsa => {
395                let identifier = reader.read_string()?;
396                let curve = Curve::from_identifier(&identifier)?;
397                let pubkey = reader.read_bytes()?;
398
399                let (private_key, sk_application) = match kt.is_sk {
400                    true => {
401                        let sk_application = Some(reader.read_string()?);
402                        let k = EcdsaSkPrivateKey {
403                            flags: reader.read_raw_bytes(1)?[0],
404                            handle: reader.read_bytes()?,
405                            reserved: reader.read_bytes()?,
406                            pin: None,
407                            device_path: None,
408                        };
409
410                        (PrivateKeyKind::EcdsaSk(k), sk_application)
411                    }
412                    false => {
413                        let key = reader.read_bytes()?;
414                        let k = EcdsaPrivateKey {
415                            curve: curve.clone(),
416                            key,
417                        };
418                        (PrivateKeyKind::Ecdsa(k), None)
419                    }
420                };
421                (
422                    private_key,
423                    PublicKey {
424                        key_type: kt.clone(),
425                        kind: PublicKeyKind::Ecdsa(EcdsaPublicKey {
426                            curve,
427                            key: pubkey,
428                            sk_application,
429                        }),
430                        comment: None,
431                    },
432                )
433            }
434            KeyTypeKind::Ed25519 => {
435                let pubkey = reader.read_bytes()?;
436
437                let (private_key, sk_application) = match kt.is_sk {
438                    true => {
439                        let sk_application = Some(reader.read_string()?);
440                        let k = Ed25519SkPrivateKey {
441                            flags: reader.read_raw_bytes(1)?[0],
442                            handle: reader.read_bytes()?,
443                            reserved: reader.read_bytes()?,
444                            pin: None,
445                            device_path: None,
446                        };
447
448                        (PrivateKeyKind::Ed25519Sk(k), sk_application)
449                    }
450                    false => {
451                        let k = Ed25519PrivateKey {
452                            key: reader.read_bytes()?,
453                        };
454
455                        (PrivateKeyKind::Ed25519(k), None)
456                    }
457                };
458                (
459                    private_key,
460                    PublicKey {
461                        key_type: kt.clone(),
462                        kind: PublicKeyKind::Ed25519(Ed25519PublicKey {
463                            key: pubkey,
464                            sk_application,
465                        }),
466                        comment: None,
467                    },
468                )
469            }
470            _ => return Err(Error::UnknownKeyType(kt.name.to_string())),
471        };
472
473        let comment = reader.read_string()?;
474
475        Ok(Self {
476            key_type: kt,
477            kind,
478            pubkey,
479            magic: 0x0,
480            comment,
481        })
482    }
483
484    /// Generate a new SSH private key. Currently this only supports Ed25519.
485    pub fn new(kind: KeyTypeKind, comment: &str) -> Result<Self> {
486        let rng = rand::SystemRandom::new();
487        match kind {
488            KeyTypeKind::Ed25519 => {
489                let key_type = KeyType::from_name("ssh-ed25519").unwrap();
490                // Ring does not expose access to the private key without
491                // PKCS#8 wrapping. So we either need to do this, or write/pull
492                // in code to parse it.
493                let seed: [u8; 32] = rand::generate(&rng).unwrap().expose();
494                let magic: [u8; 4] = rand::generate(&rng).unwrap().expose();
495                let public_key_bytes = Ed25519KeyPair::from_seed_unchecked(&seed)
496                    .unwrap()
497                    .public_key()
498                    .as_ref()
499                    .to_vec();
500                let key = Ed25519PrivateKey { key: seed.to_vec() };
501
502                let pubkey = PublicKey {
503                    key_type: key_type.clone(),
504                    kind: PublicKeyKind::Ed25519(Ed25519PublicKey {
505                        key: public_key_bytes,
506                        sk_application: None,
507                    }),
508                    comment: None,
509                };
510
511                Ok(Self {
512                    key_type,
513                    kind: PrivateKeyKind::Ed25519(key),
514                    pubkey,
515                    magic: u32::from_be_bytes(magic),
516                    comment: comment.to_owned(),
517                })
518            }
519            KeyTypeKind::Ecdsa => Err(Error::Unsupported),
520            KeyTypeKind::Rsa => Err(Error::Unsupported),
521            _ => Err(Error::KeyTypeMismatch),
522        }
523    }
524
525    /// Reads an OpenSSH private key from a given path and passphrase
526    pub fn from_path_with_passphrase<P: AsRef<Path>>(
527        path: P,
528        passphrase: Option<String>,
529    ) -> Result<Self> {
530        let mut contents = String::new();
531        File::open(path)?.read_to_string(&mut contents)?;
532
533        PrivateKey::from_string_with_passphrase(&contents, passphrase)
534    }
535
536    /// Reads an OpenSSH private key from a given path.
537    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
538        PrivateKey::from_path_with_passphrase(path, None)
539    }
540
541    /// Reads an OpenSSH private key from a given string and passphrase
542    pub fn from_string_with_passphrase(contents: &str, passphrase: Option<String>) -> Result<Self> {
543        let mut iter = contents.lines();
544        let header = iter.next().unwrap_or("");
545        if header != "-----BEGIN OPENSSH PRIVATE KEY-----" {
546            return Err(Error::InvalidFormat);
547        }
548
549        let mut encoded_key = String::new();
550        loop {
551            let part = match iter.next() {
552                Some(p) => p,
553                None => return Err(Error::InvalidFormat),
554            };
555
556            if part == "-----END OPENSSH PRIVATE KEY-----" {
557                break;
558            }
559            encoded_key.push_str(part);
560        }
561
562        let decoded = base64::decode(encoded_key)?;
563        let mut reader = Reader::new(&decoded);
564        // Construct a new `PrivateKey`
565        let k = PrivateKey::from_reader(&mut reader, passphrase)?;
566
567        Ok(k)
568    }
569
570    /// Reads an OpenSSH private key from a given string.
571    pub fn from_string(contents: &str) -> Result<PrivateKey> {
572        PrivateKey::from_string_with_passphrase(contents, None)
573    }
574
575    /// Create a private key from just the decrypted private bytes
576    pub fn from_bytes<T: ?Sized + AsRef<[u8]>>(buffer: &T) -> Result<PrivateKey> {
577        let mut reader = Reader::new(buffer);
578        PrivateKey::read_private_key(&mut reader)
579    }
580
581    /// This function is used for extracting a private key from an existing reader.
582    pub(crate) fn from_reader(
583        reader: &mut Reader<'_>,
584        passphrase: Option<String>,
585    ) -> Result<PrivateKey> {
586        let preamble = reader.read_cstring()?;
587
588        if preamble != "openssh-key-v1" {
589            return Err(Error::InvalidFormat);
590        }
591
592        // These values are for encrypted keys
593        let cipher_name = reader.read_string()?;
594        let kdf = reader.read_string()?;
595
596        #[allow(unused_variables)]
597        let encryption_data = reader.read_bytes()?;
598
599        // This seems to be hardcoded into the standard
600        let number_of_keys = reader.read_u32()?;
601        if number_of_keys != 1 {
602            return Err(Error::InvalidFormat);
603        }
604
605        // A full pubkey with the same format as seen in certificates
606        let pubkey = reader
607            .read_bytes()
608            .and_then(|v| PublicKey::from_bytes(&v))?;
609
610        let remaining_length = match reader.read_u32()?.try_into() {
611            Ok(rl) => rl,
612            Err(_) => return Err(Error::InvalidFormat),
613        };
614
615        #[allow(unused_mut)]
616        let mut remaining_bytes = reader.read_raw_bytes(remaining_length)?;
617
618        match (cipher_name.as_str(), kdf.as_str(), passphrase) {
619            ("none", "none", _) => (),
620            #[cfg(feature = "encrypted-keys")]
621            ("aes256-ctr", "bcrypt", Some(passphrase)) => {
622                let mut enc_reader = Reader::new(&encryption_data);
623                let salt = enc_reader.read_bytes()?;
624                let rounds = enc_reader.read_u32()?;
625                let mut output = [0; 48];
626                if bcrypt_pbkdf(passphrase.as_str(), &salt, rounds, &mut output).is_err() {
627                    return Err(Error::InvalidFormat);
628                }
629
630                let mut cipher = Aes256Ctr::new(
631                    GenericArray::from_slice(&output[..32]),
632                    GenericArray::from_slice(&output[32..]),
633                );
634
635                match cipher.try_apply_keystream(&mut remaining_bytes) {
636                    Ok(_) => (),
637                    Err(_) => return Err(Error::InvalidFormat),
638                }
639            }
640            ("aes256-ctr", "bcrypt", None) => return Err(Error::EncryptedPrivateKey),
641            _ => return Err(Error::EncryptedPrivateKeyNotSupported),
642        };
643
644        let mut reader = Reader::new(&remaining_bytes);
645
646        // These four bytes are repeated and are used to check that a key has
647        // been decrypted successfully
648        let m1 = reader.read_u32()?;
649        let m2 = reader.read_u32()?;
650
651        if m1 != m2 {
652            return Err(Error::InvalidFormat);
653        }
654
655        let mut private_key = PrivateKey::read_private_key(&mut reader)?;
656        private_key.magic = m1;
657
658        if private_key.pubkey != pubkey {
659            return Err(Error::InvalidFormat);
660        }
661
662        Ok(private_key)
663    }
664
665    /// If using an SK key, this allows you set the pin to use when communicating with the
666    /// external device
667    pub fn set_pin(&mut self, pin: &str) {
668        match &mut self.kind {
669            PrivateKeyKind::EcdsaSk(key) => {
670                key.pin = Some(pin.to_string());
671            }
672            PrivateKeyKind::Ed25519Sk(key) => {
673                key.pin = Some(pin.to_string());
674            }
675            _ => (),
676        };
677    }
678
679    /// If using an SK key, this allows you specify a hardware device path to use for the
680    /// signing operation
681    pub fn set_device_path(&mut self, path: &str) {
682        match &mut self.kind {
683            PrivateKeyKind::EcdsaSk(key) => {
684                key.device_path = Some(path.to_string());
685            }
686            PrivateKeyKind::Ed25519Sk(key) => {
687                key.device_path = Some(path.to_string());
688            }
689            _ => (),
690        };
691    }
692
693    /// Returns the touch requirement for this private key.
694    ///
695    /// Non-hardware-backed keys return `TouchRequirement::NotRequired`.
696    pub fn touch_requirement(&self) -> TouchRequirement {
697        match &self.kind {
698            PrivateKeyKind::EcdsaSk(key) => key.touch_requirement(),
699            PrivateKeyKind::Ed25519Sk(key) => key.touch_requirement(),
700            _ => TouchRequirement::NotRequired,
701        }
702    }
703
704    /// Encode the PrivateKey into a bytes representation
705    pub fn encode(&self) -> Vec<u8> {
706        let mut serializer = Writer::new();
707        serializer.write_cstring("openssh-key-v1"); // Preamble
708        serializer.write_string("none"); // cipher_namer
709        serializer.write_string("none"); // kdf
710        serializer.write_bytes(&[]); // encryption_data
711        serializer.write_u32(1); // number_of_keys
712        serializer.write_pub_key(&self.pubkey); // public key
713
714        let mut w = Writer::new();
715        w.write_u32(self.magic); // magic
716        w.write_u32(self.magic); // repeated magic
717
718        w.write_string(self.pubkey.key_type.name);
719        match &self.kind {
720            PrivateKeyKind::Rsa(rsa) => {
721                w.write_mpint(&rsa.n); // These are in fact in a diff-
722                w.write_mpint(&rsa.e); // erent order than a public key
723                w.write_mpint(&rsa.d);
724                w.write_mpint(&rsa.coefficient);
725                w.write_mpint(&rsa.p);
726                w.write_mpint(&rsa.q);
727            }
728            PrivateKeyKind::Ecdsa(ecdsa) => {
729                w.write_pub_key_data(&self.pubkey);
730                w.write_bytes(&ecdsa.key);
731            }
732            PrivateKeyKind::EcdsaSk(ecdsask) => {
733                w.write_pub_key_data(&self.pubkey);
734                w.write_raw_bytes(&[ecdsask.flags]);
735                w.write_bytes(&ecdsask.handle);
736                w.write_bytes(&[]);
737            }
738            PrivateKeyKind::Ed25519(ed25519) => {
739                w.write_pub_key_data(&self.pubkey);
740                w.write_bytes(&ed25519.key);
741            }
742            PrivateKeyKind::Ed25519Sk(ed25519sk) => {
743                w.write_pub_key_data(&self.pubkey);
744                w.write_raw_bytes(&[ed25519sk.flags]);
745                w.write_bytes(&ed25519sk.handle);
746                w.write_bytes(&[]);
747            }
748        };
749
750        w.write_string(&self.comment);
751
752        // Padding to make the length of the private key part of the file
753        // congruent to 8
754        let pad_bytes = (8 - (w.as_bytes().len() % 8)) % 8;
755        let padding = vec![0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7];
756        w.write_raw_bytes(&padding[..pad_bytes]);
757
758        serializer.write_bytes(w.as_bytes());
759        serializer.into_bytes()
760    }
761
762    /// Writes the private key to a given writer.
763    pub fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
764        let encoded = self.encode();
765        let data = base64::encode(&encoded);
766        let split = data
767            .chars()
768            .enumerate()
769            .flat_map(|(i, c)| {
770                if i != 0 && i % 70 == 0 {
771                    Some('\n')
772                } else {
773                    None
774                }
775                .into_iter()
776                .chain(std::iter::once(c))
777            })
778            .collect::<String>();
779        write!(
780            w,
781            "-----BEGIN OPENSSH PRIVATE KEY-----\n{}\n-----END OPENSSH PRIVATE KEY-----\n",
782            split
783        )
784    }
785}
786
787impl fmt::Display for PrivateKey {
788    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
789        write!(f, "{} {}", &self.pubkey.fingerprint(), self.comment)
790    }
791}
792
793impl Drop for PrivateKey {
794    fn drop(&mut self) {
795        self.kind.zeroize();
796    }
797}