Skip to content

Commit

Permalink
ssh-key: Sk* constructors (#201)
Browse files Browse the repository at this point in the history
Adds `SkEcdsaSha2NistP256::new()` and `SkEd25519::new()`
  • Loading branch information
jviki authored Mar 9, 2024
1 parent 8c6e81c commit 4d52699
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 0 deletions.
7 changes: 7 additions & 0 deletions ssh-key/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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}"),
}
}
}
Expand Down
46 changes: 46 additions & 0 deletions ssh-key/src/private/sk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>>,
) -> Result<Self> {
let key_handle = key_handle.into();

if key_handle.len() <= 255 {
Ok(SkEcdsaSha2NistP256 {
public,
flags,
key_handle,
reserved: Vec::<u8>::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
Expand Down Expand Up @@ -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<Vec<u8>>,
) -> Result<Self> {
let key_handle = key_handle.into();

if key_handle.len() <= 255 {
Ok(SkEd25519 {
public,
flags,
key_handle,
reserved: Vec::<u8>::new(),
})
} else {
Err(Error::TooLong {
bad_len: key_handle.len(),
})
}
}

/// Get the Ed25519 public key.
pub fn public(&self) -> &public::SkEd25519 {
&self.public
Expand Down
18 changes: 18 additions & 0 deletions ssh-key/src/public/sk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>) -> 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
Expand Down Expand Up @@ -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<String>) -> 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
Expand Down
41 changes: 41 additions & 0 deletions ssh-key/tests/public_key.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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::<U32>::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();
Expand All @@ -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() {
Expand Down

0 comments on commit 4d52699

Please sign in to comment.