UHAEQPZUODJ5YVBZJPPJVLO7EBW6DC2JXHQBN26ARELAVULG3JUQC
LDEEJH5L2QGDPOAASR6FZ7NYHZF56ZPUYMXVCKXCRGTICVCJU4EAC
TAOFQAIISOBF3NJ5BG72NRG5JSTIJN4X2QZXFPB75US6WTFSK7DQC
7FRJYUI62VW257VVFQXND6OKSAILVTHGEJCXFE6CG6FIOIUTDVYAC
MCS77Y4VJGB6TU2HOLASGSRW4B6MT74XABD4KYALIRS54GGN2DDQC
2WEO7OZLWJJPUYK4WXLT5FD46G2MAEIHEYMDW5GASCBUNKOPXCVAC
ORSEEVB5ZBACWJH7J3BPQOLZVNNHBQU3A24VNQXI4TU4SUEVQ2JQC
WXZWQLGLNQ3QRHMBH7YPTHKSPUST4HD34H3PW5E4LHQYH3KLNHYAC
NHOSLQGG4CIWBE7VKL5MB7PSY3RZ5IVDFENMGZG6X755GGZ6B3VQC
KGIUIQYIBBUEGBOQIFYJJNZGGSPV3KERBPYTKKCOBKZZ5CHIJ24AC
-----END OPENSSH PRIVATE KEY-----";
#[cfg(feature = "p256")]
const P256_KEY: &'static str = "-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABAh
krrfr9PH+vNievobev0EAAAAEAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAy
NTYAAAAIbmlzdHAyNTYAAABBBHtLt3JSpJSWyg7Tvx7LNuWEjX8lFJDupLh14RwW
zy5TaiVMM4iF+gZlzM12r6mVwWdQq9fTcN/QhElyR4m34oIAAACwe18misSLWNBp
kWsNwQD9+wZx6w4YHykv/hA1v4B1iavk176z4JwtzwnS0sDSW+ME2HEpO7uJMlAc
1ZItX9W+dRdFjPkKYibMCCcylOIUrjqWkhFnFOpHrTsiyuIwBFwSrfis8r2AX5xb
EsDf+t3612/JMMTJMUvqsK9hxR6SrT73CS2W4/UbAQ1O0b7eDShH9zMyiMC4lYec
Ru6GNGRltIpY3hqCB6di+lzu6iPrMEE=
#[cfg(feature = "p256")]
pub const PKCS8_P256: &'static str = "-----BEGIN ENCRYPTED PRIVATE KEY-----
MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAh155uUbhbyFQICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEL4PGMOq8KM573vkRzh8GSMEgZBt
6UPxhwgoiSqXdlfe/1hTzVKFjpFQNe5UUXxPHlw1a7+24sPzHwqY39tVWlbNO4QR
0bwebnTG8/pMR2X/3Hch2d/okVlfpeqYTyiBcbW1nXAJtK+eorZ80P2UCHD/X7sj
iLhibttN17vvg7HZPqf9wQm+Mef97nmP13oEFU3s856haS3T0OVVh+d+dpLuntM=
-----END ENCRYPTED PRIVATE KEY-----";
#[cfg(feature = "p256")]
fn test_client_agent_p256() {
let key = decode_secret_key(PKCS8_P256, Some("blabla")).unwrap();
test_client_agent(key)
}
#[test]
#[cfg(feature = "p256")]
fn test_client_agent_openssh_p256() {
let key = decode_secret_key(P256_KEY, Some("blabla")).unwrap();
test_client_agent(key)
}
#[test]
b"ecdsa-sha2-nistp256" if cfg!(feature = "p256") => {
#[cfg(feature = "p256")]
{
let mut p = pubkey.reader(0);
let key_algo = p.read_string()?;
let identifier = p.read_string()?;
if key_algo != b"ecdsa-sha2-nistp256" || identifier != b"nistp256" {
debug!("Rejecting {identifier:?} for not being nistp256");
return Err(Error::CouldNotReadKey.into());
}
let sec1_bytes = p.read_string()?;
return p256::PublicKey::from_sec1_bytes(sec1_bytes)
.map(PublicKey::P256)
.map_err(|e| {
debug!("from_sec1_bytes failure: {e:?}");
Error::CouldNotReadKey
});
}
#[cfg(not(feature = "p256"))]
unreachable!();
}
}
#[cfg(feature = "p256")]
&PublicKey::P256(ref public) => {
let mut reader = sig.reader(0);
let mut concat = [0u8; 64];
// Ad-hoc reading of mpints:
// - We only consider the last 32 bytes => leading 0 is still treated fine
// - We implicitly pad missing leading bytes as 0, which is also fine for a modular value
match reader.read_mpint() {
Ok(r) => r.into_iter().rev().enumerate().for_each(|(i, b)| {
if i < 32 {
concat[31 - i] = *b;
}
}),
Err(_) => {
debug!("Malformed scalar: r");
return false;
}
};
match reader.read_mpint() {
Ok(s) => s.into_iter().rev().enumerate().for_each(|(i, b)| {
if i < 32 {
concat[63 - i] = *b;
}
}),
Err(_) => {
debug!("Malformed scalar: s");
return false;
}
};
p256::ecdsa::Signature::from_slice(&concat)
.and_then(|sig| {
p256::ecdsa::VerifyingKey::from(public)
.verify(buffer, &sig)
.map(|()| true)
})
.unwrap_or(false)
#[cfg(feature = "p256")]
&KeyPair::P256(ref secret) => {
let sig = p256::ecdsa::SigningKey::from(secret)
.sign_recoverable(to_sign)?
.0;
let mut bytes = vec![];
bytes.extend_ssh_mpint(&sig.r().as_ref().to_bytes());
bytes.extend_ssh_mpint(&sig.s().as_ref().to_bytes());
Ok(Signature::P256(bytes))
}
}
#[cfg(feature = "p256")]
&KeyPair::P256(ref secret) => {
let sig = p256::ecdsa::SigningKey::from(secret)
.sign_recoverable(to_sign.as_ref())?
.0
.to_bytes();
buffer.push_u32_be((ECDSA_SHA2_NISTP256.0.len() + sig.len() + 8) as u32);
buffer.extend_ssh_string(ECDSA_SHA2_NISTP256.0.as_bytes());
buffer.extend_ssh_string(&sig);
#[cfg(feature = "p256")]
&KeyPair::P256(ref secret) => {
let sig = p256::ecdsa::SigningKey::from(secret)
.sign_recoverable(&buffer)?
.0
.to_bytes();
buffer.push_u32_be((ECDSA_SHA2_NISTP256.0.len() + sig.len() + 8) as u32);
buffer.extend_ssh_string(ECDSA_SHA2_NISTP256.0.as_bytes());
buffer.extend_ssh_string(&sig);
}
if oid.components().as_slice() == RSA {
let seq = &reader.next().read_bytes()?;
let rsa: Result<Rsa<Private>, Error> = yasna::parse_der(seq, |reader| {
reader.read_sequence(|reader| {
let version = reader.next().read_u32()?;
if version != 0 {
return Ok(Err(Error::CouldNotReadKey.into()));
}
use openssl::bn::BigNum;
let mut read_key = || -> Result<Rsa<Private>, Error> {
Ok(Rsa::from_private_components(
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
)?)
};
Ok(read_key())
if oid.components().as_slice() == RSA && cfg!(feature = "openssl") {
#[cfg(feature = "openssl")]
{
let seq = &reader.next().read_bytes()?;
let rsa: Result<Rsa<Private>, Error> = yasna::parse_der(seq, |reader| {
reader.read_sequence(|reader| {
let version = reader.next().read_u32()?;
if version != 0 {
return Ok(Err(Error::CouldNotReadKey.into()));
}
use openssl::bn::BigNum;
let mut read_key = || -> Result<Rsa<Private>, Error> {
Ok(Rsa::from_private_components(
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
BigNum::from_slice(&reader.next().read_biguint()?.to_bytes_be())?,
)?)
};
Ok(read_key())
})
})?;
Ok(key::KeyPair::RSA {
key: rsa?,
hash: SignatureHash::SHA2_256,
})?;
Ok(key::KeyPair::RSA {
key: rsa?,
hash: SignatureHash::SHA2_256,
})
}
#[cfg(not(feature = "openssl"))]
unreachable!()
} else if oid.components().as_slice() == P256 && cfg!(feature = "p256") {
#[cfg(feature = "p256")]
{
let bytes = &reader.next().read_bytes()?;
Ok(key::KeyPair::P256(p256::SecretKey::from_sec1_der(bytes)?))
}
#[cfg(not(feature = "p256"))]
unreachable!()
});
}
} else if key_type == KEYTYPE_P256 && cfg!(feature = "p256") {
#[cfg(feature = "p256")]
{
let _nistp256 = position.read_string()?;
let pub_bytes = position.read_string()?;
let sec_bytes = position.read_mpint()?;
let _comment = position.read_string()?;
let mut key_bytes = [0u8; 32];
// Reading mpints in a naive way, but sufficient for modular values here
sec_bytes.into_iter().rev().enumerate().for_each(|(i, b)| {
if i < 32 {
key_bytes[31 - i] = *b;
}
#[cfg(feature = "p256")]
b"ecdsa-sha2-nistp256" => {
let public_ = r.read_string()?;
let pos1 = r.position;
let priv_ = r.read_mpint()?;
let _comment = r.read_string()?;
let mut priv_bytes = [0u8; 32];
priv_.into_iter().rev().enumerate().for_each(|(i, b)| {
if i < 32 {
priv_bytes[31 - i] = *b;
}
});
let key =
crate::key::KeyPair::P256(p256::SecretKey::from_bytes(&priv_bytes.into())?);
if public_ != crate::PublicKeyBase64::public_key_bytes(&key) {
return Ok(false);
}
(self.buf[pos0..pos1].to_vec(), key)
}
self.buf.extend_ssh_string(b"");
}
#[cfg(feature = "p256")]
key::KeyPair::P256(ref key) => {
self.buf.extend_ssh_string(b"ecdsa-sha2-nistp256");
self.buf.extend_ssh_string(b"nistp256");
let bytes = key.public_key().to_sec1_bytes();
self.buf.extend_ssh_string(&bytes);
self.buf.extend_ssh_mpint(&key.to_bytes());
#[cfg(feature = "p256")]
PublicKey::P256(ref key) => {
buf.extend(&[0, 0, 0, 0]);
let len0 = buf.len();
buf.extend_ssh_string(b"ecdsa-sha2-nistp256");
let mut inner_buf = vec![];
inner_buf.extend_ssh_string(b"nistp256");
inner_buf.extend_ssh_string(&key.to_sec1_bytes());
buf.extend(&inner_buf);
let len1 = buf.len();
BigEndian::write_u32(&mut buf[5..], (len1 - len0) as u32);
}
#[cfg(feature = "openssl")]
#[cfg(all(feature = "openssl", feature = "p256"))]
pub const DEFAULT: Preferred = Preferred {
kex: &[kex::CURVE25519],
key: &[
key::ED25519,
key::ECDSA_SHA2_NISTP256,
key::RSA_SHA2_256,
key::RSA_SHA2_512,
],
cipher: &[cipher::chacha20poly1305::NAME],
mac: &["none"],
compression: &["none", "zlib", "zlib@openssh.com"],
};
#[cfg(all(not(feature = "openssl"), feature = "p256"))]
pub const DEFAULT: Preferred = Preferred {
kex: &[kex::CURVE25519],
key: &[key::ED25519, key::ECDSA_SHA2_NISTP256],
cipher: &[cipher::chacha20poly1305::NAME],
mac: &["none"],
compression: &["none", "zlib", "zlib@openssh.com"],
};
#[cfg(all(feature = "openssl", not(feature = "p256")))]