You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ssh-key: fix ssh-dss signature encoding on 64-bit targets
Since switching from `BigUint::to_bytes_be()` to `BoxedUint::to_be_bytes()`
in 07f295e, `Signer<Signature> for DsaKeypair::try_sign` produces a
48-byte blob instead of the 40 bytes required by RFC 4253 §6.6, panicking
the debug-assert at `signature.rs:345` and breaking ssh-dss publickey
client auth in release builds.
Root cause: `dsa::Signature::r()`/`s()` are constructed at
`KeySize::n_aligned() == n.div_ceil(Limb::BITS) * Limb::BITS` precision.
For ssh-dss (n = 160) on 64-bit targets that's 192 bits, so
`to_be_bytes()` returns 24 bytes per component.
Trim the limb-alignment leading zeros down to `DSA_SIGNATURE_SIZE / 2`
before left-padding so values shorter than 160 bits still get padded.
Also bump dsa to 0.7.0-rc.14 to pick up the verify-side overflow fix
(`v1.concatenating_mul(&v2)` in `verifying_key.rs`); without it,
`DsaPublicKey::verify` panics with "attempted to multiply with overflow"
on 1024-bit p.
Reworked the existing `try_sign_and_verify_dsa` test so it asserts on the
encoded `signature.data.len() == DSA_SIGNATURE_SIZE` rather than on the
underlying `dsa` component byte lengths -- the old assertion (`r.len() ==
20`) couldn't hold on 64-bit given the alignment above.
Refs Eugeny/russh#705
Copy file name to clipboardExpand all lines: ssh-key/Cargo.toml
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -28,7 +28,7 @@ crypto-bigint = { version = "0.7.0-rc.28", features = ["alloc"] }
28
28
argon2 = { version = "0.5", optional = true, default-features = false, features = ["alloc"] }
29
29
bcrypt-pbkdf = { version = "0.10", optional = true, default-features = false, features = ["alloc"] }
30
30
bigint = { package = "num-bigint-dig", version = "0.8.4", optional = true, default-features = false, features = ["std"] }
31
-
dsa = { version = "0.7.0-rc.13", optional = true, default-features = false, features = ["hazmat"] }
31
+
dsa = { version = "0.7.0-rc.14", optional = true, default-features = false, features = ["hazmat"] }
32
32
ecdsa = { version = "0.17.0-rc.16", optional = true, default-features = false } # only need this explicit dependency due to a minimal-versions fail @ https://github.com/RustCrypto/elliptic-curves/issues/1038
Copy file name to clipboardExpand all lines: ssh-key/src/signature.rs
+16-51Lines changed: 16 additions & 51 deletions
Original file line number
Diff line number
Diff line change
@@ -333,13 +333,19 @@ impl Signer<Signature> for DsaKeypair {
333
333
.map_err(|_| signature::Error::new())?;
334
334
335
335
// Encode the format specified in RFC4253 section 6.6: two raw 80-bit integers concatenated
336
-
letmut data = Vec::new();
336
+
letmut data = Vec::with_capacity(DSA_SIGNATURE_SIZE);
337
+
let half = DSA_SIGNATURE_SIZE / 2;
337
338
338
339
for component in[signature.r(), signature.s()]{
339
-
letmut bytes = component.to_be_bytes();
340
-
let pad_len = (DSA_SIGNATURE_SIZE / 2).saturating_sub(bytes.len());
341
-
data.extend(iter::repeat(0).take(pad_len));
342
-
data.extend(&bytes);
340
+
// `BoxedUint::to_be_bytes()` is sized to `bits_precision`, which
341
+
// `dsa` rounds up to a multiple of `Limb::BITS` (24 bytes on 64-bit
342
+
// for n = 160). Since `dsa` enforces `r < q` and `s < q`, any bytes
343
+
// above the trailing `half` are zero and can be sliced off.
344
+
let bytes = component.to_be_bytes();
345
+
let bytes = &bytes[bytes.len().saturating_sub(half)..];
346
+
let pad_len = half.saturating_sub(bytes.len());
347
+
data.resize(data.len() + pad_len,0);
348
+
data.extend_from_slice(bytes);
343
349
}
344
350
345
351
debug_assert_eq!(data.len(),DSA_SIGNATURE_SIZE);
@@ -887,65 +893,24 @@ mod tests {
887
893
use encoding::Decodeas _;
888
894
use signature::{Signeras _,Verifieras _};
889
895
890
-
fncheck_signature_component_lens(
891
-
keypair:&DsaKeypair,
892
-
data:&[u8],
893
-
r_len:usize,
894
-
s_len:usize,
895
-
){
896
-
use sha1::{Digestas _,Sha1};
897
-
use signature::DigestSigneras _;
898
-
899
-
let signature = dsa::SigningKey::try_from(keypair)
900
-
.expect("valid DSA signing key")
901
-
.try_sign_digest(|d:&mutSha1| Ok(d.update(data)))
902
-
.expect("valid DSA signature");
903
-
904
-
let r = signature.r().to_be_bytes();
905
-
assert_eq!(
906
-
r.len(),
907
-
r_len,
908
-
"dsa signature component `r` has len {} != {}",
909
-
r.len(),
910
-
r_len
911
-
);
912
-
let s = signature.s().to_be_bytes();
913
-
assert_eq!(
914
-
s.len(),
915
-
s_len,
916
-
"dsa signature component `s` has len {} != {}",
917
-
s.len(),
918
-
s_len
919
-
);
920
-
}
921
-
922
896
let keypair = hex!("0000008100c161fb30c9e4e3602c8510f93bbd48d813da845dfcc75f3696e440cd019d609809608cd592b8430db901d7b43740740045b547c60fb035d69f9c64d3dfbfb13bb3edd8ccfdd44705739a639eb70f4aed16b0b8355de1b21cd9d442eff250895573a8af7ce2fb71fb062e887482dab5c68139845fb8afafc5f3819dc782920d510000001500f3fb6762430332bd5950edc5cd1ae6f17b88514f0000008061ef1394d864905e8efec3b610b7288a6522893af2a475f910796e0de47c8b065d365e942e80e471d1e6d4abdee1d3d3ede7103c6996432f1a9f9a671a31388672d63555077911fc69e641a997087260d22cdbf4965aa64bb382204f88987890ec225a5a7723a977dc1ecc5e04cf678f994692b20470adbf697489f800817b920000008100a9a6f1b65fc724d65df7441908b34af66489a4a3872cbbba25ea1bcfc83f25c4af1a62e339eefc814907cfaf0cb6d2d16996212a32a27a63013f01c57d0630f0be16c8c69d16fc25438e613b904b98aeb3e7c356fa8e75ee1d474c9f82f1280c5a6c18e9e607fcf7586eefb75ea9399da893b807375ac1396fd586bf277161980000001500ced95f1c7bbb39be4987837ad1f71be31bb7b0d9");
923
897
let keypair = DsaKeypair::decode(&mut&keypair[..]).expect("properly encoded DSA keypair");
924
898
925
899
let data = hex!("F0000040713d5f6fffe0000e6421ab0b3a69774d3da02fd72b107d6b32b6dad7c1660bbf507bf3eac3304cc5058f7e6f81b04239b8471459b1f3b387e2626f7eb8f6bcdd3200000006626c616465320000000e7373682d636f6e6e656374696f6e00000009686f73746261736564000000077373682d647373000001b2000000077373682d6473730000008100c161fb30c9e4e3602c8510f93bbd48d813da845dfcc75f3696e440cd019d609809608cd592b8430db901d7b43740740045b547c60fb035d69f9c64d3dfbfb13bb3edd8ccfdd44705739a639eb70f4aed16b0b8355de1b21cd9d442eff250895573a8af7ce2fb71fb062e887482dab5c68139845fb8afafc5f3819dc782920d510000001500f3fb6762430332bd5950edc5cd1ae6f17b88514f0000008061ef1394d864905e8efec3b610b7288a6522893af2a475f910796e0de47c8b065d365e942e80e471d1e6d4abdee1d3d3ede7103c6996432f1a9f9a671a31388672d63555077911fc69e641a997087260d22cdbf4965aa64bb382204f88987890ec225a5a7723a977dc1ecc5e04cf678f994692b20470adbf697489f800817b920000008100a9a6f1b65fc724d65df7441908b34af66489a4a3872cbbba25ea1bcfc83f25c4af1a62e339eefc814907cfaf0cb6d2d16996212a32a27a63013f01c57d0630f0be16c8c69d16fc25438e613b904b98aeb3e7c356fa8e75ee1d474c9f82f1280c5a6c18e9e607fcf7586eefb75ea9399da893b807375ac1396fd586bf2771619800000015746f6d61746f7373682e6c6f63616c646f6d61696e00000009746f6d61746f737368");
926
-
check_signature_component_lens(
927
-
&keypair,
928
-
&data,
929
-
DSA_SIGNATURE_SIZE / 2,
930
-
DSA_SIGNATURE_SIZE / 2,
931
-
);
932
900
let signature = keypair.try_sign(&data[..]).expect("dsa try_sign is ok");
// This input produces an `r` shorter than 160 bits, exercising the
908
+
// left-pad path for naturally short signature components.
938
909
let data = hex!("00000040713d5f6fffe0000e6421ab0b3a69774d3da02fd72b107d6b32b6dad7c1660bbf507bf3eac3304cc5058f7e6f81b04239b8471459b1f3b387e2626f7eb8f6bcdd3200000006626c616465320000000e7373682d636f6e6e656374696f6e00000009686f73746261736564000000077373682d647373000001b2000000077373682d6473730000008100c161fb30c9e4e3602c8510f93bbd48d813da845dfcc75f3696e440cd019d609809608cd592b8430db901d7b43740740045b547c60fb035d69f9c64d3dfbfb13bb3edd8ccfdd44705739a639eb70f4aed16b0b8355de1b21cd9d442eff250895573a8af7ce2fb71fb062e887482dab5c68139845fb8afafc5f3819dc782920d510000001500f3fb6762430332bd5950edc5cd1ae6f17b88514f0000008061ef1394d864905e8efec3b610b7288a6522893af2a475f910796e0de47c8b065d365e942e80e471d1e6d4abdee1d3d3ede7103c6996432f1a9f9a671a31388672d63555077911fc69e641a997087260d22cdbf4965aa64bb382204f88987890ec225a5a7723a977dc1ecc5e04cf678f994692b20470adbf697489f800817b920000008100a9a6f1b65fc724d65df7441908b34af66489a4a3872cbbba25ea1bcfc83f25c4af1a62e339eefc814907cfaf0cb6d2d16996212a32a27a63013f01c57d0630f0be16c8c69d16fc25438e613b904b98aeb3e7c356fa8e75ee1d474c9f82f1280c5a6c18e9e607fcf7586eefb75ea9399da893b807375ac1396fd586bf2771619800000015746f6d61746f7373682e6c6f63616c646f6d61696e00000009746f6d61746f737368");
939
-
// verify that this data produces signature with `r` integer component that is less than 160 bits/20 bytes.
0 commit comments