atproto utils for zig zat.dev
atproto sdk zig
1//! multicodec key parsing 2//! 3//! parses multicodec-prefixed public keys from DID documents. 4//! extracts key type and raw key bytes. 5//! 6//! see: https://github.com/multiformats/multicodec 7 8const std = @import("std"); 9 10/// supported key types for AT Protocol 11pub const KeyType = enum { 12 secp256k1, // ES256K - used by most AT Protocol accounts 13 p256, // ES256 - also supported 14}; 15 16/// parsed public key with type and raw bytes 17pub const PublicKey = struct { 18 key_type: KeyType, 19 /// raw compressed public key (33 bytes for secp256k1/p256) 20 raw: []const u8, 21}; 22 23/// multicodec prefixes (unsigned varint encoding) 24/// secp256k1-pub: 0xe7 = 231, varint encoded as 0xe7 0x01 (2 bytes) 25/// p256-pub: 0x1200 = 4608, varint encoded as 0x80 0x24 (2 bytes) 26/// parse a multicodec-prefixed public key 27/// returns the key type and a slice pointing to the raw key bytes 28pub fn parsePublicKey(data: []const u8) !PublicKey { 29 if (data.len < 2) return error.TooShort; 30 31 // check for secp256k1-pub (varint 0xe7 = 231 encoded as 0xe7 0x01) 32 if (data.len >= 2 and data[0] == 0xe7 and data[1] == 0x01) { 33 const raw = data[2..]; 34 if (raw.len != 33) return error.InvalidKeyLength; 35 return .{ 36 .key_type = .secp256k1, 37 .raw = raw, 38 }; 39 } 40 41 // check for p256-pub (varint 0x1200 = 4608 encoded as 0x80 0x24) 42 if (data.len >= 2 and data[0] == 0x80 and data[1] == 0x24) { 43 const raw = data[2..]; 44 if (raw.len != 33) return error.InvalidKeyLength; 45 return .{ 46 .key_type = .p256, 47 .raw = raw, 48 }; 49 } 50 51 return error.UnsupportedKeyType; 52} 53 54// === tests === 55 56test "parse secp256k1 key" { 57 // 0xe7 0x01 prefix (varint) + 33-byte compressed key 58 var data: [35]u8 = undefined; 59 data[0] = 0xe7; 60 data[1] = 0x01; 61 data[2] = 0x02; // compressed point prefix 62 @memset(data[3..], 0xaa); 63 64 const key = try parsePublicKey(&data); 65 try std.testing.expectEqual(KeyType.secp256k1, key.key_type); 66 try std.testing.expectEqual(@as(usize, 33), key.raw.len); 67} 68 69test "parse p256 key" { 70 // 0x80 0x24 prefix + 33-byte compressed key 71 var data: [35]u8 = undefined; 72 data[0] = 0x80; 73 data[1] = 0x24; 74 data[2] = 0x03; // compressed point prefix 75 @memset(data[3..], 0xbb); 76 77 const key = try parsePublicKey(&data); 78 try std.testing.expectEqual(KeyType.p256, key.key_type); 79 try std.testing.expectEqual(@as(usize, 33), key.raw.len); 80} 81 82test "reject unsupported key type" { 83 const data = [_]u8{ 0xff, 0x02, 0x00 }; 84 try std.testing.expectError(error.UnsupportedKeyType, parsePublicKey(&data)); 85} 86 87test "reject too short" { 88 const data = [_]u8{0xe7}; 89 try std.testing.expectError(error.TooShort, parsePublicKey(&data)); 90}