| ... | @@ -2164,7 +2164,87 @@ pub const Signature = union(enum) { | ... | @@ -2164,7 +2164,87 @@ pub const Signature = union(enum) { |
| 2164 | ssh: Ssh, | 2164 | ssh: Ssh, |
| 2165 | | 2165 | |
| 2166 | pub const Pgp = struct { | 2166 | pub const Pgp = struct { |
| | 2167 | packet_length: u32, |
| | 2168 | version: u8, |
| | 2169 | type_id: Pgp.Type, |
| | 2170 | pk_algorithm: Pgp.PublicKeyAlgorithm, |
| | 2171 | hash_algorithm: Pgp.HashAlgorithm, |
| | 2172 | signed_hash_value_prefix: [4]u8, |
| 2167 | valid: ?bool, | 2173 | valid: ?bool, |
| | 2174 | |
| | 2175 | /// https://datatracker.ietf.org/doc/html/rfc9580#name-signature-types |
| | 2176 | pub const Type = enum(u8) { |
| | 2177 | binary = 0x00, |
| | 2178 | text = 0x01, |
| | 2179 | standalone = 0x02, |
| | 2180 | generic_certification = 0x10, |
| | 2181 | persona_certification = 0x11, |
| | 2182 | casual_certification = 0x12, |
| | 2183 | positive_certification = 0x13, |
| | 2184 | subkey_binding = 0x18, |
| | 2185 | primary_key_binding = 0x19, |
| | 2186 | direct_key = 0x1F, |
| | 2187 | key_revocation = 0x20, |
| | 2188 | subkey_revocation = 0x28, |
| | 2189 | certification_revocation = 0x30, |
| | 2190 | timestamp = 0x40, |
| | 2191 | third_party_confirmation = 0x50, |
| | 2192 | reserved = 0xFF, |
| | 2193 | _, |
| | 2194 | |
| | 2195 | pub fn stringifyJson(e: Type, writer: anytype, options: std.json.Stringify.Options, json: type) !void { |
| | 2196 | return switch (e) { |
| | 2197 | _ => json.stringify(writer, @intFromEnum(e), options), |
| | 2198 | else => json.stringify(writer, @tagName(e), options), |
| | 2199 | }; |
| | 2200 | } |
| | 2201 | }; |
| | 2202 | |
| | 2203 | /// https://datatracker.ietf.org/doc/html/rfc9580#name-public-key-algorithms |
| | 2204 | pub const PublicKeyAlgorithm = enum(u8) { |
| | 2205 | reserved = 0, |
| | 2206 | rsa_encrypt_or_sign = 1, |
| | 2207 | rsa_encrypt = 2, |
| | 2208 | rsa_sign = 3, |
| | 2209 | elgamal_encrypt = 16, |
| | 2210 | dsa = 17, |
| | 2211 | ecdh = 18, |
| | 2212 | ecdsa = 19, |
| | 2213 | x25519 = 25, |
| | 2214 | x448 = 26, |
| | 2215 | ed25519 = 27, |
| | 2216 | ed448 = 28, |
| | 2217 | _, |
| | 2218 | |
| | 2219 | pub fn stringifyJson(e: PublicKeyAlgorithm, writer: anytype, options: std.json.Stringify.Options, json: type) !void { |
| | 2220 | return switch (e) { |
| | 2221 | _ => json.stringify(writer, @intFromEnum(e), options), |
| | 2222 | else => json.stringify(writer, @tagName(e), options), |
| | 2223 | }; |
| | 2224 | } |
| | 2225 | }; |
| | 2226 | |
| | 2227 | /// https://datatracker.ietf.org/doc/html/rfc9580#name-hash-algorithms |
| | 2228 | pub const HashAlgorithm = enum(u8) { |
| | 2229 | reserved = 0, |
| | 2230 | md5 = 1, |
| | 2231 | sha1 = 2, |
| | 2232 | ripemd160 = 3, |
| | 2233 | sha2_256 = 8, |
| | 2234 | sha2_384 = 9, |
| | 2235 | sha2_512 = 10, |
| | 2236 | sha2_224 = 11, |
| | 2237 | sha3_256 = 12, |
| | 2238 | sha3_512 = 14, |
| | 2239 | _, |
| | 2240 | |
| | 2241 | pub fn stringifyJson(e: HashAlgorithm, writer: anytype, options: std.json.Stringify.Options, json: type) !void { |
| | 2242 | return switch (e) { |
| | 2243 | _ => json.stringify(writer, @intFromEnum(e), options), |
| | 2244 | else => json.stringify(writer, @tagName(e), options), |
| | 2245 | }; |
| | 2246 | } |
| | 2247 | }; |
| 2168 | }; | 2248 | }; |
| 2169 | | 2249 | |
| 2170 | pub const Ssh = struct { | 2250 | pub const Ssh = struct { |
| ... | @@ -2177,6 +2257,8 @@ pub const Signature = union(enum) { | ... | @@ -2177,6 +2257,8 @@ pub const Signature = union(enum) { |
| 2177 | // https://www.ietf.org/archive/id/draft-josefsson-sshsig-format-03.html | 2257 | // https://www.ietf.org/archive/id/draft-josefsson-sshsig-format-03.html |
| 2178 | // https://datatracker.ietf.org/doc/html/rfc4251#section-5 | 2258 | // https://datatracker.ietf.org/doc/html/rfc4251#section-5 |
| 2179 | // https://pkg.go.dev/golang.org/x/crypto/ssh#pkg-constants | 2259 | // https://pkg.go.dev/golang.org/x/crypto/ssh#pkg-constants |
| | 2260 | // https://datatracker.ietf.org/doc/html/rfc9580#section-4 |
| | 2261 | // https://datatracker.ietf.org/doc/html/rfc9580#signature-packet |
| 2180 | pub fn from(allocator: std.mem.Allocator, pem_sig: []const u8, content_plus_gpgsig: []const u8) !Signature { | 2262 | pub fn from(allocator: std.mem.Allocator, pem_sig: []const u8, content_plus_gpgsig: []const u8) !Signature { |
| 2181 | if (pem_sig.len == 0) { | 2263 | if (pem_sig.len == 0) { |
| 2182 | return .none; | 2264 | return .none; |
| ... | @@ -2187,9 +2269,51 @@ pub const Signature = union(enum) { | ... | @@ -2187,9 +2269,51 @@ pub const Signature = union(enum) { |
| 2187 | var fixed: nio.FixedBufferStream([]const u8) = .init(sigcontent); | 2269 | var fixed: nio.FixedBufferStream([]const u8) = .init(sigcontent); |
| 2188 | var skip = nio.SkipReader(void).from(&fixed, "\n "); | 2270 | var skip = nio.SkipReader(void).from(&fixed, "\n "); |
| 2189 | var b64r = nio.Base64Reader(void).from(&skip); | 2271 | var b64r = nio.Base64Reader(void).from(&skip); |
| 2190 | _ = &b64r; | 2272 | |
| | 2273 | const packet_type: packed struct { id: u6, format: u1, reserved: u1 } = @bitCast(try b64r.readByte()); |
| | 2274 | if (packet_type.reserved != 1) return .unrecognized; |
| | 2275 | if (packet_type.format != 1) return .unrecognized; // legacy non-OpenPGP format |
| | 2276 | if (packet_type.id != 2) return .unrecognized; // not a signature |
| | 2277 | |
| | 2278 | var packet_len_partial = false; |
| | 2279 | const packet_len: u32 = blk: { |
| | 2280 | const oct1: u32 = try b64r.readByte(); |
| | 2281 | if (oct1 <= 191) break :blk oct1; |
| | 2282 | if (oct1 >= 224 and oct1 < 255) packet_len_partial = true; |
| | 2283 | if (oct1 >= 224 and oct1 < 255) break :blk @as(u32, 1) << @intCast(oct1 & 0x1F); |
| | 2284 | const oct2: u32 = try b64r.readByte(); |
| | 2285 | if (oct1 <= 223) break :blk ((oct1 - 192) << 8) + (oct2) + 192; |
| | 2286 | const oct3: u32 = try b64r.readByte(); |
| | 2287 | const oct4: u32 = try b64r.readByte(); |
| | 2288 | const oct5: u32 = try b64r.readByte(); |
| | 2289 | std.debug.assert(oct1 == 255); |
| | 2290 | break :blk (oct2 << 24) | (oct3 << 16) | (oct4 << 8) | oct5; |
| | 2291 | }; |
| | 2292 | |
| | 2293 | const sig_version = try b64r.readByte(); |
| | 2294 | var type_id: Pgp.Type = .reserved; |
| | 2295 | var pk_algo: Pgp.PublicKeyAlgorithm = .reserved; |
| | 2296 | var hash_algo: Pgp.HashAlgorithm = .reserved; |
| | 2297 | var signed_hash_value_prefix: [2]u8 = @splat(0); |
| | 2298 | |
| | 2299 | if (sig_version == 4) { |
| | 2300 | type_id = @enumFromInt(try b64r.readByte()); |
| | 2301 | pk_algo = @enumFromInt(try b64r.readByte()); |
| | 2302 | hash_algo = @enumFromInt(try b64r.readByte()); |
| | 2303 | const subpacket_len_hashed = try b64r.readInt(u16, .big); |
| | 2304 | try b64r.skipBytes(subpacket_len_hashed, .{}); // TODO read this |
| | 2305 | const subpacket_len_unhashed = try b64r.readInt(u16, .big); |
| | 2306 | try b64r.skipBytes(subpacket_len_unhashed, .{}); // TODO read this |
| | 2307 | signed_hash_value_prefix = try b64r.readArray(2); |
| | 2308 | } |
| 2191 | | 2309 | |
| 2192 | return .{ .pgp = .{ | 2310 | return .{ .pgp = .{ |
| | 2311 | .packet_length = packet_len, |
| | 2312 | .version = sig_version, |
| | 2313 | .type_id = type_id, |
| | 2314 | .pk_algorithm = pk_algo, |
| | 2315 | .hash_algorithm = hash_algo, |
| | 2316 | .signed_hash_value_prefix = extras.to_hex(signed_hash_value_prefix), |
| 2193 | .valid = null, | 2317 | .valid = null, |
| 2194 | } }; | 2318 | } }; |
| 2195 | } | 2319 | } |