Skip to content

Commit 722b1f0

Browse files
committed
k256: use ecdsa/dev test macros + API support
Converts ECDSA tests to use the `new_signing_test` and `new_verification_test` macros added to the new `dev` feature of the `ecdsa` crate in RustCrypto/signatures#103. The macros use a few new APIs which were also added in this PR, namely `AffinePoint::{from_compressed_point, from_uncompressed_point}` which provide a more direct conversion path from SEC-1 encodings to `AffinePoint` which skip the `PublicKey` interface (which is more of a high-level, user-facing API)
1 parent 061a1a2 commit 722b1f0

File tree

4 files changed

+68
-114
lines changed

4 files changed

+68
-114
lines changed

Cargo.lock

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

k256/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ keywords = ["bitcoin", "crypto", "ecc", "ethereum", "secp256k1"]
1313

1414
[dependencies]
1515
cfg-if = "0.1"
16-
ecdsa = { version = "= 0.7.0-pre", optional = true, default-features = false }
16+
ecdsa = { version = "= 0.7.0-pre", optional = true, default-features = false, features = ["hazmat"] }
1717
elliptic-curve = { version = "= 0.5.0-pre", default-features = false, features = ["weierstrass"] }
1818
sha2 = { version = "0.9", optional = true }
1919
zeroize = { version = "1", optional = true, default-features = false }
2020

2121
[dev-dependencies]
22+
ecdsa = { version = "= 0.7.0-pre", default-features = false, features = ["dev", "hazmat"] }
2223
hex = "0.4" # TODO: switch to hex-literal
2324
hex-literal = "0.2"
2425
proptest = "0.10"
@@ -34,7 +35,7 @@ endomorphism-mul = []
3435
field-montgomery = []
3536
force-32-bit = []
3637
rand = ["elliptic-curve/rand_core"]
37-
sha256 = ["digest", "ecdsa/hazmat", "sha2"]
38+
sha256 = ["digest", "sha2"]
3839
test-vectors = []
3940
std = ["elliptic-curve/std"]
4041

k256/src/arithmetic.rs

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -98,53 +98,57 @@ impl AffinePoint {
9898
}
9999
}
100100

101-
/// Attempts to parse the given [`PublicKey`] as an SEC-1-encoded `AffinePoint`.
101+
/// Attempts to parse the given [`CompressedPoint`] as a SEC-1 encoded [`AffinePoint`]
102+
pub fn from_compressed_point(point: &CompressedPoint) -> CtOption<Self> {
103+
let bytes = point.as_bytes();
104+
let y_is_odd = Choice::from(bytes[0] & 0x01);
105+
let x = FieldElement::from_bytes(bytes[1..33].try_into().unwrap());
106+
107+
x.and_then(|x| {
108+
let alpha = (x * &x * &x) + &CURVE_EQUATION_B;
109+
let beta = alpha.sqrt();
110+
111+
beta.map(|beta| {
112+
let y = FieldElement::conditional_select(
113+
&beta.negate(1),
114+
&beta,
115+
// beta.is_odd() == y_is_odd
116+
!(beta.normalize().is_odd() ^ y_is_odd),
117+
);
118+
119+
AffinePoint {
120+
x,
121+
y: y.normalize(),
122+
}
123+
})
124+
})
125+
}
126+
127+
/// Attempts to parse the given [`UncompressedPoint`] as a SEC-1 encoded [`AffinePoint`]
128+
pub fn from_uncompressed_point(point: &UncompressedPoint) -> CtOption<Self> {
129+
let bytes = point.as_bytes();
130+
let x = FieldElement::from_bytes(bytes[1..33].try_into().unwrap());
131+
let y = FieldElement::from_bytes(bytes[33..65].try_into().unwrap());
132+
133+
x.and_then(|x| {
134+
y.and_then(|y| {
135+
// Check that the point is on the curve
136+
let lhs = (y * &y).negate(1);
137+
let rhs = x * &x * &x + &CURVE_EQUATION_B;
138+
CtOption::new(AffinePoint { x, y }, (lhs + &rhs).normalizes_to_zero())
139+
})
140+
})
141+
}
142+
143+
/// Attempts to parse the given [`PublicKey`] as an SEC-1-encoded [`AffinePoint`].
102144
///
103145
/// # Returns
104146
///
105147
/// `None` value if `pubkey` is not on the secp256k1 curve.
106148
pub fn from_pubkey(pubkey: &PublicKey) -> CtOption<Self> {
107149
match pubkey {
108-
PublicKey::Compressed(point) => {
109-
let bytes = point.as_bytes();
110-
111-
let y_is_odd = Choice::from(bytes[0] & 0x01);
112-
let x = FieldElement::from_bytes(bytes[1..33].try_into().unwrap());
113-
114-
x.and_then(|x| {
115-
let alpha = (x * &x * &x) + &CURVE_EQUATION_B;
116-
let beta = alpha.sqrt();
117-
118-
beta.map(|beta| {
119-
let y = FieldElement::conditional_select(
120-
&beta.negate(1),
121-
&beta,
122-
// beta.is_odd() == y_is_odd
123-
!(beta.normalize().is_odd() ^ y_is_odd),
124-
);
125-
126-
AffinePoint {
127-
x,
128-
y: y.normalize(),
129-
}
130-
})
131-
})
132-
}
133-
PublicKey::Uncompressed(point) => {
134-
let bytes = point.as_bytes();
135-
136-
let x = FieldElement::from_bytes(bytes[1..33].try_into().unwrap());
137-
let y = FieldElement::from_bytes(bytes[33..65].try_into().unwrap());
138-
139-
x.and_then(|x| {
140-
y.and_then(|y| {
141-
// Check that the point is on the curve
142-
let lhs = (y * &y).negate(1);
143-
let rhs = x * &x * &x + &CURVE_EQUATION_B;
144-
CtOption::new(AffinePoint { x, y }, (lhs + &rhs).normalizes_to_zero())
145-
})
146-
})
147-
}
150+
PublicKey::Compressed(point) => Self::from_compressed_point(point),
151+
PublicKey::Uncompressed(point) => Self::from_uncompressed_point(point),
148152
}
149153
}
150154

