| ... | @@ -2167,9 +2167,12 @@ pub const Signature = union(enum) { | ... | @@ -2167,9 +2167,12 @@ pub const Signature = union(enum) { |
| 2167 | packet_length: u32, | 2167 | packet_length: u32, |
| 2168 | version: u8, | 2168 | version: u8, |
| 2169 | type_id: Pgp.Type, | 2169 | type_id: Pgp.Type, |
| 2170 | pk_algorithm: Pgp.PublicKeyAlgorithm, | 2170 | material: Material, |
| 2171 | hash_algorithm: Pgp.HashAlgorithm, | 2171 | hash_algorithm: Pgp.HashAlgorithm, |
| | 2172 | keyid: [16]u8, |
| | 2173 | creation_time: time.DateTime, |
| 2172 | signed_hash_value_prefix: [4]u8, | 2174 | signed_hash_value_prefix: [4]u8, |
| | 2175 | pubkey: ?PubKey, |
| 2173 | valid: ?bool, | 2176 | valid: ?bool, |
| 2174 | | 2177 | |
| 2175 | /// https://datatracker.ietf.org/doc/html/rfc9580#name-signature-types | 2178 | /// https://datatracker.ietf.org/doc/html/rfc9580#name-signature-types |
| ... | @@ -2245,6 +2248,77 @@ pub const Signature = union(enum) { | ... | @@ -2245,6 +2248,77 @@ pub const Signature = union(enum) { |
| 2245 | }; | 2248 | }; |
| 2246 | } | 2249 | } |
| 2247 | }; | 2250 | }; |
| | 2251 | |
| | 2252 | pub const SubType = enum(u8) { |
| | 2253 | signature_creation_time = 2, |
| | 2254 | signature_expiration_time = 3, |
| | 2255 | exportable_certification = 4, |
| | 2256 | trust_signature = 5, |
| | 2257 | revocable_expression = 6, |
| | 2258 | revocable = 7, |
| | 2259 | key_expiration_time = 9, |
| | 2260 | preferred_v1_seipd_ciphers = 11, |
| | 2261 | issuer_key_id = 16, |
| | 2262 | notation_data = 20, |
| | 2263 | preferred_hash_algorithms = 21, |
| | 2264 | preferred_compression_algorithms = 22, |
| | 2265 | key_server_preferences = 23, |
| | 2266 | preferred_key_server = 24, |
| | 2267 | primary_user_id = 25, |
| | 2268 | policy_uri = 26, |
| | 2269 | key_flags = 27, |
| | 2270 | signer_user_id = 28, |
| | 2271 | revocation_reason = 29, |
| | 2272 | features = 30, |
| | 2273 | signature_target = 31, |
| | 2274 | embedded_signature = 32, |
| | 2275 | issuer_fingerprint = 33, |
| | 2276 | intended_recipient_fingerprint = 35, |
| | 2277 | preferred_aead_ciphersuites = 39, |
| | 2278 | _, |
| | 2279 | }; |
| | 2280 | |
| | 2281 | pub const Material = union(PublicKeyAlgorithm) { |
| | 2282 | reserved: void, |
| | 2283 | rsa_encrypt_or_sign: []const u8, |
| | 2284 | rsa_encrypt: void, |
| | 2285 | rsa_sign: void, |
| | 2286 | elgamal_encrypt: void, |
| | 2287 | dsa: void, |
| | 2288 | ecdh: void, |
| | 2289 | ecdsa: void, |
| | 2290 | x25519: void, |
| | 2291 | x448: void, |
| | 2292 | ed25519: void, |
| | 2293 | ed448: void, |
| | 2294 | }; |
| | 2295 | |
| | 2296 | const PubKey = struct { |
| | 2297 | version: u8, |
| | 2298 | creation_time: time.DateTime, |
| | 2299 | days_valid: u16, |
| | 2300 | material: PubKey.Material, |
| | 2301 | |
| | 2302 | const Material = union(Pgp.PublicKeyAlgorithm) { |
| | 2303 | reserved: void, |
| | 2304 | rsa_encrypt_or_sign: RSA, |
| | 2305 | rsa_encrypt: void, |
| | 2306 | rsa_sign: void, |
| | 2307 | elgamal_encrypt: void, |
| | 2308 | dsa: void, |
| | 2309 | ecdh: void, |
| | 2310 | ecdsa: void, |
| | 2311 | x25519: void, |
| | 2312 | x448: void, |
| | 2313 | ed25519: void, |
| | 2314 | ed448: void, |
| | 2315 | |
| | 2316 | const RSA = struct { |
| | 2317 | n: []const u8, |
| | 2318 | e: []const u8, |
| | 2319 | }; |
| | 2320 | }; |
| | 2321 | }; |
| 2248 | }; | 2322 | }; |
| 2249 | | 2323 | |
| 2250 | pub const Ssh = struct { | 2324 | pub const Ssh = struct { |
| ... | @@ -2264,57 +2338,159 @@ pub const Signature = union(enum) { | ... | @@ -2264,57 +2338,159 @@ pub const Signature = union(enum) { |
| 2264 | return .none; | 2338 | return .none; |
| 2265 | } | 2339 | } |
| 2266 | if (std.mem.startsWith(u8, pem_sig, "-----BEGIN PGP SIGNATURE-----\n \n ") and (std.mem.endsWith(u8, pem_sig, "\n -----END PGP SIGNATURE-----\n ") or std.mem.endsWith(u8, pem_sig, "\n -----END PGP SIGNATURE-----"))) { | 2340 | if (std.mem.startsWith(u8, pem_sig, "-----BEGIN PGP SIGNATURE-----\n \n ") and (std.mem.endsWith(u8, pem_sig, "\n -----END PGP SIGNATURE-----\n ") or std.mem.endsWith(u8, pem_sig, "\n -----END PGP SIGNATURE-----"))) { |
| 2267 | const pembody = pem_sig[33 .. std.mem.indexOf(u8, pem_sig, "\n =") orelse std.mem.indexOf(u8, pem_sig, "\n -----END").?]; | 2341 | const pembody = pem_sig[33..std.mem.indexOf(u8, pem_sig, "\n -----END").?]; |
| 2268 | const sigcontent = pembody[0..std.mem.lastIndexOf(u8, pembody, "\n ").?]; | 2342 | const sigcontent = pembody[0..std.mem.lastIndexOf(u8, pembody, "\n ").?]; |
| 2269 | var fixed: nio.FixedBufferStream([]const u8) = .init(sigcontent); | 2343 | var fixed: nio.FixedBufferStream([]const u8) = .init(sigcontent); |
| 2270 | var skip = nio.SkipReader(void).from(&fixed, "\n "); | 2344 | var skip = nio.SkipReader(void).from(&fixed, "\n "); |
| 2271 | var b64r = nio.Base64Reader(void).from(&skip); | 2345 | var b64r = nio.Base64Reader(void).from(&skip); |
| 2272 | | 2346 | |
| | 2347 | var message = extras.ManyArrayList(u8).init(allocator); |
| | 2348 | defer message.deinit(); |
| | 2349 | try message.appendSlice(try message.add(), content_plus_gpgsig); |
| | 2350 | message.lengths.items.len = 0; |
| | 2351 | { |
| | 2352 | var iter = std.mem.splitScalar(u8, content_plus_gpgsig, '\n'); |
| | 2353 | while (iter.next()) |line| { |
| | 2354 | try message.lengths.append(allocator, line.len + 1); |
| | 2355 | } |
| | 2356 | var skipping = false; |
| | 2357 | var i: usize = 0; |
| | 2358 | while (i < message.lengths.items.len) : (i += 1) { |
| | 2359 | if (!skipping and std.mem.startsWith(u8, message.items(i), "gpgsig ")) { |
| | 2360 | skipping = true; |
| | 2361 | message.remove(i); |
| | 2362 | i -= 1; |
| | 2363 | continue; |
| | 2364 | } |
| | 2365 | if (!skipping) { |
| | 2366 | continue; |
| | 2367 | } |
| | 2368 | if (skipping and std.mem.startsWith(u8, message.items(i), " ")) { |
| | 2369 | message.remove(i); |
| | 2370 | i -= 1; |
| | 2371 | continue; |
| | 2372 | } |
| | 2373 | break; |
| | 2374 | } |
| | 2375 | } |
| | 2376 | |
| | 2377 | var signed_data: nio.AllocatingWriter = .init(allocator); |
| | 2378 | defer signed_data.deinit(); |
| | 2379 | try signed_data.writeAll(message.list.items); |
| | 2380 | |
| 2273 | const packet_type: packed struct { id: u6, format: u1, reserved: u1 } = @bitCast(try b64r.readByte()); | 2381 | const packet_type: packed struct { id: u6, format: u1, reserved: u1 } = @bitCast(try b64r.readByte()); |
| 2274 | if (packet_type.reserved != 1) return .unrecognized; | 2382 | if (packet_type.reserved != 1) return .unrecognized; |
| 2275 | if (packet_type.format != 1) return .unrecognized; // legacy non-OpenPGP format | 2383 | if (packet_type.format != 1) return .unrecognized; // legacy non-OpenPGP format |
| 2276 | if (packet_type.id != 2) return .unrecognized; // not a signature | 2384 | if (packet_type.id != 2) return .unrecognized; // not a signature |
| 2277 | | 2385 | |
| 2278 | var packet_len_partial = false; | 2386 | _, const packet_len = pgp_read_packet_len(&b64r) catch return .unrecognized; |
| 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 | | 2387 | |
| 2293 | const sig_version = try b64r.readByte(); | 2388 | const sig_version = try b64r.readByte(); |
| | 2389 | try signed_data.writeAll(&.{sig_version}); |
| 2294 | var type_id: Pgp.Type = .reserved; | 2390 | var type_id: Pgp.Type = .reserved; |
| 2295 | var pk_algo: Pgp.PublicKeyAlgorithm = .reserved; | 2391 | var pk_algo: Pgp.PublicKeyAlgorithm = .reserved; |
| 2296 | var hash_algo: Pgp.HashAlgorithm = .reserved; | 2392 | var hash_algo: Pgp.HashAlgorithm = .reserved; |
| | 2393 | var creation_time: [4]u8 = @splat(0); |
| | 2394 | var keyid: [8]u8 = @splat(0); |
| 2297 | var signed_hash_value_prefix: [2]u8 = @splat(0); | 2395 | var signed_hash_value_prefix: [2]u8 = @splat(0); |
| 2298 | | 2396 | |
| | 2397 | if (sig_version == 3) blk: { |
| | 2398 | const hashed_len = try b64r.readByte(); |
| | 2399 | if (hashed_len != 5) break :blk; |
| | 2400 | type_id = @enumFromInt(try b64r.readByte()); |
| | 2401 | creation_time = try b64r.readArray(4); |
| | 2402 | keyid = try b64r.readArray(8); |
| | 2403 | pk_algo = @enumFromInt(try b64r.readByte()); |
| | 2404 | hash_algo = @enumFromInt(try b64r.readByte()); |
| | 2405 | signed_hash_value_prefix = try b64r.readArray(2); |
| | 2406 | } |
| 2299 | if (sig_version == 4) { | 2407 | if (sig_version == 4) { |
| 2300 | type_id = @enumFromInt(try b64r.readByte()); | 2408 | type_id = @enumFromInt(try b64r.readByte()); |
| 2301 | pk_algo = @enumFromInt(try b64r.readByte()); | 2409 | pk_algo = @enumFromInt(try b64r.readByte()); |
| 2302 | hash_algo = @enumFromInt(try b64r.readByte()); | 2410 | hash_algo = @enumFromInt(try b64r.readByte()); |
| | 2411 | try signed_data.writeAll(&.{ @intFromEnum(type_id), @intFromEnum(pk_algo), @intFromEnum(hash_algo) }); |
| 2303 | const subpacket_len_hashed = try b64r.readInt(u16, .big); | 2412 | const subpacket_len_hashed = try b64r.readInt(u16, .big); |
| 2304 | try b64r.skipBytes(subpacket_len_hashed, .{}); // TODO read this | 2413 | try signed_data.writeInt(u16, subpacket_len_hashed, .big); |
| | 2414 | const subpacket_bytes = try b64r.readAlloc(allocator, subpacket_len_hashed); |
| | 2415 | try signed_data.writeAll(subpacket_bytes); |
| | 2416 | try signed_data.writeAll(&.{0x04}); |
| | 2417 | try signed_data.writeAll(&.{0xFF}); |
| | 2418 | try signed_data.writeInt(u32, @truncate(signed_data.items.len - 2 - message.list.items.len), .big); |
| | 2419 | { |
| | 2420 | var lr = nio.FixedBufferStream([]u8).init(subpacket_bytes); |
| | 2421 | while (lr.rest().len > 0) { |
| | 2422 | const subpacket_len = try pgp_read_subpacket_len(&lr); |
| | 2423 | const subpacket_typeid: Pgp.SubType = @enumFromInt(try lr.readByte() & 127); |
| | 2424 | if (subpacket_typeid == .signature_creation_time and subpacket_len == 1 + 4) { |
| | 2425 | creation_time = try lr.readArray(4); |
| | 2426 | continue; |
| | 2427 | } |
| | 2428 | if (subpacket_typeid == .issuer_key_id and subpacket_len == 1 + 8) { |
| | 2429 | keyid = try lr.readArray(8); |
| | 2430 | continue; |
| | 2431 | } |
| | 2432 | try lr.skipBytes(subpacket_len - 1, .{}); |
| | 2433 | } |
| | 2434 | } |
| 2305 | const subpacket_len_unhashed = try b64r.readInt(u16, .big); | 2435 | const subpacket_len_unhashed = try b64r.readInt(u16, .big); |
| 2306 | try b64r.skipBytes(subpacket_len_unhashed, .{}); // TODO read this | 2436 | { |
| | 2437 | try b64r.skipBytes(subpacket_len_unhashed, .{}); // TODO read this |
| | 2438 | } |
| 2307 | signed_hash_value_prefix = try b64r.readArray(2); | 2439 | signed_hash_value_prefix = try b64r.readArray(2); |
| 2308 | } | 2440 | } |
| | 2441 | if (sig_version == 6) { |
| | 2442 | type_id = @enumFromInt(try b64r.readByte()); |
| | 2443 | pk_algo = @enumFromInt(try b64r.readByte()); |
| | 2444 | hash_algo = @enumFromInt(try b64r.readByte()); |
| | 2445 | const subpacket_len_hashed = try b64r.readInt(u32, .big); |
| | 2446 | { |
| | 2447 | try b64r.skipBytes(subpacket_len_hashed, .{}); // TODO read this |
| | 2448 | } |
| | 2449 | const subpacket_len_unhashed = try b64r.readInt(u32, .big); |
| | 2450 | { |
| | 2451 | try b64r.skipBytes(subpacket_len_unhashed, .{}); // TODO read this |
| | 2452 | } |
| | 2453 | signed_hash_value_prefix = try b64r.readArray(2); |
| | 2454 | } |
| | 2455 | |
| | 2456 | const material = try pgp_read_signature_material(&b64r, allocator, pk_algo); |
| | 2457 | |
| | 2458 | var valid: ?bool = null; |
| | 2459 | _ = &valid; |
| | 2460 | var pubkey_t: ?Pgp.PubKey = null; |
| | 2461 | |
| | 2462 | // PGP keys are not stored in-band, need to fetch them and cache them |
| | 2463 | // TODO check https://keys.openpgp.org |
| | 2464 | // TODO check https://keyserver.ubuntu.com |
| | 2465 | // TODO check manually uploaded keys in database |
| | 2466 | if (known_pgp_keys.get(&extras.to_HEX(keyid))) |pubkey_bytes| blk: { |
| | 2467 | const ns = std.crypto.Certificate.rsa; |
| | 2468 | var pubkey_fbs: nio.FixedBufferStream([]const u8) = .init(pubkey_bytes); |
| | 2469 | pubkey_t = pgp_parse_pubkey(&pubkey_fbs, allocator) catch break :blk; |
| | 2470 | if (pubkey_t.?.material != pk_algo) { |
| | 2471 | valid = false; |
| | 2472 | break :blk; |
| | 2473 | } |
| | 2474 | switch (pubkey_t.?.material) { |
| | 2475 | .rsa_encrypt_or_sign => |*m| { |
| | 2476 | const pubkey = ns.PublicKey.fromBytes(m.e, m.n) catch break :blk; |
| | 2477 | valid = if (pgp_rsa_verify(m.n.len, material.rsa_encrypt_or_sign, signed_data.items, pubkey, hash_algo)) true else |err| if (err == error.unrecognized) null else false; |
| | 2478 | }, |
| | 2479 | else => {}, |
| | 2480 | } |
| | 2481 | } |
| 2309 | | 2482 | |
| 2310 | return .{ .pgp = .{ | 2483 | return .{ .pgp = .{ |
| 2311 | .packet_length = packet_len, | 2484 | .packet_length = packet_len, |
| 2312 | .version = sig_version, | 2485 | .version = sig_version, |
| 2313 | .type_id = type_id, | 2486 | .type_id = type_id, |
| 2314 | .pk_algorithm = pk_algo, | 2487 | .material = material, |
| 2315 | .hash_algorithm = hash_algo, | 2488 | .hash_algorithm = hash_algo, |
| | 2489 | .keyid = extras.to_HEX(keyid), |
| | 2490 | .creation_time = .initUnix(std.mem.readInt(u32, &creation_time, .big)), |
| 2316 | .signed_hash_value_prefix = extras.to_hex(signed_hash_value_prefix), | 2491 | .signed_hash_value_prefix = extras.to_hex(signed_hash_value_prefix), |
| 2317 | .valid = null, | 2492 | .pubkey = pubkey_t, |
| | 2493 | .valid = valid, |
| 2318 | } }; | 2494 | } }; |
| 2319 | } | 2495 | } |
| 2320 | if (std.mem.startsWith(u8, pem_sig, "-----BEGIN SSH SIGNATURE-----\n") and std.mem.endsWith(u8, pem_sig, "\n -----END SSH SIGNATURE-----")) { | 2496 | if (std.mem.startsWith(u8, pem_sig, "-----BEGIN SSH SIGNATURE-----\n") and std.mem.endsWith(u8, pem_sig, "\n -----END SSH SIGNATURE-----")) { |
| ... | @@ -2429,6 +2605,126 @@ pub const Signature = union(enum) { | ... | @@ -2429,6 +2605,126 @@ pub const Signature = union(enum) { |
| 2429 | } | 2605 | } |
| 2430 | return .unrecognized; | 2606 | return .unrecognized; |
| 2431 | } | 2607 | } |
| | 2608 | |
| | 2609 | fn pgp_read_packet_len(r: anytype) !struct { bool, u32 } { |
| | 2610 | const oct1: u32 = try r.readByte(); |
| | 2611 | if (oct1 <= 191) return .{ false, oct1 }; |
| | 2612 | if (oct1 >= 224 and oct1 < 255) return .{ true, @as(u32, 1) << @intCast(oct1 & 0x1F) }; |
| | 2613 | const oct2: u32 = try r.readByte(); |
| | 2614 | if (oct1 <= 223) return .{ false, ((oct1 - 192) << 8) + (oct2) + 192 }; |
| | 2615 | const oct3: u32 = try r.readByte(); |
| | 2616 | const oct4: u32 = try r.readByte(); |
| | 2617 | const oct5: u32 = try r.readByte(); |
| | 2618 | std.debug.assert(oct1 == 255); |
| | 2619 | return .{ false, (oct2 << 24) | (oct3 << 16) | (oct4 << 8) | oct5 }; |
| | 2620 | } |
| | 2621 | |
| | 2622 | fn pgp_read_subpacket_len(r: anytype) !u32 { |
| | 2623 | const oct1: u32 = try r.readByte(); |
| | 2624 | if (oct1 < 192) return oct1; |
| | 2625 | const oct2: u32 = try r.readByte(); |
| | 2626 | if (oct1 < 255) return ((oct1 - 192) << 8) + (oct2) + 192; |
| | 2627 | const oct3: u32 = try r.readByte(); |
| | 2628 | const oct4: u32 = try r.readByte(); |
| | 2629 | const oct5: u32 = try r.readByte(); |
| | 2630 | return (oct2 << 24) | (oct3 << 16) | (oct4 << 8) | oct5; |
| | 2631 | } |
| | 2632 | |
| | 2633 | fn pgp_parse_pubkey(r: anytype, allocator: std.mem.Allocator) !Pgp.PubKey { |
| | 2634 | const packet_type: packed struct { id: u6, format: u1, reserved: u1 } = @bitCast(try r.readByte()); |
| | 2635 | if (packet_type.reserved != 1) return error.unrecognized; |
| | 2636 | if (packet_type.format != 1) return error.unrecognized; // legacy non-OpenPGP format |
| | 2637 | if (packet_type.id != 6) return error.unrecognized; // not a public key |
| | 2638 | _, const packet_len = pgp_read_packet_len(r) catch return error.unrecognized; |
| | 2639 | var lr = nio.LimitedReader(void).from(r, packet_len); |
| | 2640 | const key_version = try lr.readByte(); |
| | 2641 | if (key_version == 3) { |
| | 2642 | // TODO |
| | 2643 | } |
| | 2644 | if (key_version == 4) { |
| | 2645 | const creation_time = try lr.readArray(4); |
| | 2646 | const pk_algo: Pgp.PublicKeyAlgorithm = @enumFromInt(try lr.readByte()); |
| | 2647 | const material = try pgp_parse_pubkey_material(r, allocator, pk_algo); |
| | 2648 | return .{ |
| | 2649 | .version = key_version, |
| | 2650 | .creation_time = .initUnix(std.mem.readInt(u32, &creation_time, .big)), |
| | 2651 | .days_valid = 0, |
| | 2652 | .material = material, |
| | 2653 | }; |
| | 2654 | } |
| | 2655 | if (key_version == 6) { |
| | 2656 | // TODO |
| | 2657 | } |
| | 2658 | return .{ |
| | 2659 | .version = key_version, |
| | 2660 | .creation_time = .initUnix(0), |
| | 2661 | .days_valid = 0, |
| | 2662 | .material = .reserved, |
| | 2663 | }; |
| | 2664 | } |
| | 2665 | |
| | 2666 | fn pgp_parse_pubkey_material(r: anytype, allocator: std.mem.Allocator, pk_algo: Pgp.PublicKeyAlgorithm) !Pgp.PubKey.Material { |
| | 2667 | switch (pk_algo) { |
| | 2668 | .rsa_encrypt_or_sign => { |
| | 2669 | const n_len = try r.readInt(u16, .big); |
| | 2670 | const n = try r.readAlloc(allocator, (n_len + 7) / 8); |
| | 2671 | const e_len = try r.readInt(u16, .big); |
| | 2672 | const e = try r.readAlloc(allocator, (e_len + 7) / 8); |
| | 2673 | return .{ .rsa_encrypt_or_sign = .{ .n = n, .e = e } }; |
| | 2674 | }, |
| | 2675 | inline else => |t| return @unionInit(Pgp.PubKey.Material, @tagName(t), {}), |
| | 2676 | _ => return .reserved, |
| | 2677 | } |
| | 2678 | } |
| | 2679 | |
| | 2680 | fn pgp_read_signature_material(r: anytype, allocator: std.mem.Allocator, pk_algo: Pgp.PublicKeyAlgorithm) !Pgp.Material { |
| | 2681 | switch (pk_algo) { |
| | 2682 | .rsa_encrypt_or_sign => { |
| | 2683 | const len = try r.readInt(u16, .big); |
| | 2684 | const bytes = try r.readAlloc(allocator, (len + 7) / 8); |
| | 2685 | return .{ .rsa_encrypt_or_sign = bytes }; |
| | 2686 | }, |
| | 2687 | inline else => |t| return @unionInit(Pgp.Material, @tagName(t), {}), |
| | 2688 | _ => return .reserved, |
| | 2689 | } |
| | 2690 | } |
| | 2691 | |
| | 2692 | // https://datatracker.ietf.org/doc/html/rfc9580#name-rsa |
| | 2693 | // An implementation SHOULD NOT encrypt, sign, or verify using RSA keys of a size less than 3072 bits. |
| | 2694 | // An implementation that decrypts a message using an RSA secret key of a size less than 3072 bits SHOULD generate a deprecation warning that the key is too weak for modern use. |
| | 2695 | fn pgp_rsa_verify(modulus_len_r: usize, sig: []const u8, message: []const u8, pubkey: std.crypto.Certificate.rsa.PublicKey, hash_algorithm: Pgp.HashAlgorithm) !void { |
| | 2696 | return switch (modulus_len_r) { |
| | 2697 | inline 384, 512 => |modulus_len| pgp_rsa_verify_inner(modulus_len, sig, message, pubkey, hash_algorithm), |
| | 2698 | else => error.unrecognized, |
| | 2699 | }; |
| | 2700 | } |
| | 2701 | |
| | 2702 | fn pgp_rsa_verify_inner(comptime modulus_len: usize, sig: []const u8, message: []const u8, pubkey: std.crypto.Certificate.rsa.PublicKey, hash_algorithm: Pgp.HashAlgorithm) !void { |
| | 2703 | const ns = std.crypto.Certificate.rsa; |
| | 2704 | const signature = ns.PKCS1v1_5Signature.fromBytes(modulus_len, sig); |
| | 2705 | return switch (hash_algorithm) { |
| | 2706 | .reserved => error.unrecognized, |
| | 2707 | .md5 => error.unrecognized, //ns.PKCS1v1_5Signature.verify(modulus_len, signature, message, pubkey, std.crypto.hash.Md5), |
| | 2708 | .sha1 => ns.PKCS1v1_5Signature.verify(modulus_len, signature, message, pubkey, std.crypto.hash.Sha1), |
| | 2709 | .ripemd160 => error.unrecognized, |
| | 2710 | .sha2_224 => ns.PKCS1v1_5Signature.verify(modulus_len, signature, message, pubkey, std.crypto.hash.sha2.Sha224), |
| | 2711 | .sha2_256 => ns.PKCS1v1_5Signature.verify(modulus_len, signature, message, pubkey, std.crypto.hash.sha2.Sha256), |
| | 2712 | .sha2_384 => ns.PKCS1v1_5Signature.verify(modulus_len, signature, message, pubkey, std.crypto.hash.sha2.Sha384), |
| | 2713 | .sha2_512 => ns.PKCS1v1_5Signature.verify(modulus_len, signature, message, pubkey, std.crypto.hash.sha2.Sha512), |
| | 2714 | .sha3_256 => error.unrecognized, //ns.PKCS1v1_5Signature.verify(modulus_len, signature, message, pubkey, std.crypto.hash.sha3.Sha3_256), |
| | 2715 | .sha3_512 => error.unrecognized, //ns.PKCS1v1_5Signature.verify(modulus_len, signature, message, pubkey, std.crypto.hash.sha3.Sha3_512), |
| | 2716 | _ => error.unrecognized, |
| | 2717 | }; |
| | 2718 | } |
| | 2719 | |
| | 2720 | // curl https://keys.openpgp.org/vks/v1/by-keyid/<KEYID> | head -n -2 | tail +4 | tr -d '\n' | base64 -d | xxd -p |
| | 2721 | pub const known_pgp_keys: std.StaticStringMap([]const u8) = blk: { |
| | 2722 | @setEvalBranchQuota(std.math.maxInt(u32)); |
| | 2723 | break :blk .initComptime(.{ |
| | 2724 | // GitHub <noreply@github.com> |
| | 2725 | .{ "B5690EEEBB952194", &extras.from_hex("c6c14d0465a6c576011000b237ee2f88540ce904282abd00d96d2b19d3286d3270399b28d903e97b06f206cb8bdeb356e4e987ad6c170213ca36a508c8ecddabc3284b1a134eea8b92ca1a862c442ff5f69f284e07147d1a4633fe823d4779d870b45a150a1226855f3adeb5a2990b5336d601e8696e04fc10a967f09b0436ad3ed270e55908b487643da069aa960573b24c26559f0b7b2bf8320ee137ab30c99ee6a197e49290f9f94ed9d9756eac2ce44927e0e336f694c44b268b3678201cef8f440ac5f4b7b3817ed17e62a8232178b15ec9b646b52abd4e3b3a43ad65e021bc061e9db0b7a6bf4585ae4dc6805411afb5d4c2bd5ba39c461c2fc55094acc511e854d26720bffd5a574d3b6b4653aaa54e7018c6cfdf3a67f607aa3970a5f2b17bfb58003c6fe8f901504c1b1512bbb08b8256a20890b90c8ec0022515effb76f6b15991a9bffc96dbe2f782e2fbc5a1ca1551ac6b28658ad410e576be3f589280a4970f245e09b5a8b5fa807a6a4fc92236e4b1e1dd271723a3607474ab481685a23c007e0c5b3d3110e9a748b0a1c24287eb9cbacd6b750e7860c688e38ad1a615de38840c6028b9ab6435fdaaf39128b23d5ff65682a801f760b243edd7129ded620a12b9a914c1c8cbc8c9e483d0d6cd9d5305386a57c92c96b4a6f695da6ef00581409b09b3d166601716006040da9a7e2316829faac120722c51515137c39564ab04b01916cb0011010001") }, |
| | 2726 | }); |
| | 2727 | }; |
| 2432 | }; | 2728 | }; |
| 2433 | | 2729 | |
| 2434 | pub fn findFirstUnset(set: std.bit_set.DynamicBitSetUnmanaged, after: usize) ?usize { | 2730 | pub fn findFirstUnset(set: std.bit_set.DynamicBitSetUnmanaged, after: usize) ?usize { |