const std = @import("std"); const crypto = std.crypto; const Secp256k1 = crypto.ecc.Secp256k1; const Sha256 = crypto.hash.sha2.Sha256; const scalar = Secp256k1.scalar; const verify_mod = @import("verify.zig"); // re-export internals for testing pub const field = @import("field.zig"); pub const endo = @import("endo.zig"); pub const point = @import("point.zig"); pub const verify = @import("verify.zig"); pub const jacobian = @import("jacobian.zig"); pub const affine = @import("affine.zig"); /// Drop-in replacement for std.crypto.sign.ecdsa.EcdsaSecp256k1Sha256 /// with optimized verification (~3.6x faster). /// Signing delegates to stdlib (not the bottleneck). pub const EcdsaSecp256k1Sha256 = struct { const Curve = Secp256k1; const Hash = Sha256; const StdEcdsa = crypto.sign.ecdsa.EcdsaSecp256k1Sha256; pub const Signature = struct { pub const encoded_length = 64; r: [32]u8, s: [32]u8, pub fn fromBytes(bytes: [64]u8) Signature { return .{ .r = bytes[0..32].*, .s = bytes[32..64].* }; } pub fn toBytes(sig: Signature) [64]u8 { var buf: [64]u8 = undefined; @memcpy(buf[0..32], &sig.r); @memcpy(buf[32..64], &sig.s); return buf; } /// Verify this signature against a message and public key. pub fn verifyMsg(sig: Signature, msg: []const u8, public_key: PublicKey) VerifyError!void { var h = Sha256.init(.{}); h.update(msg); const digest = h.finalResult(); return sig.verifyPrehashed(digest, public_key); } /// Verify this signature against a pre-hashed message. pub fn verifyPrehashed(sig: Signature, msg_hash: [32]u8, public_key: PublicKey) VerifyError!void { return verify_mod.verify(sig.r, sig.s, msg_hash, public_key.p); } /// Create a streaming verifier. pub fn verifier(sig: Signature, public_key: PublicKey) InitError!Verifier { return Verifier.init(sig, public_key); } /// DER encoding support. pub const der_encoded_length_max = encoded_length + 2 + 2 * 3; pub fn toDer(sig: Signature, buf: *[der_encoded_length_max]u8) []u8 { const std_sig = StdEcdsa.Signature{ .r = sig.r, .s = sig.s }; return std_sig.toDer(buf); } pub fn fromDer(der: []const u8) crypto.errors.EncodingError!Signature { const std_sig = try StdEcdsa.Signature.fromDer(der); return .{ .r = std_sig.r, .s = std_sig.s }; } }; pub const PublicKey = struct { pub const compressed_sec1_encoded_length = 33; pub const uncompressed_sec1_encoded_length = 65; p: Curve, pub fn fromSec1(sec1: []const u8) !PublicKey { const pt = try Curve.fromSec1(sec1); return .{ .p = pt }; } pub fn toCompressedSec1(pk: PublicKey) [33]u8 { return pk.p.toCompressedSec1(); } pub fn toUncompressedSec1(pk: PublicKey) [65]u8 { return pk.p.toUncompressedSec1(); } }; /// Delegate to stdlib for signing (not the bottleneck). pub const SecretKey = StdEcdsa.SecretKey; pub const KeyPair = StdEcdsa.KeyPair; pub const InitError = verify_mod.VerifyError; pub const VerifyError = verify_mod.VerifyError; /// Streaming verifier: feed data incrementally, then verify. pub const Verifier = struct { h: Sha256, sig: Signature, public_key: PublicKey, pub fn init(sig: Signature, public_key: PublicKey) InitError!Verifier { // validate r, s upfront const r = scalar.Scalar.fromBytes(sig.r, .big) catch return error.SignatureVerificationFailed; const s = scalar.Scalar.fromBytes(sig.s, .big) catch return error.SignatureVerificationFailed; if (r.isZero() or s.isZero()) return error.IdentityElement; return .{ .h = Sha256.init(.{}), .sig = sig, .public_key = public_key }; } pub fn update(self: *Verifier, data: []const u8) void { self.h.update(data); } pub fn verify(self: *Verifier) VerifyError!void { const digest = self.h.finalResult(); return self.sig.verifyPrehashed(digest, self.public_key); } }; }; test { _ = @import("field.zig"); _ = @import("endo.zig"); _ = @import("point.zig"); _ = @import("verify.zig"); _ = @import("jacobian.zig"); _ = @import("affine.zig"); }