k256/src/ecdsa.rs

Lines changed: 11 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -133,73 +133,22 @@ pub fn normalize_s(signature: &Signature) -> Result<Signature, Error> {
133133
#[cfg(all(test, feature = "arithmetic"))]
134134
mod tests {
135135
use super::*;
136-
use crate::PublicKey;
137-
use ecdsa::signature::Signature as _;
136+
use ecdsa::{dev::TestVector, signature::Signature as _};
138137
use hex_literal::hex;
139138

140-
/// ECDSA test vector
141-
struct EcdsaVector {
142-
d: [u8; 32],
143-
k: [u8; 32],
144-
m: [u8; 32],
145-
r: [u8; 32],
146-
s: [u8; 32],
147-
q: [u8; 65],
148-
}
149-
150139
/// ECDSA test vectors
151-
const ECDSA_VECTORS: &[EcdsaVector] = &[EcdsaVector {
152-
d: hex!("ebb2c082fd7727890a28ac82f6bdf97bad8de9f5d7c9028692de1a255cad3e0f"),
153-
k: hex!("49a0d7b786ec9cde0d0721d72804befd06571c974b191efb42ecf322ba9ddd9a"),
154-
m: hex!("4b688df40bcedbe641ddb16ff0a1842d9c67ea1c3bf63f3e0471baa664531d1a"),
155-
r: hex!("241097efbf8b63bf145c8961dbdf10c310efbb3b2676bbc0f8b08505c9e2f795"),
156-
s: hex!("021006b7838609339e8b415a7f9acb1b661828131aef1ecbc7955dfb01f3ca0e"),
157-
q: hex!(
158-
"04779dd197a5df977ed2cf6cb31d82d43328b790dc6b3b7d4437a427bd5847df
159-
cde94b724a555b6d017bb7607c3e3281daf5b1699d6ef4124975c9237b917d426f"
160-
),
140+
const TEST_VECTORS: &[TestVector] = &[TestVector {
141+
d: &hex!("ebb2c082fd7727890a28ac82f6bdf97bad8de9f5d7c9028692de1a255cad3e0f"),
142+
q_x: &hex!("779dd197a5df977ed2cf6cb31d82d43328b790dc6b3b7d4437a427bd5847dfcd"),
143+
q_y: &hex!("e94b724a555b6d017bb7607c3e3281daf5b1699d6ef4124975c9237b917d426f"),
144+
k: &hex!("49a0d7b786ec9cde0d0721d72804befd06571c974b191efb42ecf322ba9ddd9a"),
145+
m: &hex!("4b688df40bcedbe641ddb16ff0a1842d9c67ea1c3bf63f3e0471baa664531d1a"),
146+
r: &hex!("241097efbf8b63bf145c8961dbdf10c310efbb3b2676bbc0f8b08505c9e2f795"),
147+
s: &hex!("021006b7838609339e8b415a7f9acb1b661828131aef1ecbc7955dfb01f3ca0e"),
161148
}];
162149

163-
#[test]
164-
fn ecdsa_signing() {
165-
for vector in ECDSA_VECTORS {
166-
let d = Scalar::from_bytes(&vector.d).unwrap();
167-
let k = Scalar::from_bytes(&vector.k).unwrap();
168-
let sig = d.try_sign_prehashed(&k, None, &vector.m.into()).unwrap();
169-
170-
assert_eq!(vector.r, sig.r().as_ref());
171-
assert_eq!(vector.s, sig.s().as_ref());
172-
}
173-
}
174-
175-
#[test]
176-
fn ecdsa_verify_success() {
177-
for vector in ECDSA_VECTORS {
178-
let pk = PublicKey::from_bytes(&vector.q[..]).unwrap();
179-
let q = AffinePoint::from_pubkey(&pk).unwrap();
180-
let sig = Signature::from_scalars(&vector.r.into(), &vector.s.into());
181-
let result = q.verify_prehashed(&vector.m.into(), &sig);
182-
183-
assert!(result.is_ok());
184-
}
185-
}
186-
187-
#[test]
188-
fn ecdsa_verify_failure() {
189-
for vector in ECDSA_VECTORS {
190-
let pk = PublicKey::from_bytes(&vector.q[..]).unwrap();
191-
let q = AffinePoint::from_pubkey(&pk).unwrap();
192-
193-
// Flip a bit in `s`
194-
let mut s_tweaked = vector.s;
195-
s_tweaked[0] ^= 1;
196-
197-
let sig = Signature::from_scalars(&vector.r.into(), &s_tweaked.into());
198-
let result = q.verify_prehashed(&vector.m.into(), &sig);
199-
200-
assert!(result.is_err());
201-
}
202-
}
150+
ecdsa::new_signing_test!(TEST_VECTORS);
151+
ecdsa::new_verification_test!(TEST_VECTORS);
203152

204153
// Test vectors generated using rust-secp256k1
205154
#[test]

0 commit comments

Comments
 (0)