diff --git a/ssh-key/src/error.rs b/ssh-key/src/error.rs index 1428db4..08d1e17 100644 --- a/ssh-key/src/error.rs +++ b/ssh-key/src/error.rs @@ -80,6 +80,12 @@ pub enum Error { /// Version number. number: u32, }, + + /// Byte array is longer than allowed. + TooLong { + /// Bad length that did not pass validation check. + bad_len: usize, + }, } impl fmt::Display for Error { @@ -111,6 +117,7 @@ impl fmt::Display for Error { "unexpected trailing data at end of message ({remaining} bytes)", ), Error::Version { number: version } => write!(f, "version unsupported: {version}"), + Error::TooLong { bad_len } => write!(f, "too long byte array: {bad_len}"), } } } diff --git a/ssh-key/src/private/sk.rs b/ssh-key/src/private/sk.rs index 41427b5..9986173 100644 --- a/ssh-key/src/private/sk.rs +++ b/ssh-key/src/private/sk.rs @@ -26,6 +26,29 @@ pub struct SkEcdsaSha2NistP256 { #[cfg(feature = "ecdsa")] impl SkEcdsaSha2NistP256 { + /// Construct new instance of SkEcdsaSha2NistP256. + #[cfg(feature = "alloc")] + pub fn new( + public: public::SkEcdsaSha2NistP256, + flags: u8, + key_handle: impl Into>, + ) -> Result { + let key_handle = key_handle.into(); + + if key_handle.len() <= 255 { + Ok(SkEcdsaSha2NistP256 { + public, + flags, + key_handle, + reserved: Vec::::new(), + }) + } else { + Err(Error::TooLong { + bad_len: key_handle.len(), + }) + } + } + /// Get the ECDSA/NIST P-256 public key. pub fn public(&self) -> &public::SkEcdsaSha2NistP256 { &self.public @@ -96,6 +119,29 @@ pub struct SkEd25519 { } impl SkEd25519 { + /// Construct new instance of SkEd25519. + #[cfg(feature = "alloc")] + pub fn new( + public: public::SkEd25519, + flags: u8, + key_handle: impl Into>, + ) -> Result { + let key_handle = key_handle.into(); + + if key_handle.len() <= 255 { + Ok(SkEd25519 { + public, + flags, + key_handle, + reserved: Vec::::new(), + }) + } else { + Err(Error::TooLong { + bad_len: key_handle.len(), + }) + } + } + /// Get the Ed25519 public key. pub fn public(&self) -> &public::SkEd25519 { &self.public diff --git a/ssh-key/src/public/sk.rs b/ssh-key/src/public/sk.rs index 03ff599..5f33758 100644 --- a/ssh-key/src/public/sk.rs +++ b/ssh-key/src/public/sk.rs @@ -30,6 +30,15 @@ pub struct SkEcdsaSha2NistP256 { #[cfg(feature = "ecdsa")] impl SkEcdsaSha2NistP256 { + /// Construct new instance of SkEcdsaSha2NistP256. + #[cfg(feature = "alloc")] + pub fn new(ec_point: EcdsaNistP256PublicKey, application: impl Into) -> Self { + SkEcdsaSha2NistP256 { + ec_point, + application: application.into(), + } + } + /// Get the elliptic curve point for this Security Key. pub fn ec_point(&self) -> &EcdsaNistP256PublicKey { &self.ec_point @@ -123,6 +132,15 @@ pub struct SkEd25519 { } impl SkEd25519 { + /// Construct new instance of SkEd25519. + #[cfg(feature = "alloc")] + pub fn new(public_key: Ed25519PublicKey, application: impl Into) -> Self { + SkEd25519 { + public_key, + application: application.into(), + } + } + /// Get the Ed25519 private key for this security key. pub fn public_key(&self) -> &Ed25519PublicKey { &self.public_key diff --git a/ssh-key/tests/public_key.rs b/ssh-key/tests/public_key.rs index ff0d528..f7621b3 100644 --- a/ssh-key/tests/public_key.rs +++ b/ssh-key/tests/public_key.rs @@ -1,8 +1,12 @@ //! SSH public key tests. use hex_literal::hex; +#[cfg(feature = "alloc")] +use ssh_key::public::{Ed25519PublicKey, SkEd25519}; use ssh_key::{Algorithm, PublicKey}; use std::collections::HashSet; +#[cfg(all(feature = "ecdsa", feature = "alloc"))] +use {sec1::consts::U32, ssh_key::public::SkEcdsaSha2NistP256}; #[cfg(feature = "ecdsa")] use ssh_key::EcdsaCurve; @@ -296,6 +300,25 @@ fn decode_sk_ecdsa_p256_openssh() { ); } +#[cfg(all(feature = "ecdsa", feature = "alloc"))] +#[test] +fn new_sk_ecdsa_p256() { + const EXAMPLE_EC_POINT: [u8; 65] = [ + 0x04, 0x81, 0x0b, 0x40, 0x9d, 0x83, 0x82, 0xf6, 0x97, 0xd7, 0x24, 0x25, 0x28, 0x5a, 0x24, + 0x7d, 0x63, 0x36, 0xb2, 0xeb, 0x9a, 0x08, 0x52, 0x36, 0xaa, 0x9d, 0x1e, 0x26, 0x87, 0x47, + 0xca, 0x0e, 0x8e, 0xe2, 0x27, 0xf1, 0x73, 0x75, 0xe9, 0x44, 0xa7, 0x75, 0x39, 0x2f, 0x1d, + 0x35, 0x84, 0x2d, 0x13, 0xf6, 0x23, 0x75, 0x74, 0xab, 0x03, 0xe0, 0x0e, 0x9c, 0xc1, 0x79, + 0x9e, 0xcd, 0x8d, 0x93, 0x1e, + ]; + + let ec_point = sec1::EncodedPoint::::from_bytes(&EXAMPLE_EC_POINT).unwrap(); + let sk_key = SkEcdsaSha2NistP256::new(ec_point, "ssh:".to_string()); + let key = PublicKey::from_openssh(OPENSSH_SK_ECDSA_P256_EXAMPLE).unwrap(); + + let ecdsa_key = key.key_data().sk_ecdsa_p256().unwrap(); + assert_eq!(&sk_key, ecdsa_key); +} + #[test] fn decode_sk_ed25519_openssh() { let key = PublicKey::from_openssh(OPENSSH_SK_ED25519_EXAMPLE).unwrap(); @@ -318,6 +341,24 @@ fn decode_sk_ed25519_openssh() { ); } +#[cfg(feature = "alloc")] +#[test] +fn new_sk_ed25519_openssh() { + const EXAMPLE_PUBKEY: Ed25519PublicKey = Ed25519PublicKey { + 0: [ + 0x21, 0x68, 0xfe, 0x4e, 0x4b, 0x53, 0xcf, 0x3a, 0xde, 0xee, 0xba, 0x60, 0x2f, 0x5e, + 0x50, 0xed, 0xb5, 0xef, 0x44, 0x1d, 0xba, 0x88, 0x4f, 0x51, 0x19, 0x10, 0x9d, 0xb2, + 0xda, 0xfd, 0xd7, 0x33, + ], + }; + + let sk_key = SkEd25519::new(EXAMPLE_PUBKEY, "ssh:".to_string()); + let key = PublicKey::from_openssh(OPENSSH_SK_ED25519_EXAMPLE).unwrap(); + + let ed25519_key = key.key_data().sk_ed25519().unwrap(); + assert_eq!(&sk_key, ed25519_key); +} + #[cfg(all(feature = "alloc"))] #[test] fn decode_custom_algorithm_openssh() {