authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2022-10-28 16:25:37+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-10-28 16:25:37+02:00
logf28e4e03eeb622d1cfd391cf9f0c7e45f4d80681
treecf2d7800883683628255bb577439b369ae4607d4
parentd6943f87f9cb5607fb18a85b985bb6193e8107ae
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.sign.ecdsa: add support for incremental signatures (#13332)

Similar to what was done for EdDSA, allow incremental creation and verification of ECDSA signatures. Doing so for ECDSA is trivial, and can be useful for TLS as well as the future package manager.

1 files changed, 113 insertions(+), 47 deletions(-)

lib/std/crypto/ecdsa.zig+113-47
......@@ -84,34 +84,18 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
8484 /// The S component of an ECDSA signature.
8585 s: Curve.scalar.CompressedScalar,
8686
87 /// Create a Verifier for incremental verification of a signature.
88 pub fn verifier(self: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {
89 return Verifier.init(self, public_key);
90 }
91
8792 /// Verify the signature against a message and public key.
8893 /// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
8994 /// or SignatureVerificationError if the signature is invalid for the given message and key.
9095 pub fn verify(self: Signature, msg: []const u8, public_key: PublicKey) (IdentityElementError || NonCanonicalError || SignatureVerificationError)!void {
91 const r = try Curve.scalar.Scalar.fromBytes(self.r, .Big);
92 const s = try Curve.scalar.Scalar.fromBytes(self.s, .Big);
93 if (r.isZero() or s.isZero()) return error.IdentityElement;
94
95 const ht = Curve.scalar.encoded_length;
96 const h_len = @max(Hash.digest_length, ht);
97 var h: [h_len]u8 = [_]u8{0} ** h_len;
98 Hash.hash(msg, h[h_len - Hash.digest_length .. h_len], .{});
99
100 const z = reduceToScalar(ht, h[0..ht].*);
101 if (z.isZero()) {
102 return error.SignatureVerificationFailed;
103 }
104
105 const s_inv = s.invert();
106 const v1 = z.mul(s_inv).toBytes(.Little);
107 const v2 = r.mul(s_inv).toBytes(.Little);
108 const v1g = try Curve.basePoint.mulPublic(v1, .Little);
109 const v2pk = try public_key.p.mulPublic(v2, .Little);
110 const vxs = v1g.add(v2pk).affineCoordinates().x.toBytes(.Big);
111 const vr = reduceToScalar(Curve.Fe.encoded_length, vxs);
112 if (!r.equivalent(vr)) {
113 return error.SignatureVerificationFailed;
114 }
96 var st = try Verifier.init(self, public_key);
97 st.update(msg);
98 return st.verify();
11599 }
116100
117101 /// Return the raw signature (r, s) in big-endian format.
......@@ -191,6 +175,104 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
191175 }
192176 };
193177
178 /// A Signer is used to incrementally compute a signature.
179 /// It can be obtained from a `KeyPair`, using the `signer()` function.
180 pub const Signer = struct {
181 h: Hash,
182 secret_key: SecretKey,
183 noise: ?[noise_length]u8,
184
185 fn init(secret_key: SecretKey, noise: ?[noise_length]u8) !Signer {
186 return Signer{
187 .h = Hash.init(.{}),
188 .secret_key = secret_key,
189 .noise = noise,
190 };
191 }
192
193 /// Add new data to the message being signed.
194 pub fn update(self: *Signer, data: []const u8) void {
195 self.h.update(data);
196 }
197
198 /// Compute a signature over the entire message.
199 pub fn finalize(self: *Signer) (IdentityElementError || NonCanonicalError)!Signature {
200 const scalar_encoded_length = Curve.scalar.encoded_length;
201 const h_len = @max(Hash.digest_length, scalar_encoded_length);
202 var h: [h_len]u8 = [_]u8{0} ** h_len;
203 var h_slice = h[h_len - Hash.digest_length .. h_len];
204 self.h.final(h_slice);
205
206 std.debug.assert(h.len >= scalar_encoded_length);
207 const z = reduceToScalar(scalar_encoded_length, h[0..scalar_encoded_length].*);
208
209 const k = deterministicScalar(h_slice.*, self.secret_key.bytes, self.noise);
210
211 const p = try Curve.basePoint.mul(k.toBytes(.Big), .Big);
212 const xs = p.affineCoordinates().x.toBytes(.Big);
213 const r = reduceToScalar(Curve.Fe.encoded_length, xs);
214 if (r.isZero()) return error.IdentityElement;
215
216 const k_inv = k.invert();
217 const zrs = z.add(r.mul(try Curve.scalar.Scalar.fromBytes(self.secret_key.bytes, .Big)));
218 const s = k_inv.mul(zrs);
219 if (s.isZero()) return error.IdentityElement;
220
221 return Signature{ .r = r.toBytes(.Big), .s = s.toBytes(.Big) };
222 }
223 };
224
225 /// A Verifier is used to incrementally verify a signature.
226 /// It can be obtained from a `Signature`, using the `verifier()` function.
227 pub const Verifier = struct {
228 h: Hash,
229 r: Curve.scalar.Scalar,
230 s: Curve.scalar.Scalar,
231 public_key: PublicKey,
232
233 fn init(sig: Signature, public_key: PublicKey) (IdentityElementError || NonCanonicalError)!Verifier {
234 const r = try Curve.scalar.Scalar.fromBytes(sig.r, .Big);
235 const s = try Curve.scalar.Scalar.fromBytes(sig.s, .Big);
236 if (r.isZero() or s.isZero()) return error.IdentityElement;
237
238 return Verifier{
239 .h = Hash.init(.{}),
240 .r = r,
241 .s = s,
242 .public_key = public_key,
243 };
244 }
245
246 /// Add new content to the message to be verified.
247 pub fn update(self: *Verifier, data: []const u8) void {
248 self.h.update(data);
249 }
250
251 /// Verify that the signature is valid for the entire message.
252 pub fn verify(self: *Verifier) (IdentityElementError || SignatureVerificationError)!void {
253 const ht = Curve.scalar.encoded_length;
254 const h_len = @max(Hash.digest_length, ht);
255 var h: [h_len]u8 = [_]u8{0} ** h_len;
256 self.h.final(h[h_len - Hash.digest_length .. h_len]);
257
258 const z = reduceToScalar(ht, h[0..ht].*);
259 if (z.isZero()) {
260 return error.SignatureVerificationFailed;
261 }
262
263 const s_inv = self.s.invert();
264 const v1 = z.mul(s_inv).toBytes(.Little);
265 const v2 = self.r.mul(s_inv).toBytes(.Little);
266 const v1g = try Curve.basePoint.mulPublic(v1, .Little);
267 const v2pk = try self.public_key.p.mulPublic(v2, .Little);
268 const vxs = v1g.add(v2pk).affineCoordinates().x.toBytes(.Big);
269 const vr = reduceToScalar(Curve.Fe.encoded_length, vxs);
270 if (!self.r.equivalent(vr)) {
271 return error.SignatureVerificationFailed;
272 }
273 }
274 };
275
194276 /// An ECDSA key pair.
195277 pub const KeyPair = struct {
196278 /// Length (in bytes) of a seed required to create a key pair.
......@@ -227,30 +309,14 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
227309 /// If deterministic signatures are not required, the noise should be randomly generated instead.
228310 /// This helps defend against fault attacks.
229311 pub fn sign(key_pair: KeyPair, msg: []const u8, noise: ?[noise_length]u8) (IdentityElementError || NonCanonicalError)!Signature {
230 const secret_key = key_pair.secret_key;
231
232 const scalar_encoded_length = Curve.scalar.encoded_length;
233 const h_len = @max(Hash.digest_length, scalar_encoded_length);
234 var h: [h_len]u8 = [_]u8{0} ** h_len;
235 var h_slice = h[h_len - Hash.digest_length .. h_len];
236 Hash.hash(msg, h_slice, .{});
237
238 std.debug.assert(h.len >= scalar_encoded_length);
239 const z = reduceToScalar(scalar_encoded_length, h[0..scalar_encoded_length].*);
240
241 const k = deterministicScalar(h_slice.*, secret_key.bytes, noise);
242
243 const p = try Curve.basePoint.mul(k.toBytes(.Big), .Big);
244 const xs = p.affineCoordinates().x.toBytes(.Big);
245 const r = reduceToScalar(Curve.Fe.encoded_length, xs);
246 if (r.isZero()) return error.IdentityElement;
247
248 const k_inv = k.invert();
249 const zrs = z.add(r.mul(try Curve.scalar.Scalar.fromBytes(secret_key.bytes, .Big)));
250 const s = k_inv.mul(zrs);
251 if (s.isZero()) return error.IdentityElement;
312 var st = try key_pair.signer(noise);
313 st.update(msg);
314 return st.finalize();
315 }
252316
253 return Signature{ .r = r.toBytes(.Big), .s = s.toBytes(.Big) };
317 /// Create a Signer, that can be used for incremental signature verification.
318 pub fn signer(key_pair: KeyPair, noise: ?[noise_length]u8) !Signer {
319 return Signer.init(key_pair.secret_key, noise);
254320 }
255321 };
256322