| ... | ... | @@ -2164,7 +2164,87 @@ pub const Signature = union(enum) { |
| 2164 | 2164 | ssh: Ssh, |
| 2165 | 2165 | |
| 2166 | 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 | 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 | 2250 | pub const Ssh = struct { |
| ... | ... | @@ -2177,6 +2257,8 @@ pub const Signature = union(enum) { |
| 2177 | 2257 | // https://www.ietf.org/archive/id/draft-josefsson-sshsig-format-03.html |
| 2178 | 2258 | // https://datatracker.ietf.org/doc/html/rfc4251#section-5 |
| 2179 | 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 | 2262 | pub fn from(allocator: std.mem.Allocator, pem_sig: []const u8, content_plus_gpgsig: []const u8) !Signature { |
| 2181 | 2263 | if (pem_sig.len == 0) { |
| 2182 | 2264 | return .none; |
| ... | ... | @@ -2187,9 +2269,51 @@ pub const Signature = union(enum) { |
| 2187 | 2269 | var fixed: nio.FixedBufferStream([]const u8) = .init(sigcontent); |
| 2188 | 2270 | var skip = nio.SkipReader(void).from(&fixed, "\n "); |
| 2189 | 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 | 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 | 2317 | .valid = null, |
| 2194 | 2318 | } }; |
| 2195 | 2319 | } |