Commit cd4865d88c

Naoki MATSUMOTO <33079554+naoki9911@users.noreply.github.com>
2022-10-26 13:18:06
std.crypto.sign.ecdsa: accepts unusual parameters like EcdsaP384Sha256 (#13302)
This commit accepts unusual parameters like EcdsaP384Sha256. Some certifictes(below certs are in /etc/ssl/certs/ca-certificates.crt on Ubuntu 22.04) use EcdsaP384Sha256 to sign itself. - Subject: C=GR, L=Athens, O=Hellenic Academic and Research Institutions Cert. Authority, CN=Hellenic Academic and Research Institutions ECC RootCA 2015 - Subject: C=US, ST=Texas, L=Houston, O=SSL Corporation, CN=SSL.com EV Root Certification Authority ECC - Subject: C=US, ST=Texas, L=Houston, O=SSL Corporation, CN=SSL.com Root Certification Authority ECC In verify(), hash array `h` is allocated to be larger than the scalar.encoded_length. The array is regarded as big-endian. Hash values are filled in the back of the array and the rest bytes in front are filled with zero. In sign(), the hash array is allocated and filled as same as verify(). In deterministicScalar(), hash bytes are insufficient to generate `k` To generate `k` without narrowing its value range, this commit uses algorithm stage h. in "Section 3.2 Generation of k" in RFC6979.
1 parent 22b71b1
Changed files (1)
lib
std
crypto
lib/std/crypto/ecdsa.zig
@@ -92,10 +92,11 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
                 const s = try Curve.scalar.Scalar.fromBytes(self.s, .Big);
                 if (r.isZero() or s.isZero()) return error.IdentityElement;
 
-                var h: [Hash.digest_length]u8 = undefined;
-                Hash.hash(msg, &h, .{});
-
                 const ht = Curve.scalar.encoded_length;
+                const h_len = @max(Hash.digest_length, ht);
+                var h: [h_len]u8 = [_]u8{0} ** h_len;
+                Hash.hash(msg, h[h_len - Hash.digest_length .. h_len], .{});
+
                 const z = reduceToScalar(ht, h[0..ht].*);
                 if (z.isZero()) {
                     return error.SignatureVerificationFailed;
@@ -228,14 +229,16 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
             pub fn sign(key_pair: KeyPair, msg: []const u8, noise: ?[noise_length]u8) (IdentityElementError || NonCanonicalError)!Signature {
                 const secret_key = key_pair.secret_key;
 
-                var h: [Hash.digest_length]u8 = undefined;
-                Hash.hash(msg, &h, .{});
-
                 const scalar_encoded_length = Curve.scalar.encoded_length;
+                const h_len = @max(Hash.digest_length, scalar_encoded_length);
+                var h: [h_len]u8 = [_]u8{0} ** h_len;
+                var h_slice = h[h_len - Hash.digest_length .. h_len];
+                Hash.hash(msg, h_slice, .{});
+
                 std.debug.assert(h.len >= scalar_encoded_length);
                 const z = reduceToScalar(scalar_encoded_length, h[0..scalar_encoded_length].*);
 
-                const k = deterministicScalar(h, secret_key.bytes, noise);
+                const k = deterministicScalar(h_slice.*, secret_key.bytes, noise);
 
                 const p = try Curve.basePoint.mul(k.toBytes(.Big), .Big);
                 const xs = p.affineCoordinates().x.toBytes(.Big);
@@ -268,6 +271,7 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
         fn deterministicScalar(h: [Hash.digest_length]u8, secret_key: Curve.scalar.CompressedScalar, noise: ?[noise_length]u8) Curve.scalar.Scalar {
             var k = [_]u8{0x00} ** h.len;
             var m = [_]u8{0x00} ** (h.len + 1 + noise_length + secret_key.len + h.len);
+            var t = [_]u8{0x00} ** Curve.scalar.encoded_length;
             const m_v = m[0..h.len];
             const m_i = &m[m_v.len];
             const m_z = m[m_v.len + 1 ..][0..noise_length];
@@ -286,8 +290,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
             Hmac.create(&k, &m, &k);
             Hmac.create(m_v, m_v, &k);
             while (true) {
-                Hmac.create(m_v, m_v, &k);
-                if (Curve.scalar.Scalar.fromBytes(m_v[0..Curve.scalar.encoded_length].*, .Big)) |s| return s else |_| {}
+                var t_off: usize = 0;
+                while (t_off < t.len) : (t_off += m_v.len) {
+                    const t_end = @min(t_off + m_v.len, t.len);
+                    Hmac.create(m_v, m_v, &k);
+                    std.mem.copy(u8, t[t_off..t_end], m_v[0 .. t_end - t_off]);
+                }
+                if (Curve.scalar.Scalar.fromBytes(t, .Big)) |s| return s else |_| {}
                 mem.copy(u8, m_v, m_v);
                 m_i.* = 0x00;
                 Hmac.create(&k, m[0 .. m_v.len + 1], &k);
@@ -325,6 +334,55 @@ test "ECDSA - Basic operations over Secp256k1" {
     try sig2.verify(msg, kp.public_key);
 }
 
+test "ECDSA - Basic operations over EcdsaP384Sha256" {
+    const Scheme = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha256);
+    const kp = try Scheme.KeyPair.create(null);
+    const msg = "test";
+
+    var noise: [Scheme.noise_length]u8 = undefined;
+    crypto.random.bytes(&noise);
+    const sig = try kp.sign(msg, noise);
+    try sig.verify(msg, kp.public_key);
+
+    const sig2 = try kp.sign(msg, null);
+    try sig2.verify(msg, kp.public_key);
+}
+
+test "ECDSA - Verifying a existing signature with EcdsaP384Sha256" {
+    const Scheme = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha256);
+    // zig fmt: off
+    const sk_bytes = [_]u8{
+    0x6a, 0x53, 0x9c, 0x83, 0x0f, 0x06, 0x86, 0xd9, 0xef, 0xf1, 0xe7, 0x5c, 0xae,
+    0x93, 0xd9, 0x5b, 0x16, 0x1e, 0x96, 0x7c, 0xb0, 0x86, 0x35, 0xc9, 0xea, 0x20,
+    0xdc, 0x2b, 0x02, 0x37, 0x6d, 0xd2, 0x89, 0x72, 0x0a, 0x37, 0xf6, 0x5d, 0x4f,
+    0x4d, 0xf7, 0x97, 0xcb, 0x8b, 0x03, 0x63, 0xc3, 0x2d
+    };
+    const msg = [_]u8{
+    0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x67, 0x6e,
+    0x69, 0x6e, 0x67, 0x0a
+    };
+    const sig_ans_bytes = [_]u8{
+    0x30, 0x64, 0x02, 0x30, 0x7a, 0x31, 0xd8, 0xe0, 0xf8, 0x40, 0x7d, 0x6a, 0xf3,
+    0x1a, 0x5d, 0x02, 0xe5, 0xcb, 0x24, 0x29, 0x1a, 0xac, 0x15, 0x94, 0xd1, 0x5b,
+    0xcd, 0x75, 0x2f, 0x45, 0x79, 0x98, 0xf7, 0x60, 0x9a, 0xd5, 0xca, 0x80, 0x15,
+    0x87, 0x9b, 0x0c, 0x27, 0xe3, 0x01, 0x8b, 0x73, 0x4e, 0x57, 0xa3, 0xd2, 0x9a,
+    0x02, 0x30, 0x33, 0xe0, 0x04, 0x5e, 0x76, 0x1f, 0xc8, 0xcf, 0xda, 0xbe, 0x64,
+    0x95, 0x0a, 0xd4, 0x85, 0x34, 0x33, 0x08, 0x7a, 0x81, 0xf2, 0xf6, 0xb6, 0x94,
+    0x68, 0xc3, 0x8c, 0x5f, 0x88, 0x92, 0x27, 0x5e, 0x4e, 0x84, 0x96, 0x48, 0x42,
+    0x84, 0x28, 0xac, 0x37, 0x93, 0x07, 0xd3, 0x50, 0x32, 0x71, 0xb0
+    };
+    // zig fmt: on
+
+    const sk = try Scheme.SecretKey.fromBytes(sk_bytes);
+    const kp = try Scheme.KeyPair.fromSecretKey(sk);
+
+    const sig_ans = try Scheme.Signature.fromDer(&sig_ans_bytes);
+    try sig_ans.verify(&msg, kp.public_key);
+
+    const sig = try kp.sign(&msg, null);
+    try sig.verify(&msg, kp.public_key);
+}
+
 const TestVector = struct {
     key: []const u8,
     msg: []const u8,