atproto pds in zig pds.zat.dev
pds atproto
25

Configure Feed

Select the types of activity you want to include in your feed.

Accept standard JOSE ECDSA signatures

zzstoatzz (May 24, 2026, 11:00 PM -0500) 6f779f9e 1e22206b

+32 -10
+32 -10
src/internal/jose.zig
··· 2 2 const zat = @import("zat"); 3 3 4 4 pub fn verifyEcdsaJwtSignature(allocator: std.mem.Allocator, alg: []const u8, signing_input: []const u8, signature: []const u8, public_key: []const u8) !void { 5 + _ = allocator; 5 6 if (std.mem.eql(u8, alg, "ES256")) { 6 - zat.jwt.verifyP256(signing_input, signature, public_key) catch { 7 - const normalized = try normalizeEcdsaHighS(allocator, signature, p256_order); 8 - defer allocator.free(normalized); 9 - try zat.jwt.verifyP256(signing_input, normalized, public_key); 10 - }; 7 + try verifyRawEcdsa(std.crypto.sign.ecdsa.EcdsaP256Sha256, signing_input, signature, public_key); 11 8 } else if (std.mem.eql(u8, alg, "ES256K")) { 12 - zat.jwt.verifySecp256k1(signing_input, signature, public_key) catch { 13 - const normalized = try normalizeEcdsaHighS(allocator, signature, secp256k1_order); 14 - defer allocator.free(normalized); 15 - try zat.jwt.verifySecp256k1(signing_input, normalized, public_key); 16 - }; 9 + try verifyRawEcdsa(std.crypto.sign.ecdsa.EcdsaSecp256k1Sha256, signing_input, signature, public_key); 17 10 } else { 18 11 return error.UnsupportedAlgorithm; 19 12 } 13 + } 14 + 15 + fn verifyRawEcdsa(comptime Scheme: type, message: []const u8, signature: []const u8, public_key_raw: []const u8) !void { 16 + if (signature.len != 64) return error.InvalidSignature; 17 + if (public_key_raw.len != 33) return error.InvalidPublicKey; 18 + 19 + const sig = Scheme.Signature.fromBytes(signature[0..64].*); 20 + const public_key = Scheme.PublicKey.fromSec1(public_key_raw) catch return error.InvalidPublicKey; 21 + sig.verify(message, public_key) catch return error.SignatureVerificationFailed; 20 22 } 21 23 22 24 const p256_order: [32]u8 = .{ ··· 81 83 const pk = kp.public_key.toCompressedSec1(); 82 84 try verifyEcdsaJwtSignature(std.testing.allocator, "ES256", message, &high_s_sig, &pk); 83 85 } 86 + 87 + test "JOSE ECDSA verifier accepts high-S secp256k1 signatures" { 88 + const Scheme = std.crypto.sign.ecdsa.EcdsaSecp256k1Sha256; 89 + const sk_bytes = [_]u8{ 90 + 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 91 + 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 92 + 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 93 + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 94 + }; 95 + const message = "oauth client assertion"; 96 + const sig = try zat.jwt.signSecp256k1(message, &sk_bytes); 97 + 98 + var high_s_sig = sig.bytes; 99 + subtractBigEndian(high_s_sig[32..64], &secp256k1_order, sig.bytes[32..64]); 100 + 101 + const sk = try Scheme.SecretKey.fromBytes(sk_bytes); 102 + const kp = try Scheme.KeyPair.fromSecretKey(sk); 103 + const pk = kp.public_key.toCompressedSec1(); 104 + try verifyEcdsaJwtSignature(std.testing.allocator, "ES256K", message, &high_s_sig, &pk); 105 + }