1#![cfg_attr(all(feature = "encryption", feature = "std"), doc = " ```")]
14#![cfg_attr(not(all(feature = "encryption", feature = "std")), doc = " ```ignore")]
15#![cfg_attr(
50 all(
51 feature = "ed25519",
52 feature = "encryption",
53 feature = "getrandom",
54 feature = "std"
55 ),
56 doc = " ```"
57)]
58#![cfg_attr(
59 not(all(
60 feature = "ed25519",
61 feature = "encryption",
62 feature = "getrandom",
63 feature = "std"
64 )),
65 doc = " ```ignore"
66)]
67#![cfg_attr(
92 all(feature = "ed25519", feature = "getrandom", feature = "std"),
93 doc = " ```"
94)]
95#![cfg_attr(
96 not(all(feature = "ed25519", feature = "getrandom", feature = "std")),
97 doc = " ```ignore"
98)]
99#[cfg(feature = "alloc")]
108mod dsa;
109#[cfg(feature = "ecdsa")]
110mod ecdsa;
111mod ed25519;
112mod keypair;
113#[cfg(feature = "alloc")]
114mod opaque;
115#[cfg(feature = "alloc")]
116mod rsa;
117#[cfg(feature = "alloc")]
118mod sk;
119
120pub use self::{
121 ed25519::{Ed25519Keypair, Ed25519PrivateKey},
122 keypair::KeypairData,
123};
124
125#[cfg(feature = "alloc")]
126pub use crate::{
127 private::{
128 dsa::{DsaKeypair, DsaPrivateKey},
129 opaque::{OpaqueKeypair, OpaqueKeypairBytes, OpaquePrivateKeyBytes},
130 rsa::{RsaKeypair, RsaPrivateKey},
131 sk::SkEd25519,
132 },
133 SshSig,
134};
135
136#[cfg(feature = "ecdsa")]
137pub use self::ecdsa::{EcdsaKeypair, EcdsaPrivateKey};
138
139#[cfg(all(feature = "alloc", feature = "ecdsa"))]
140pub use self::sk::SkEcdsaSha2NistP256;
141
142use crate::{public, Algorithm, Cipher, Error, Fingerprint, HashAlg, Kdf, PublicKey, Result};
143use cipher::Tag;
144use core::str;
145use encoding::{
146 pem::{LineEnding, PemLabel},
147 CheckedSum, Decode, DecodePem, Encode, EncodePem, Reader, Writer,
148};
149use subtle::{Choice, ConstantTimeEq};
150
151#[cfg(feature = "alloc")]
152use {
153 alloc::{string::String, vec::Vec},
154 zeroize::Zeroizing,
155};
156
157#[cfg(feature = "rand_core")]
158use rand_core::CryptoRngCore;
159
160#[cfg(feature = "std")]
161use std::{fs, path::Path};
162
163#[cfg(all(unix, feature = "std"))]
164use std::{io::Write, os::unix::fs::OpenOptionsExt};
165
166const CONVERSION_ERROR_MSG: &str = "SSH private key conversion error";
168
169#[cfg(all(feature = "rand_core", feature = "rsa"))]
171const DEFAULT_RSA_KEY_SIZE: usize = 4096;
172
173const MAX_BLOCK_SIZE: usize = 16;
177
178const PADDING_BYTES: [u8; MAX_BLOCK_SIZE - 1] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
180
181#[cfg(all(unix, feature = "std"))]
183const UNIX_FILE_PERMISSIONS: u32 = 0o600;
184
185#[derive(Clone, Debug)]
187pub struct PrivateKey {
188 cipher: Cipher,
190
191 kdf: Kdf,
193
194 checkint: Option<u32>,
196
197 public_key: PublicKey,
199
200 key_data: KeypairData,
202
203 auth_tag: Option<Tag>,
205}
206
207impl PrivateKey {
208 const AUTH_MAGIC: &'static [u8] = b"openssh-key-v1\0";
210
211 #[cfg(feature = "alloc")]
215 pub fn new(key_data: KeypairData, comment: impl Into<String>) -> Result<Self> {
216 if key_data.is_encrypted() {
217 return Err(Error::Encrypted);
218 }
219
220 let mut private_key = Self::try_from(key_data)?;
221 private_key.public_key.comment = comment.into();
222 Ok(private_key)
223 }
224
225 pub fn from_openssh(pem: impl AsRef<[u8]>) -> Result<Self> {
233 Self::decode_pem(pem)
234 }
235
236 pub fn from_bytes(mut bytes: &[u8]) -> Result<Self> {
238 let reader = &mut bytes;
239 let private_key = Self::decode(reader)?;
240 Ok(reader.finish(private_key)?)
241 }
242
243 pub fn encode_openssh<'o>(
245 &self,
246 line_ending: LineEnding,
247 out: &'o mut [u8],
248 ) -> Result<&'o str> {
249 Ok(self.encode_pem(line_ending, out)?)
250 }
251
252 #[cfg(feature = "alloc")]
255 pub fn to_openssh(&self, line_ending: LineEnding) -> Result<Zeroizing<String>> {
256 Ok(self.encode_pem_string(line_ending).map(Zeroizing::new)?)
257 }
258
259 #[cfg(feature = "alloc")]
261 pub fn to_bytes(&self) -> Result<Zeroizing<Vec<u8>>> {
262 let mut private_key_bytes = Vec::with_capacity(self.encoded_len()?);
263 self.encode(&mut private_key_bytes)?;
264 Ok(Zeroizing::new(private_key_bytes))
265 }
266
267 #[cfg_attr(feature = "ed25519", doc = "```")]
283 #[cfg_attr(not(feature = "ed25519"), doc = "```ignore")]
284 #[cfg(feature = "alloc")]
314 pub fn sign(&self, namespace: &str, hash_alg: HashAlg, msg: &[u8]) -> Result<SshSig> {
315 SshSig::sign(self, namespace, hash_alg, msg)
316 }
317
318 #[cfg(feature = "std")]
320 pub fn read_openssh_file(path: &Path) -> Result<Self> {
321 let pem = Zeroizing::new(fs::read_to_string(path)?);
323 Self::from_openssh(&*pem)
324 }
325
326 #[cfg(feature = "std")]
328 pub fn write_openssh_file(&self, path: &Path, line_ending: LineEnding) -> Result<()> {
329 let pem = self.to_openssh(line_ending)?;
330
331 #[cfg(not(unix))]
332 fs::write(path, pem.as_bytes())?;
333 #[cfg(unix)]
334 fs::OpenOptions::new()
335 .create(true)
336 .write(true)
337 .truncate(true)
338 .mode(UNIX_FILE_PERMISSIONS)
339 .open(path)
340 .and_then(|mut file| file.write_all(pem.as_bytes()))?;
341
342 Ok(())
343 }
344
345 #[cfg(feature = "encryption")]
350 pub fn decrypt(&self, password: impl AsRef<[u8]>) -> Result<Self> {
351 let (key, iv) = self.kdf.derive_key_and_iv(self.cipher, password)?;
352
353 let ciphertext = self.key_data.encrypted().ok_or(Error::Decrypted)?;
354 let mut buffer = Zeroizing::new(ciphertext.to_vec());
355 self.cipher.decrypt(&key, &iv, &mut buffer, self.auth_tag)?;
356
357 Self::decode_privatekey_comment_pair(
358 &mut &**buffer,
359 self.public_key.key_data.clone(),
360 self.cipher.block_size(),
361 )
362 }
363
364 #[cfg(feature = "encryption")]
373 pub fn encrypt(
374 &self,
375 rng: &mut impl CryptoRngCore,
376 password: impl AsRef<[u8]>,
377 ) -> Result<Self> {
378 self.encrypt_with_cipher(rng, Cipher::Aes256Ctr, password)
379 }
380
381 #[cfg(feature = "encryption")]
386 pub fn encrypt_with_cipher(
387 &self,
388 rng: &mut impl CryptoRngCore,
389 cipher: Cipher,
390 password: impl AsRef<[u8]>,
391 ) -> Result<Self> {
392 let checkint = rng.next_u32();
393
394 self.encrypt_with(
395 cipher,
396 Kdf::new(Default::default(), rng)?,
397 checkint,
398 password,
399 )
400 }
401
402 #[cfg(feature = "encryption")]
407 pub fn encrypt_with(
408 &self,
409 cipher: Cipher,
410 kdf: Kdf,
411 checkint: u32,
412 password: impl AsRef<[u8]>,
413 ) -> Result<Self> {
414 if self.is_encrypted() {
415 return Err(Error::Encrypted);
416 }
417
418 let (key_bytes, iv_bytes) = kdf.derive_key_and_iv(cipher, password)?;
419 let msg_len = self.encoded_privatekey_comment_pair_len(cipher)?;
420 let mut out = Vec::with_capacity(msg_len);
421
422 self.encode_privatekey_comment_pair(&mut out, cipher, checkint)?;
424 let auth_tag = cipher.encrypt(&key_bytes, &iv_bytes, out.as_mut_slice())?;
425
426 Ok(Self {
427 cipher,
428 kdf,
429 checkint: None,
430 public_key: self.public_key.key_data.clone().into(),
431 key_data: KeypairData::Encrypted(out),
432 auth_tag,
433 })
434 }
435
436 pub fn algorithm(&self) -> Algorithm {
438 self.public_key.algorithm()
439 }
440
441 pub fn comment(&self) -> &str {
443 self.public_key.comment()
444 }
445
446 pub fn cipher(&self) -> Cipher {
448 self.cipher
449 }
450
451 pub fn fingerprint(&self, hash_alg: HashAlg) -> Fingerprint {
455 self.public_key.fingerprint(hash_alg)
456 }
457
458 pub fn is_encrypted(&self) -> bool {
460 let ret = self.key_data.is_encrypted();
461 debug_assert_eq!(ret, self.cipher.is_some());
462 ret
463 }
464
465 pub fn kdf(&self) -> &Kdf {
469 &self.kdf
470 }
471
472 pub fn key_data(&self) -> &KeypairData {
474 &self.key_data
475 }
476
477 pub fn public_key(&self) -> &PublicKey {
479 &self.public_key
480 }
481
482 #[cfg(feature = "rand_core")]
487 #[allow(unreachable_code, unused_variables)]
488 pub fn random(rng: &mut impl CryptoRngCore, algorithm: Algorithm) -> Result<Self> {
489 let checkint = rng.next_u32();
490 let key_data = match algorithm {
491 #[cfg(feature = "dsa")]
492 Algorithm::Dsa => KeypairData::from(DsaKeypair::random(rng)?),
493 #[cfg(any(feature = "p256", feature = "p384", feature = "p521"))]
494 Algorithm::Ecdsa { curve } => KeypairData::from(EcdsaKeypair::random(rng, curve)?),
495 #[cfg(feature = "ed25519")]
496 Algorithm::Ed25519 => KeypairData::from(Ed25519Keypair::random(rng)),
497 #[cfg(feature = "rsa")]
498 Algorithm::Rsa { .. } => {
499 KeypairData::from(RsaKeypair::random(rng, DEFAULT_RSA_KEY_SIZE)?)
500 }
501 _ => return Err(Error::AlgorithmUnknown),
502 };
503 let public_key = public::KeyData::try_from(&key_data)?;
504
505 Ok(Self {
506 cipher: Cipher::None,
507 kdf: Kdf::None,
508 checkint: Some(checkint),
509 public_key: public_key.into(),
510 key_data,
511 auth_tag: None,
512 })
513 }
514
515 #[cfg(feature = "alloc")]
517 pub fn set_comment(&mut self, comment: impl Into<String>) {
518 self.public_key.set_comment(comment);
519 }
520
521 fn decode_privatekey_comment_pair(
548 reader: &mut impl Reader,
549 public_key: public::KeyData,
550 block_size: usize,
551 ) -> Result<Self> {
552 debug_assert!(block_size <= MAX_BLOCK_SIZE);
553
554 if reader.remaining_len().checked_rem(block_size) != Some(0) {
556 return Err(encoding::Error::Length.into());
557 }
558
559 let checkint1 = u32::decode(reader)?;
560 let checkint2 = u32::decode(reader)?;
561
562 if checkint1 != checkint2 {
563 return Err(Error::Crypto);
564 }
565
566 let key_data = KeypairData::decode(reader)?;
567
568 if public_key != public::KeyData::try_from(&key_data)? {
570 return Err(Error::PublicKey);
571 }
572
573 let mut public_key = PublicKey::from(public_key);
574 public_key.decode_comment(reader)?;
575
576 let padding_len = reader.remaining_len();
577
578 if padding_len >= block_size {
579 return Err(encoding::Error::Length.into());
580 }
581
582 if padding_len != 0 {
583 let mut padding = [0u8; MAX_BLOCK_SIZE];
584 reader.read(&mut padding[..padding_len])?;
585
586 if PADDING_BYTES[..padding_len] != padding[..padding_len] {
587 return Err(Error::FormatEncoding);
588 }
589 }
590
591 if !reader.is_finished() {
592 return Err(Error::TrailingData {
593 remaining: reader.remaining_len(),
594 });
595 }
596
597 Ok(Self {
598 cipher: Cipher::None,
599 kdf: Kdf::None,
600 checkint: Some(checkint1),
601 public_key,
602 key_data,
603 auth_tag: None,
604 })
605 }
606
607 fn encode_privatekey_comment_pair(
610 &self,
611 writer: &mut impl Writer,
612 cipher: Cipher,
613 checkint: u32,
614 ) -> encoding::Result<()> {
615 let unpadded_len = self.unpadded_privatekey_comment_pair_len()?;
616 let padding_len = cipher.padding_len(unpadded_len);
617
618 checkint.encode(writer)?;
619 checkint.encode(writer)?;
620 self.key_data.encode(writer)?;
621 self.comment().encode(writer)?;
622 writer.write(&PADDING_BYTES[..padding_len])?;
623 Ok(())
624 }
625
626 fn encoded_privatekey_comment_pair_len(&self, cipher: Cipher) -> encoding::Result<usize> {
629 let len = self.unpadded_privatekey_comment_pair_len()?;
630 [len, cipher.padding_len(len)].checked_sum()
631 }
632
633 fn unpadded_privatekey_comment_pair_len(&self) -> encoding::Result<usize> {
638 debug_assert!(!self.is_encrypted(), "called on encrypted key");
640
641 [
642 8, self.key_data.encoded_len()?,
644 self.comment().encoded_len()?,
645 ]
646 .checked_sum()
647 }
648}
649
650impl ConstantTimeEq for PrivateKey {
651 fn ct_eq(&self, other: &Self) -> Choice {
652 self.key_data.ct_eq(&other.key_data)
654 & Choice::from(
655 (self.cipher == other.cipher
656 && self.kdf == other.kdf
657 && self.public_key == other.public_key) as u8,
658 )
659 }
660}
661
662impl Eq for PrivateKey {}
663
664impl PartialEq for PrivateKey {
665 fn eq(&self, other: &Self) -> bool {
666 self.ct_eq(other).into()
667 }
668}
669
670impl Decode for PrivateKey {
671 type Error = Error;
672
673 fn decode(reader: &mut impl Reader) -> Result<Self> {
674 let mut auth_magic = [0u8; Self::AUTH_MAGIC.len()];
675 reader.read(&mut auth_magic)?;
676
677 if auth_magic != Self::AUTH_MAGIC {
678 return Err(Error::FormatEncoding);
679 }
680
681 let cipher = Cipher::decode(reader)?;
682 let kdf = Kdf::decode(reader)?;
683 let nkeys = usize::decode(reader)?;
684
685 if nkeys != 1 {
687 return Err(encoding::Error::Length.into());
688 }
689
690 let public_key = reader.read_prefixed(public::KeyData::decode)?;
691
692 #[cfg(not(feature = "alloc"))]
694 if cipher.is_some() {
695 return Err(Error::Encrypted);
696 }
697 #[cfg(feature = "alloc")]
698 if cipher.is_some() {
699 let ciphertext = Vec::decode(reader)?;
700
701 if ciphertext.len().checked_rem(cipher.block_size()) != Some(0) {
703 return Err(Error::Crypto);
704 }
705
706 let auth_tag = if cipher.has_tag() {
707 let mut tag = Tag::default();
708 reader.read(&mut tag)?;
709 Some(tag)
710 } else {
711 None
712 };
713
714 if !reader.is_finished() {
715 return Err(Error::TrailingData {
716 remaining: reader.remaining_len(),
717 });
718 }
719
720 return Ok(Self {
721 cipher,
722 kdf,
723 checkint: None,
724 public_key: public_key.into(),
725 key_data: KeypairData::Encrypted(ciphertext),
726 auth_tag,
727 });
728 }
729
730 if kdf.is_some() {
732 return Err(Error::Crypto);
733 }
734
735 reader.read_prefixed(|reader| {
736 Self::decode_privatekey_comment_pair(reader, public_key, cipher.block_size())
737 })
738 }
739}
740
741impl Encode for PrivateKey {
742 fn encoded_len(&self) -> encoding::Result<usize> {
743 let private_key_len = if self.is_encrypted() {
744 self.key_data.encoded_len_prefixed()?
745 } else {
746 [4, self.encoded_privatekey_comment_pair_len(Cipher::None)?].checked_sum()?
747 };
748
749 [
750 Self::AUTH_MAGIC.len(),
751 self.cipher.encoded_len()?,
752 self.kdf.encoded_len()?,
753 4, self.public_key.key_data().encoded_len_prefixed()?,
755 private_key_len,
756 self.auth_tag.map(|tag| tag.len()).unwrap_or(0),
757 ]
758 .checked_sum()
759 }
760
761 fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
762 writer.write(Self::AUTH_MAGIC)?;
763 self.cipher.encode(writer)?;
764 self.kdf.encode(writer)?;
765
766 1usize.encode(writer)?;
768
769 self.public_key.key_data().encode_prefixed(writer)?;
771
772 if self.is_encrypted() {
774 self.key_data.encode_prefixed(writer)?;
775
776 if let Some(tag) = &self.auth_tag {
777 writer.write(tag)?;
778 }
779 } else {
780 self.encoded_privatekey_comment_pair_len(Cipher::None)?
781 .encode(writer)?;
782
783 let checkint = self.checkint.unwrap_or_else(|| self.key_data.checkint());
784 self.encode_privatekey_comment_pair(writer, Cipher::None, checkint)?;
785 }
786
787 Ok(())
788 }
789}
790
791impl From<PrivateKey> for PublicKey {
792 fn from(private_key: PrivateKey) -> PublicKey {
793 private_key.public_key
794 }
795}
796
797impl From<&PrivateKey> for PublicKey {
798 fn from(private_key: &PrivateKey) -> PublicKey {
799 private_key.public_key.clone()
800 }
801}
802
803impl From<PrivateKey> for public::KeyData {
804 fn from(private_key: PrivateKey) -> public::KeyData {
805 private_key.public_key.key_data
806 }
807}
808
809impl From<&PrivateKey> for public::KeyData {
810 fn from(private_key: &PrivateKey) -> public::KeyData {
811 private_key.public_key.key_data.clone()
812 }
813}
814
815#[cfg(feature = "alloc")]
816impl From<DsaKeypair> for PrivateKey {
817 fn from(keypair: DsaKeypair) -> PrivateKey {
818 KeypairData::from(keypair)
819 .try_into()
820 .expect(CONVERSION_ERROR_MSG)
821 }
822}
823
824#[cfg(feature = "ecdsa")]
825impl From<EcdsaKeypair> for PrivateKey {
826 fn from(keypair: EcdsaKeypair) -> PrivateKey {
827 KeypairData::from(keypair)
828 .try_into()
829 .expect(CONVERSION_ERROR_MSG)
830 }
831}
832
833impl From<Ed25519Keypair> for PrivateKey {
834 fn from(keypair: Ed25519Keypair) -> PrivateKey {
835 KeypairData::from(keypair)
836 .try_into()
837 .expect(CONVERSION_ERROR_MSG)
838 }
839}
840
841#[cfg(feature = "alloc")]
842impl From<RsaKeypair> for PrivateKey {
843 fn from(keypair: RsaKeypair) -> PrivateKey {
844 KeypairData::from(keypair)
845 .try_into()
846 .expect(CONVERSION_ERROR_MSG)
847 }
848}
849
850#[cfg(all(feature = "alloc", feature = "ecdsa"))]
851impl From<SkEcdsaSha2NistP256> for PrivateKey {
852 fn from(keypair: SkEcdsaSha2NistP256) -> PrivateKey {
853 KeypairData::from(keypair)
854 .try_into()
855 .expect(CONVERSION_ERROR_MSG)
856 }
857}
858
859#[cfg(feature = "alloc")]
860impl From<SkEd25519> for PrivateKey {
861 fn from(keypair: SkEd25519) -> PrivateKey {
862 KeypairData::from(keypair)
863 .try_into()
864 .expect(CONVERSION_ERROR_MSG)
865 }
866}
867
868impl TryFrom<KeypairData> for PrivateKey {
869 type Error = Error;
870
871 fn try_from(key_data: KeypairData) -> Result<PrivateKey> {
872 let public_key = public::KeyData::try_from(&key_data)?;
873
874 Ok(Self {
875 cipher: Cipher::None,
876 kdf: Kdf::None,
877 checkint: None,
878 public_key: public_key.into(),
879 key_data,
880 auth_tag: None,
881 })
882 }
883}
884
885impl PemLabel for PrivateKey {
886 const PEM_LABEL: &'static str = "OPENSSH PRIVATE KEY";
887}
888
889impl str::FromStr for PrivateKey {
890 type Err = Error;
891
892 fn from_str(s: &str) -> Result<Self> {
893 Self::from_openssh(s)
894 }
895}