Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 62 additions & 17 deletions ssh-key/src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use crate::{private::Ed25519Keypair, public::Ed25519PublicKey};
use {
crate::{private::DsaKeypair, public::DsaPublicKey},
bigint::BigUint,
core::iter,
sha1::Sha1,
signature::{DigestSigner, DigestVerifier},
};
Expand All @@ -24,6 +23,9 @@ use crate::{
public::EcdsaPublicKey,
};

#[cfg(any(feature = "dsa", feature = "p256", feature = "p384", feature = "p521"))]
use core::iter;

#[cfg(feature = "rsa")]
use {
crate::{private::RsaKeypair, public::RsaPublicKey, HashAlg},
Expand Down Expand Up @@ -151,7 +153,7 @@ fn ecdsa_sig_size(mut data: &[u8], curve: EcdsaCurve, sk_trailer: bool) -> Resul
for _ in 0..2 {
let component = Mpint::decode(reader)?;
let bytes = component.as_positive_bytes().ok_or(Error::FormatEncoding)?;
if bytes.len() != curve.field_size() {
if bytes.len() > curve.field_size() {
return Err(encoding::Error::Length.into());
}
}
Expand Down Expand Up @@ -517,6 +519,17 @@ impl_signature_for_curve!(p256, "p256", NistP256, 32);
impl_signature_for_curve!(p384, "p384", NistP384, 48);
impl_signature_for_curve!(p521, "p521", NistP521, 66);

/// Build a generic sized object from a `u8` iterator, with leading zero padding
#[cfg(any(feature = "p256", feature = "p384", feature = "p521"))]
fn zero_pad_field_bytes<B: FromIterator<u8> + Copy>(m: Mpint) -> Option<B> {
use core::mem::size_of;

let bytes = m.as_positive_bytes()?;
size_of::<B>()
.checked_sub(bytes.len())
.map(|i| B::from_iter(iter::repeat(0u8).take(i).chain(bytes.iter().cloned())))
}

#[cfg(feature = "p256")]
impl TryFrom<&Signature> for p256::ecdsa::Signature {
type Error = Error;
Expand All @@ -536,11 +549,11 @@ fn p256_signature_from_openssh_bytes(mut signature_bytes: &[u8]) -> Result<p256:
let r = Mpint::decode(reader)?;
let s = Mpint::decode(reader)?;

match (r.as_positive_bytes(), s.as_positive_bytes()) {
(Some(r), Some(s)) => Ok(p256::ecdsa::Signature::from_scalars(
p256::FieldBytes::try_from(r).map_err(|_| Error::Crypto)?,
p256::FieldBytes::try_from(s).map_err(|_| Error::Crypto)?,
)?),
match (
zero_pad_field_bytes::<p256::FieldBytes>(r),
zero_pad_field_bytes::<p256::FieldBytes>(s),
) {
(Some(r), Some(s)) => Ok(p256::ecdsa::Signature::from_scalars(r, s)?),
_ => Err(Error::Crypto),
}
}
Expand All @@ -558,11 +571,11 @@ impl TryFrom<&Signature> for p384::ecdsa::Signature {
let r = Mpint::decode(reader)?;
let s = Mpint::decode(reader)?;

match (r.as_positive_bytes(), s.as_positive_bytes()) {
(Some(r), Some(s)) => Ok(p384::ecdsa::Signature::from_scalars(
p384::FieldBytes::try_from(r).map_err(|_| Error::Crypto)?,
p384::FieldBytes::try_from(s).map_err(|_| Error::Crypto)?,
)?),
match (
zero_pad_field_bytes::<p384::FieldBytes>(r),
zero_pad_field_bytes::<p384::FieldBytes>(s),
) {
(Some(r), Some(s)) => Ok(p384::ecdsa::Signature::from_scalars(r, s)?),
_ => Err(Error::Crypto),
}
}
Expand All @@ -584,11 +597,11 @@ impl TryFrom<&Signature> for p521::ecdsa::Signature {
let r = Mpint::decode(reader)?;
let s = Mpint::decode(reader)?;

match (r.as_positive_bytes(), s.as_positive_bytes()) {
(Some(r), Some(s)) => Ok(p521::ecdsa::Signature::from_scalars(
p521::FieldBytes::try_from(r).map_err(|_| Error::Crypto)?,
p521::FieldBytes::try_from(s).map_err(|_| Error::Crypto)?,
)?),
match (
zero_pad_field_bytes::<p521::FieldBytes>(r),
zero_pad_field_bytes::<p521::FieldBytes>(s),
) {
(Some(r), Some(s)) => Ok(p521::ecdsa::Signature::from_scalars(r, s)?),
_ => Err(Error::Crypto),
}
}
Expand Down Expand Up @@ -699,6 +712,9 @@ mod tests {
signature::{Signer, Verifier},
};

#[cfg(feature = "p256")]
use super::{zero_pad_field_bytes, Mpint};

const DSA_SIGNATURE: &[u8] = &hex!("000000077373682d6473730000002866725bf3c56100e975e21fff28a60f73717534d285ea3e1beefc2891f7189d00bd4d94627e84c55c");
const ECDSA_SHA2_P256_SIGNATURE: &[u8] = &hex!("0000001365636473612d736861322d6e6973747032353600000048000000201298ab320720a32139cda8a40c97a13dc54ce032ea3c6f09ea9e87501e48fa1d0000002046e4ac697a6424a9870b9ef04ca1182cd741965f989bd1f1f4a26fd83cf70348");
const ED25519_SIGNATURE: &[u8] = &hex!("0000000b7373682d65643235353139000000403d6b9906b76875aef1e7b2f1e02078a94f439aebb9a4734da1a851a81e22ce0199bbf820387a8de9c834c9c3cc778d9972dcbe70f68d53cc6bc9e26b02b46d04");
Expand All @@ -716,6 +732,35 @@ mod tests {
let _ssh_signature = Signature::try_from(p256_signature).unwrap();
}

#[cfg(feature = "p256")]
#[test]
fn zero_pad_field_bytes_p256() {
let i = Mpint::from_bytes(&hex!(
"1122334455667788112233445566778811223344556677881122334455667788"
))
.unwrap();
let fb = zero_pad_field_bytes::<p256::FieldBytes>(i);
assert!(fb.is_some());

// too long
let i = Mpint::from_bytes(&hex!(
"991122334455667788112233445566778811223344556677881122334455667788"
))
.unwrap();
let fb = zero_pad_field_bytes::<p256::FieldBytes>(i);
assert!(fb.is_none());

// short is okay
let i = Mpint::from_bytes(&hex!(
"22334455667788112233445566778811223344556677881122334455667788"
))
.unwrap();
let fb = zero_pad_field_bytes::<p256::FieldBytes>(i)
.expect("failed to build FieldBytes from short hex string");
assert_eq!(fb[0], 0x00);
assert_eq!(fb[1], 0x22);
}

#[test]
fn decode_dsa() {
let signature = Signature::try_from(DSA_SIGNATURE).unwrap();
Expand Down