From 9907c8e37dfb7225d1a98dea24507f4d70bfad44 Mon Sep 17 00:00:00 2001 From: deatil <2217957370@qq.com> Date: Sat, 29 Mar 2025 12:21:19 +0800 Subject: [PATCH 1/5] crypto: add pem.zig --- lib/std/crypto.zig | 1 + lib/std/crypto/ecdsa.zig | 2 +- lib/std/crypto/pem.zig | 543 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 545 insertions(+), 1 deletion(-) create mode 100644 lib/std/crypto/pem.zig diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index a444b41cc3af..55020c06e799 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -221,6 +221,7 @@ pub const errors = @import("crypto/errors.zig"); pub const tls = @import("crypto/tls.zig"); pub const Certificate = @import("crypto/Certificate.zig"); pub const asn1 = @import("crypto/asn1.zig"); +pub const pem = @import("crypto/pem.zig"); /// Side-channels mitigations. pub const SideChannelsMitigations = enum { diff --git a/lib/std/crypto/ecdsa.zig b/lib/std/crypto/ecdsa.zig index 96b348079822..9651b104d5cb 100644 --- a/lib/std/crypto/ecdsa.zig +++ b/lib/std/crypto/ecdsa.zig @@ -19,7 +19,7 @@ pub const EcdsaP256Sha3_256 = Ecdsa(crypto.ecc.P256, crypto.hash.sha3.Sha3_256); /// ECDSA over P-384 with SHA-384. pub const EcdsaP384Sha384 = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha384); /// ECDSA over P-384 with SHA3-384. -pub const EcdsaP256Sha3_384 = Ecdsa(crypto.ecc.P384, crypto.hash.sha3.Sha3_384); +pub const EcdsaP384Sha3_384 = Ecdsa(crypto.ecc.P384, crypto.hash.sha3.Sha3_384); /// ECDSA over Secp256k1 with SHA-256. pub const EcdsaSecp256k1Sha256 = Ecdsa(crypto.ecc.Secp256k1, crypto.hash.sha2.Sha256); /// ECDSA over Secp256k1 with SHA-256(SHA-256()) -- The Bitcoin signature system. diff --git a/lib/std/crypto/pem.zig b/lib/std/crypto/pem.zig new file mode 100644 index 000000000000..27a63966210f --- /dev/null +++ b/lib/std/crypto/pem.zig @@ -0,0 +1,543 @@ +const std = @import("std"); + +const fmt = std.fmt; +const mem = std.mem; +const sort = std.sort; +const testing = std.testing; +const base64 = std.base64; +const StringHashMap = std.hash_map.StringHashMap; +const Allocator = mem.Allocator; + +/// pem block data. +pub const Block = struct { + /// The pem type. + type: []const u8, + /// Optional headers. + headers: StringHashMap([]const u8), + /// Decoded content of a PEM file. + bytes: []const u8, + allocator: Allocator, + + const Self = @This(); + + /// init + pub fn init(allocator: Allocator) Block { + const headers = StringHashMap([]const u8).init(allocator); + + return .{ + .type = "", + .headers = headers, + .bytes = "", + .allocator = allocator, + }; + } + + /// Frees any memory that was allocated during Pem decoding. + pub fn deinit(self: *Self) void { + var headers = self.headers; + headers.deinit(); + + self.allocator.free(self.bytes); + self.* = undefined; + } +}; + +// pem errors +pub const Error = error { + NotPemData, + PemDataEmpty, + PemHeaderHasColon, +}; + +const pem_start = "\n-----BEGIN "; +const pem_end = "\n-----END "; +const pem_end_of_line = "-----"; +const colon = ":"; + +/// Decodes pem bytes. +pub fn decode(allocator: Allocator, data: []const u8) !Block { + var rest = data; + + while (true) { + if (hasPrefix(rest, pem_start[1..])) { + rest = rest[pem_start.len-1..]; + } else { + const cut_data = cut(rest, pem_start); + if (cut_data.found) { + rest = cut_data.after; + } else { + return Error.NotPemData; + } + } + + const line_data = getLine(rest); + if (!hasSuffix(line_data.line, pem_end_of_line)) { + continue; + } + + const type_line = line_data.line[0 .. line_data.line.len-pem_end_of_line.len]; + + rest = line_data.rest; + + var p = Block.init(allocator); + p.type = type_line; + + while (true) { + if (rest.len == 0) { + return Error.PemDataEmpty; + } + + const line_data2 = getLine(rest); + + const cut_data = cut(line_data2.line, colon); + if (!cut_data.found) { + break; + } + + const key = trimSpace(cut_data.before); + const val = trimSpace(cut_data.after); + + try p.headers.put(key, val); + + rest = line_data2.rest; + } + + var end_index: usize = 0; + var end_trailer_index: usize = 0; + + if (p.headers.count() == 0 and hasPrefix(rest, pem_end[1..])) { + end_index = 0; + end_trailer_index = pem_end.len - 1; + } else { + end_index = mem.indexOf(u8, rest, pem_end).?; + end_trailer_index = end_index + pem_end.len; + } + + if (end_index < 0) { + continue; + } + + const end_trailer = rest[end_trailer_index..]; + const end_trailer_len = type_line.len + pem_end_of_line.len; + if (end_trailer.len < end_trailer_len) { + continue; + } + + const rest_of_end_line = end_trailer[end_trailer_len..]; + const end_trailer2 = end_trailer[0..end_trailer_len]; + if (!hasPrefix(end_trailer2, type_line) or !hasSuffix(end_trailer2, pem_end_of_line)) { + continue; + } + + const line_data2 = getLine(rest_of_end_line); + if (line_data2.line.len != 0) { + continue; + } + + const base64_data = try removeSpacesAndTabs(allocator, rest[0..end_index]); + const base64_decode_len = try std.base64.standard.Decoder.calcSizeForSlice(base64_data); + + const decoded_data = try allocator.alloc(u8, base64_decode_len); + try std.base64.standard.Decoder.decode(decoded_data, base64_data); + + p.bytes = decoded_data; + + return p; + } +} + +const nl = "\n"; + +const pem_line_length = 64; + +fn writeHeader(writer: anytype, k: []const u8, v: []const u8) !void { + try writer.appendSlice(k); + try writer.appendSlice(":"); + try writer.appendSlice(v); + try writer.appendSlice("\n"); +} + +/// Encodes pem bytes. +pub fn encode(allocator: Allocator, b: Block) ![:0]u8 { + var headers1 = (try b.headers.clone()).iterator(); + while (headers1.next()) |kv| { + if (contains(kv.value_ptr.*, ":")) { + return Error.PemHeaderHasColon; + } + } + + var buf = std.ArrayList(u8).init(allocator); + defer buf.deinit(); + + try buf.appendSlice(pem_start[1..]); + + try buf.appendSlice(b.type); + try buf.appendSlice("-----\n"); + + if (b.headers.count() > 0) { + const proc_type = "Proc-Type"; + + var h = try allocator.alloc([]const u8, b.headers.count()); + + var has_proc_type: bool = false; + + var kv_i: usize = 0; + + var headers = (try b.headers.clone()).iterator(); + while (headers.next()) |kv| { + if (mem.eql(u8, kv.key_ptr.*, proc_type)) { + has_proc_type = true; + continue; + } + + h[kv_i] = kv.key_ptr.*; + kv_i += 1; + } + + if (has_proc_type) { + if (b.headers.get(proc_type)) |vv| { + try writeHeader(&buf, proc_type, vv[0..]); + } + + h.len -= 1; + h = h[0..]; + } + + // strings sort a to z + sort.block([]const u8, h, {}, stringSort([]const u8)); + + for (h) |k| { + if (b.headers.get(k)) |val| { + try writeHeader(&buf, k, val); + } + } + + try buf.appendSlice("\n"); + } + + const bytes_len = base64.standard.Encoder.calcSize(b.bytes.len); + const buffer = try allocator.alloc(u8, bytes_len); + + const banse64_encoded = base64.standard.Encoder.encode(buffer, b.bytes); + + var idx: usize = 0; + while (true) { + if (banse64_encoded[idx..].len < pem_line_length) { + try buf.appendSlice(banse64_encoded[idx..]); + try buf.appendSlice(nl); + break; + } else { + try buf.appendSlice(banse64_encoded[idx..(idx+pem_line_length)]); + try buf.appendSlice(nl); + + idx += pem_line_length; + } + } + + try buf.appendSlice(pem_end[1..]); + try buf.appendSlice(b.type); + try buf.appendSlice("-----\n"); + + return buf.toOwnedSliceSentinel(0); +} + +pub fn stringSort(comptime T: type) fn (void, T, T) bool { + return struct { + pub fn inner(_: void, a: T, b: T) bool { + if (a.len < b.len) { + for (a, 0..) |aa, i| { + if (aa > b[i]) { + return false; + } + } + } else { + for (b, 0..) |bb, j| { + if (bb < a[j]) { + return false; + } + } + } + + return true; + } + }.inner; +} + +fn contains(data: []const u8, sep: []const u8) bool { + const i = mem.indexOf(u8, data, sep); + if (i != null) { + return true; + } + + return false; +} + +const GetLineData = struct { + line: []const u8, + rest: []const u8, +}; + +fn getLine(data: []const u8) GetLineData { + var i = mem.indexOf(u8, data, "\n").?; + var j: usize = 0; + + if (i < 0) { + i = data.len; + j = i; + } else { + j = i + 1; + if (i > 0 and data[i-1] == '\r') { + i -= 1; + } + } + + return .{ + .line = mem.trimRight(u8, data[0..i], " \t"), + .rest = data[j..], + }; +} + +fn removeSpacesAndTabs(allocator: Allocator, data: []const u8) ![:0]u8 { + var buf = std.ArrayList(u8).init(allocator); + defer buf.deinit(); + + var n: usize = 0; + + for (data) |b| { + if (b == ' ' or b == '\t' or b == '\n') { + continue; + } + + try buf.append(b); + n += 1; + } + + return buf.toOwnedSliceSentinel(0); +} + +fn hasPrefix(rest: []const u8, needle: []const u8) bool { + return rest.len > needle.len and mem.eql(u8, rest[0..needle.len], needle); +} + +fn hasSuffix(rest: []const u8, needle: []const u8) bool { + return rest.len > needle.len and mem.eql(u8, rest[rest.len-needle.len..], needle); +} + +const CutData = struct { + before: []const u8, + after: []const u8, + found: bool, +}; + +fn cut(s: []const u8, sep: []const u8) CutData { + const i = mem.indexOf(u8, s, sep); + if (i != null) { + const j: usize = mem.indexOf(u8, s, sep).?; + return .{ + .before = s[0..j], + .after = s[j+sep.len..], + .found = true, + }; + } + + return .{ + .before = s, + .after = "", + .found = false, + }; +} + +fn isSpace(r: u8) bool { + return switch(r) { + '\t', '\n', '\r', ' ', 0x85, 0xA0 => true, + else => false, + }; +} + +fn trimSpace(s: []const u8) []const u8 { + var start: usize = 0; + while (start < s.len) : (start += 1) { + if (!isSpace(s[start])) { + break; + } + } + + var stop = s.len - 1; + while (stop > start) : (stop -= 1) { + if (!isSpace(s[stop])) { + break; + } + } + + if (start == stop) { + return ""; + } + + return s[start..(stop+1)]; +} + +pub fn base64Encode(alloc: Allocator, input: []const u8) ![]const u8 { + const encoder = base64.standard.Encoder; + const encode_len = encoder.calcSize(input.len); + + const buffer = try alloc.alloc(u8, encode_len); + const res = encoder.encode(buffer, input); + + return res; +} + +test "ASN.1 type CERTIFICATE" { + const byte = + "-----BEGIN CERTIFICATE-----\n" ++ + "MIIBmTCCAUegAwIBAgIBKjAJBgUrDgMCHQUAMBMxETAPBgNVBAMTCEF0bGFudGlz\n" ++ + "MB4XDTEyMDcwOTAzMTAzOFoXDTEzMDcwOTAzMTAzN1owEzERMA8GA1UEAxMIQXRs\n" ++ + "YW50aXMwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAu+BXo+miabDIHHx+yquqzqNh\n" ++ + "Ryn/XtkJIIHVcYtHvIX+S1x5ErgMoHehycpoxbErZmVR4GCq1S2diNmRFZCRtQID\n" ++ + "AQABo4GJMIGGMAwGA1UdEwEB/wQCMAAwIAYDVR0EAQH/BBYwFDAOMAwGCisGAQQB\n" ++ + "gjcCARUDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDAzA1BgNVHQEE\n" ++ + "LjAsgBA0jOnSSuIHYmnVryHAdywMoRUwEzERMA8GA1UEAxMIQXRsYW50aXOCASow\n" ++ + "CQYFKw4DAh0FAANBAKi6HRBaNEL5R0n56nvfclQNaXiDT174uf+lojzA4lhVInc0\n" ++ + "ILwpnZ1izL4MlI9eCSHhVQBHEp2uQdXJB+d5Byg=\n" ++ + "-----END CERTIFICATE-----\n"; + + const alloc = std.heap.page_allocator; + var pem = try decode(alloc, byte); + defer pem.deinit(); + + try testing.expectFmt("CERTIFICATE", "{s}", .{pem.type}); + try testing.expect(pem.bytes.len > 0); + + const check = + "MIIBmTCCAUegAwIBAgIBKjAJBgUrDgMCHQUAMBMxETAPBgNVBAMTCEF0bGFudGlz" ++ + "MB4XDTEyMDcwOTAzMTAzOFoXDTEzMDcwOTAzMTAzN1owEzERMA8GA1UEAxMIQXRs" ++ + "YW50aXMwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAu+BXo+miabDIHHx+yquqzqNh" ++ + "Ryn/XtkJIIHVcYtHvIX+S1x5ErgMoHehycpoxbErZmVR4GCq1S2diNmRFZCRtQID" ++ + "AQABo4GJMIGGMAwGA1UdEwEB/wQCMAAwIAYDVR0EAQH/BBYwFDAOMAwGCisGAQQB" ++ + "gjcCARUDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDAzA1BgNVHQEE" ++ + "LjAsgBA0jOnSSuIHYmnVryHAdywMoRUwEzERMA8GA1UEAxMIQXRsYW50aXOCASow" ++ + "CQYFKw4DAh0FAANBAKi6HRBaNEL5R0n56nvfclQNaXiDT174uf+lojzA4lhVInc0" ++ + "ILwpnZ1izL4MlI9eCSHhVQBHEp2uQdXJB+d5Byg="; + + try testing.expectEqualStrings(check, try base64Encode(alloc, pem.bytes)); +} + +test "ASN.1 type CERTIFICATE + Explanatory Text" { + const byte = + "Subject: CN=Atlantis\n" ++ + "Issuer: CN=Atlantis\n" ++ + "Validity: from 7/9/2012 3:10:38 AM UTC to 7/9/2013 3:10:37 AM UTC\n" ++ + "-----BEGIN CERTIFICATE-----\n" ++ + "MIIBmTCCAUegAwIBAgIBKjAJBgUrDgMCHQUAMBMxETAPBgNVBAMTCEF0bGFudGlz\n" ++ + "MB4XDTEyMDcwOTAzMTAzOFoXDTEzMDcwOTAzMTAzN1owEzERMA8GA1UEAxMIQXRs\n" ++ + "YW50aXMwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAu+BXo+miabDIHHx+yquqzqNh\n" ++ + "Ryn/XtkJIIHVcYtHvIX+S1x5ErgMoHehycpoxbErZmVR4GCq1S2diNmRFZCRtQID\n" ++ + "AQABo4GJMIGGMAwGA1UdEwEB/wQCMAAwIAYDVR0EAQH/BBYwFDAOMAwGCisGAQQB\n" ++ + "gjcCARUDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDAzA1BgNVHQEE\n" ++ + "LjAsgBA0jOnSSuIHYmnVryHAdywMoRUwEzERMA8GA1UEAxMIQXRsYW50aXOCASow\n" ++ + "CQYFKw4DAh0FAANBAKi6HRBaNEL5R0n56nvfclQNaXiDT174uf+lojzA4lhVInc0\n" ++ + "ILwpnZ1izL4MlI9eCSHhVQBHEp2uQdXJB+d5Byg=\n" ++ + "-----END CERTIFICATE-----\n"; + + const alloc = std.heap.page_allocator; + var pem = try decode(alloc, byte); + defer pem.deinit(); + + try testing.expectFmt("CERTIFICATE", "{s}", .{pem.type}); + try testing.expect(pem.bytes.len > 0); + + const check = + "MIIBmTCCAUegAwIBAgIBKjAJBgUrDgMCHQUAMBMxETAPBgNVBAMTCEF0bGFudGlz" ++ + "MB4XDTEyMDcwOTAzMTAzOFoXDTEzMDcwOTAzMTAzN1owEzERMA8GA1UEAxMIQXRs" ++ + "YW50aXMwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAu+BXo+miabDIHHx+yquqzqNh" ++ + "Ryn/XtkJIIHVcYtHvIX+S1x5ErgMoHehycpoxbErZmVR4GCq1S2diNmRFZCRtQID" ++ + "AQABo4GJMIGGMAwGA1UdEwEB/wQCMAAwIAYDVR0EAQH/BBYwFDAOMAwGCisGAQQB" ++ + "gjcCARUDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDAzA1BgNVHQEE" ++ + "LjAsgBA0jOnSSuIHYmnVryHAdywMoRUwEzERMA8GA1UEAxMIQXRsYW50aXOCASow" ++ + "CQYFKw4DAh0FAANBAKi6HRBaNEL5R0n56nvfclQNaXiDT174uf+lojzA4lhVInc0" ++ + "ILwpnZ1izL4MlI9eCSHhVQBHEp2uQdXJB+d5Byg="; + + try testing.expectEqualStrings(check, try base64Encode(alloc, pem.bytes)); +} + +test "ASN.1 type RSA PRIVATE With headers" { + const byte = + "-----BEGIN RSA PRIVATE-----\n" ++ + "ID: RSA IDs\n" ++ + "ABC: thsasd \n" ++ + "\n" ++ + "MIIBmTCCAUegAwIBAgIBKjAJBgUrDgMCHQUAMBMxETAPBgNVBAMTCEF0bGFudGlz\n" ++ + "MB4XDTEyMDcwOTAzMTAzOFoXDTEzMDcwOTAzMTAzN1owEzERMA8GA1UEAxMIQXRs\n" ++ + "YW50aXMwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAu+BXo+miabDIHHx+yquqzqNh\n" ++ + "Ryn/XtkJIIHVcYtHvIX+S1x5ErgMoHehycpoxbErZmVR4GCq1S2diNmRFZCRtQID\n" ++ + "AQABo4GJMIGGMAwGA1UdEwEB/wQCMAAwIAYDVR0EAQH/BBYwFDAOMAwGCisGAQQB\n" ++ + "gjcCARUDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDAzA1BgNVHQEE\n" ++ + "LjAsgBA0jOnSSuIHYmnVryHAdywMoRUwEzERMA8GA1UEAxMIQXRsYW50aXOCASow\n" ++ + "CQYFKw4DAh0FAANBAKi6HRBaNEL5R0n56nvfclQNaXiDT174uf+lojzA4lhVInc0\n" ++ + "ILwpnZ1izL4MlI9eCSHhVQBHEp2uQdXJB+d5Byg=\n" ++ + "-----END RSA PRIVATE-----\n"; + + const alloc = std.heap.page_allocator; + var pem = try decode(alloc, byte); + defer pem.deinit(); + + try testing.expectFmt("RSA PRIVATE", "{s}", .{pem.type}); + try testing.expect(pem.bytes.len > 0); + + const header_1 = pem.headers.get("ID").?; + const header_2 = pem.headers.get("ABC").?; + try testing.expectFmt("RSA IDs", "{s}", .{header_1}); + try testing.expectFmt("thsasd", "{s}", .{header_2}); + + const check = + "MIIBmTCCAUegAwIBAgIBKjAJBgUrDgMCHQUAMBMxETAPBgNVBAMTCEF0bGFudGlz" ++ + "MB4XDTEyMDcwOTAzMTAzOFoXDTEzMDcwOTAzMTAzN1owEzERMA8GA1UEAxMIQXRs" ++ + "YW50aXMwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAu+BXo+miabDIHHx+yquqzqNh" ++ + "Ryn/XtkJIIHVcYtHvIX+S1x5ErgMoHehycpoxbErZmVR4GCq1S2diNmRFZCRtQID" ++ + "AQABo4GJMIGGMAwGA1UdEwEB/wQCMAAwIAYDVR0EAQH/BBYwFDAOMAwGCisGAQQB" ++ + "gjcCARUDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDAzA1BgNVHQEE" ++ + "LjAsgBA0jOnSSuIHYmnVryHAdywMoRUwEzERMA8GA1UEAxMIQXRsYW50aXOCASow" ++ + "CQYFKw4DAh0FAANBAKi6HRBaNEL5R0n56nvfclQNaXiDT174uf+lojzA4lhVInc0" ++ + "ILwpnZ1izL4MlI9eCSHhVQBHEp2uQdXJB+d5Byg="; + + try testing.expectEqualStrings(check, try base64Encode(alloc, pem.bytes)); +} + +test "encode pem bin" { + const alloc = std.heap.page_allocator; + + var pp = Block.init(alloc); + pp.type = "RSA PRIVATE"; + try pp.headers.put("TTTYYY", "dghW66666"); + try pp.headers.put("Proc-Type", "4,Encond"); + pp.bytes = "pem bytes"; + + const allocator = std.heap.page_allocator; + const encoded_pem = try encode(allocator, pp); + + const check = + \\-----BEGIN RSA PRIVATE----- + \\Proc-Type:4,Encond + \\TTTYYY:dghW66666 + \\ + \\cGVtIGJ5dGVz + \\-----END RSA PRIVATE----- + \\ + ; + + try testing.expectFmt(check, "{s}", .{encoded_pem}); + + const alloc2 = std.heap.page_allocator; + var pem = try decode(alloc2, encoded_pem); + defer pem.deinit(); + + try testing.expectFmt("RSA PRIVATE", "{s}", .{pem.type}); + try testing.expect(pem.bytes.len > 0); + try testing.expectFmt("pem bytes", "{s}", .{pem.bytes}); + + const header_1 = pem.headers.get("Proc-Type").?; + const header_2 = pem.headers.get("TTTYYY").?; + try testing.expectFmt("4,Encond", "{s}", .{header_1}); + try testing.expectFmt("dghW66666", "{s}", .{header_2}); + +} + From f468d77a201c9068eb0936f75758bc1c1fee87cf Mon Sep 17 00:00:00 2001 From: deatil <2217957370@qq.com> Date: Sat, 29 Mar 2025 12:58:23 +0800 Subject: [PATCH 2/5] fixed --- lib/std/crypto/pem.zig | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/std/crypto/pem.zig b/lib/std/crypto/pem.zig index 27a63966210f..42cab77eebf6 100644 --- a/lib/std/crypto/pem.zig +++ b/lib/std/crypto/pem.zig @@ -109,23 +109,23 @@ pub fn decode(allocator: Allocator, data: []const u8) !Block { end_index = 0; end_trailer_index = pem_end.len - 1; } else { - end_index = mem.indexOf(u8, rest, pem_end).?; + end_index = mem.indexOf(u8, rest, pem_end) orelse 0; end_trailer_index = end_index + pem_end.len; } - if (end_index < 0) { + if (mem.indexOf(u8, rest, pem_end) == null) { continue; } - const end_trailer = rest[end_trailer_index..]; + var end_trailer = rest[end_trailer_index..]; const end_trailer_len = type_line.len + pem_end_of_line.len; if (end_trailer.len < end_trailer_len) { continue; } const rest_of_end_line = end_trailer[end_trailer_len..]; - const end_trailer2 = end_trailer[0..end_trailer_len]; - if (!hasPrefix(end_trailer2, type_line) or !hasSuffix(end_trailer2, pem_end_of_line)) { + end_trailer = end_trailer[0..end_trailer_len]; + if (!hasPrefix(end_trailer, type_line) or !hasSuffix(end_trailer, pem_end_of_line)) { continue; } @@ -278,13 +278,11 @@ const GetLineData = struct { }; fn getLine(data: []const u8) GetLineData { - var i = mem.indexOf(u8, data, "\n").?; - var j: usize = 0; - - if (i < 0) { - i = data.len; - j = i; - } else { + var i = data.len; + var j = i; + + if (mem.indexOf(u8, data, "\n")) |val| { + i = val; j = i + 1; if (i > 0 and data[i-1] == '\r') { i -= 1; @@ -331,8 +329,7 @@ const CutData = struct { fn cut(s: []const u8, sep: []const u8) CutData { const i = mem.indexOf(u8, s, sep); - if (i != null) { - const j: usize = mem.indexOf(u8, s, sep).?; + if (i) |j| { return .{ .before = s[0..j], .after = s[j+sep.len..], From 259567a0066ffe7bc41e9c214dee29965c93611d Mon Sep 17 00:00:00 2001 From: deatil <2217957370@qq.com> Date: Sat, 29 Mar 2025 17:15:49 +0800 Subject: [PATCH 3/5] fix pem.zig --- lib/std/crypto/pem.zig | 79 +++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 52 deletions(-) diff --git a/lib/std/crypto/pem.zig b/lib/std/crypto/pem.zig index 42cab77eebf6..8a91e809bd91 100644 --- a/lib/std/crypto/pem.zig +++ b/lib/std/crypto/pem.zig @@ -1,12 +1,10 @@ const std = @import("std"); - -const fmt = std.fmt; const mem = std.mem; const sort = std.sort; const testing = std.testing; const base64 = std.base64; const StringHashMap = std.hash_map.StringHashMap; -const Allocator = mem.Allocator; +const Allocator = std.mem.Allocator; /// pem block data. pub const Block = struct { @@ -20,7 +18,6 @@ pub const Block = struct { const Self = @This(); - /// init pub fn init(allocator: Allocator) Block { const headers = StringHashMap([]const u8).init(allocator); @@ -59,7 +56,7 @@ pub fn decode(allocator: Allocator, data: []const u8) !Block { var rest = data; while (true) { - if (hasPrefix(rest, pem_start[1..])) { + if (mem.startsWith(u8, rest, pem_start[1..])) { rest = rest[pem_start.len-1..]; } else { const cut_data = cut(rest, pem_start); @@ -71,7 +68,7 @@ pub fn decode(allocator: Allocator, data: []const u8) !Block { } const line_data = getLine(rest); - if (!hasSuffix(line_data.line, pem_end_of_line)) { + if (!mem.endsWith(u8, line_data.line, pem_end_of_line)) { continue; } @@ -105,16 +102,16 @@ pub fn decode(allocator: Allocator, data: []const u8) !Block { var end_index: usize = 0; var end_trailer_index: usize = 0; - if (p.headers.count() == 0 and hasPrefix(rest, pem_end[1..])) { + if (p.headers.count() == 0 and mem.startsWith(u8, rest, pem_end[1..])) { end_index = 0; end_trailer_index = pem_end.len - 1; } else { - end_index = mem.indexOf(u8, rest, pem_end) orelse 0; - end_trailer_index = end_index + pem_end.len; - } - - if (mem.indexOf(u8, rest, pem_end) == null) { - continue; + if (mem.indexOf(u8, rest, pem_end)) |val| { + end_index = val; + end_trailer_index = end_index + pem_end.len; + } else { + continue; + } } var end_trailer = rest[end_trailer_index..]; @@ -125,7 +122,7 @@ pub fn decode(allocator: Allocator, data: []const u8) !Block { const rest_of_end_line = end_trailer[end_trailer_len..]; end_trailer = end_trailer[0..end_trailer_len]; - if (!hasPrefix(end_trailer, type_line) or !hasSuffix(end_trailer, pem_end_of_line)) { + if (!mem.startsWith(u8, end_trailer, type_line) or !mem.endsWith(u8, end_trailer, pem_end_of_line)) { continue; } @@ -135,10 +132,10 @@ pub fn decode(allocator: Allocator, data: []const u8) !Block { } const base64_data = try removeSpacesAndTabs(allocator, rest[0..end_index]); - const base64_decode_len = try std.base64.standard.Decoder.calcSizeForSlice(base64_data); + const base64_decode_len = try base64.standard.Decoder.calcSizeForSlice(base64_data); const decoded_data = try allocator.alloc(u8, base64_decode_len); - try std.base64.standard.Decoder.decode(decoded_data, base64_data); + try base64.standard.Decoder.decode(decoded_data, base64_data); p.bytes = decoded_data; @@ -150,18 +147,18 @@ const nl = "\n"; const pem_line_length = 64; -fn writeHeader(writer: anytype, k: []const u8, v: []const u8) !void { - try writer.appendSlice(k); - try writer.appendSlice(":"); - try writer.appendSlice(v); - try writer.appendSlice("\n"); +fn appendHeader(list: *std.ArrayList(u8), k: []const u8, v: []const u8) !void { + try list.appendSlice(k); + try list.appendSlice(":"); + try list.appendSlice(v); + try list.appendSlice("\n"); } /// Encodes pem bytes. pub fn encode(allocator: Allocator, b: Block) ![:0]u8 { var headers1 = (try b.headers.clone()).iterator(); while (headers1.next()) |kv| { - if (contains(kv.value_ptr.*, ":")) { + if (mem.indexOf(u8, kv.value_ptr.*, ":") != null) { return Error.PemHeaderHasColon; } } @@ -196,7 +193,7 @@ pub fn encode(allocator: Allocator, b: Block) ![:0]u8 { if (has_proc_type) { if (b.headers.get(proc_type)) |vv| { - try writeHeader(&buf, proc_type, vv[0..]); + try appendHeader(&buf, proc_type, vv[0..]); } h.len -= 1; @@ -208,7 +205,7 @@ pub fn encode(allocator: Allocator, b: Block) ![:0]u8 { for (h) |k| { if (b.headers.get(k)) |val| { - try writeHeader(&buf, k, val); + try appendHeader(&buf, k, val); } } @@ -263,15 +260,6 @@ pub fn stringSort(comptime T: type) fn (void, T, T) bool { }.inner; } -fn contains(data: []const u8, sep: []const u8) bool { - const i = mem.indexOf(u8, data, sep); - if (i != null) { - return true; - } - - return false; -} - const GetLineData = struct { line: []const u8, rest: []const u8, @@ -295,8 +283,8 @@ fn getLine(data: []const u8) GetLineData { }; } -fn removeSpacesAndTabs(allocator: Allocator, data: []const u8) ![:0]u8 { - var buf = std.ArrayList(u8).init(allocator); +fn removeSpacesAndTabs(alloc: Allocator, data: []const u8) ![:0]u8 { + var buf = std.ArrayList(u8).init(alloc); defer buf.deinit(); var n: usize = 0; @@ -313,14 +301,6 @@ fn removeSpacesAndTabs(allocator: Allocator, data: []const u8) ![:0]u8 { return buf.toOwnedSliceSentinel(0); } -fn hasPrefix(rest: []const u8, needle: []const u8) bool { - return rest.len > needle.len and mem.eql(u8, rest[0..needle.len], needle); -} - -fn hasSuffix(rest: []const u8, needle: []const u8) bool { - return rest.len > needle.len and mem.eql(u8, rest[rest.len-needle.len..], needle); -} - const CutData = struct { before: []const u8, after: []const u8, @@ -344,24 +324,17 @@ fn cut(s: []const u8, sep: []const u8) CutData { }; } -fn isSpace(r: u8) bool { - return switch(r) { - '\t', '\n', '\r', ' ', 0x85, 0xA0 => true, - else => false, - }; -} - fn trimSpace(s: []const u8) []const u8 { var start: usize = 0; while (start < s.len) : (start += 1) { - if (!isSpace(s[start])) { + if (!std.ascii.isWhitespace(s[start])) { break; } } var stop = s.len - 1; while (stop > start) : (stop -= 1) { - if (!isSpace(s[stop])) { + if (!std.ascii.isWhitespace(s[stop])) { break; } } @@ -397,7 +370,9 @@ test "ASN.1 type CERTIFICATE" { "ILwpnZ1izL4MlI9eCSHhVQBHEp2uQdXJB+d5Byg=\n" ++ "-----END CERTIFICATE-----\n"; + // const alloc = testing.allocator; const alloc = std.heap.page_allocator; + var pem = try decode(alloc, byte); defer pem.deinit(); From 993a27f8cf2a93201e65ae9047a07aa8811af61e Mon Sep 17 00:00:00 2001 From: deatil <2217957370@qq.com> Date: Sun, 30 Mar 2025 18:23:01 +0800 Subject: [PATCH 4/5] fixed pem.zig --- lib/std/crypto/pem.zig | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/std/crypto/pem.zig b/lib/std/crypto/pem.zig index 8a91e809bd91..139ad375d156 100644 --- a/lib/std/crypto/pem.zig +++ b/lib/std/crypto/pem.zig @@ -3,15 +3,17 @@ const mem = std.mem; const sort = std.sort; const testing = std.testing; const base64 = std.base64; -const StringHashMap = std.hash_map.StringHashMap; const Allocator = std.mem.Allocator; +const ArraySlice = std.ArrayList(u8); +const StringKeyHashMap = std.hash_map.StringHashMap([]const u8); + /// pem block data. pub const Block = struct { /// The pem type. type: []const u8, /// Optional headers. - headers: StringHashMap([]const u8), + headers: StringKeyHashMap, /// Decoded content of a PEM file. bytes: []const u8, allocator: Allocator, @@ -19,7 +21,7 @@ pub const Block = struct { const Self = @This(); pub fn init(allocator: Allocator) Block { - const headers = StringHashMap([]const u8).init(allocator); + const headers = StringKeyHashMap.init(allocator); return .{ .type = "", @@ -147,7 +149,7 @@ const nl = "\n"; const pem_line_length = 64; -fn appendHeader(list: *std.ArrayList(u8), k: []const u8, v: []const u8) !void { +fn appendHeader(list: *ArraySlice, k: []const u8, v: []const u8) !void { try list.appendSlice(k); try list.appendSlice(":"); try list.appendSlice(v); @@ -163,7 +165,7 @@ pub fn encode(allocator: Allocator, b: Block) ![:0]u8 { } } - var buf = std.ArrayList(u8).init(allocator); + var buf = ArraySlice.init(allocator); defer buf.deinit(); try buf.appendSlice(pem_start[1..]); @@ -284,10 +286,8 @@ fn getLine(data: []const u8) GetLineData { } fn removeSpacesAndTabs(alloc: Allocator, data: []const u8) ![:0]u8 { - var buf = std.ArrayList(u8).init(alloc); + var buf = ArraySlice.init(alloc); defer buf.deinit(); - - var n: usize = 0; for (data) |b| { if (b == ' ' or b == '\t' or b == '\n') { @@ -295,7 +295,6 @@ fn removeSpacesAndTabs(alloc: Allocator, data: []const u8) ![:0]u8 { } try buf.append(b); - n += 1; } return buf.toOwnedSliceSentinel(0); @@ -370,8 +369,7 @@ test "ASN.1 type CERTIFICATE" { "ILwpnZ1izL4MlI9eCSHhVQBHEp2uQdXJB+d5Byg=\n" ++ "-----END CERTIFICATE-----\n"; - // const alloc = testing.allocator; - const alloc = std.heap.page_allocator; + const alloc = testing.allocator; var pem = try decode(alloc, byte); defer pem.deinit(); @@ -410,7 +408,8 @@ test "ASN.1 type CERTIFICATE + Explanatory Text" { "ILwpnZ1izL4MlI9eCSHhVQBHEp2uQdXJB+d5Byg=\n" ++ "-----END CERTIFICATE-----\n"; - const alloc = std.heap.page_allocator; + const alloc = testing.allocator; + var pem = try decode(alloc, byte); defer pem.deinit(); @@ -448,7 +447,8 @@ test "ASN.1 type RSA PRIVATE With headers" { "ILwpnZ1izL4MlI9eCSHhVQBHEp2uQdXJB+d5Byg=\n" ++ "-----END RSA PRIVATE-----\n"; - const alloc = std.heap.page_allocator; + const alloc = testing.allocator; + var pem = try decode(alloc, byte); defer pem.deinit(); @@ -475,7 +475,7 @@ test "ASN.1 type RSA PRIVATE With headers" { } test "encode pem bin" { - const alloc = std.heap.page_allocator; + const alloc = testing.allocator; var pp = Block.init(alloc); pp.type = "RSA PRIVATE"; @@ -483,8 +483,7 @@ test "encode pem bin" { try pp.headers.put("Proc-Type", "4,Encond"); pp.bytes = "pem bytes"; - const allocator = std.heap.page_allocator; - const encoded_pem = try encode(allocator, pp); + const encoded_pem = try encode(alloc, pp); const check = \\-----BEGIN RSA PRIVATE----- @@ -498,8 +497,7 @@ test "encode pem bin" { try testing.expectFmt(check, "{s}", .{encoded_pem}); - const alloc2 = std.heap.page_allocator; - var pem = try decode(alloc2, encoded_pem); + var pem = try decode(alloc, encoded_pem); defer pem.deinit(); try testing.expectFmt("RSA PRIVATE", "{s}", .{pem.type}); From 5b3098fba04b39c88d39fc2b8fb24d6c261424e3 Mon Sep 17 00:00:00 2001 From: deatil <2217957370@qq.com> Date: Mon, 31 Mar 2025 17:32:52 +0800 Subject: [PATCH 5/5] fixed pem.zig comment --- lib/std/crypto/pem.zig | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/std/crypto/pem.zig b/lib/std/crypto/pem.zig index 139ad375d156..d83d02910455 100644 --- a/lib/std/crypto/pem.zig +++ b/lib/std/crypto/pem.zig @@ -1,3 +1,7 @@ +/// pem.zig implements the PEM data encoding, which originated in Privacy Enhanced Mail. +/// The most common use of PEM encoding today is in TLS keys and certificates. +/// See RFC 1421. + const std = @import("std"); const mem = std.mem; const sort = std.sort; @@ -8,13 +12,22 @@ const Allocator = std.mem.Allocator; const ArraySlice = std.ArrayList(u8); const StringKeyHashMap = std.hash_map.StringHashMap([]const u8); -/// pem block data. +/// A Block represents a PEM encoded structure. +/// +/// The encoded form is: +/// +/// -----BEGIN Type----- +/// Headers +/// base64-encoded Bytes +/// -----END Type----- +/// +/// where Headers is a possibly empty sequence of Key: Value lines. pub const Block = struct { - /// The pem type. + /// The type, taken from the preamble (i.e. "RSA PRIVATE KEY"). type: []const u8, /// Optional headers. headers: StringKeyHashMap, - /// Decoded content of a PEM file. + /// The decoded bytes of the contents. Typically a DER encoded ASN.1 structure. bytes: []const u8, allocator: Allocator, @@ -41,7 +54,6 @@ pub const Block = struct { } }; -// pem errors pub const Error = error { NotPemData, PemDataEmpty, @@ -53,7 +65,8 @@ const pem_end = "\n-----END "; const pem_end_of_line = "-----"; const colon = ":"; -/// Decodes pem bytes. +/// decode will find the next PEM formatted block (certificate, private key +/// etc) in the input. It returns that block and the remainder of the input. pub fn decode(allocator: Allocator, data: []const u8) !Block { var rest = data; @@ -158,8 +171,8 @@ fn appendHeader(list: *ArraySlice, k: []const u8, v: []const u8) !void { /// Encodes pem bytes. pub fn encode(allocator: Allocator, b: Block) ![:0]u8 { - var headers1 = (try b.headers.clone()).iterator(); - while (headers1.next()) |kv| { + var headers_it = (try b.headers.clone()).iterator(); + while (headers_it.next()) |kv| { if (mem.indexOf(u8, kv.value_ptr.*, ":") != null) { return Error.PemHeaderHasColon; } @@ -285,6 +298,8 @@ fn getLine(data: []const u8) GetLineData { }; } +// removeSpacesAndTabs returns a copy of its input with all spaces and tabs +// removed, if there were any. Otherwise, the input is returned unchanged. fn removeSpacesAndTabs(alloc: Allocator, data: []const u8) ![:0]u8 { var buf = ArraySlice.init(alloc); defer buf.deinit(); @@ -323,6 +338,8 @@ fn cut(s: []const u8, sep: []const u8) CutData { }; } +// TrimSpace returns a subslice of s by slicing off all leading and +// trailing white space, as defined by Unicode. fn trimSpace(s: []const u8) []const u8 { var start: usize = 0; while (start < s.len) : (start += 1) { @@ -345,7 +362,7 @@ fn trimSpace(s: []const u8) []const u8 { return s[start..(stop+1)]; } -pub fn base64Encode(alloc: Allocator, input: []const u8) ![]const u8 { +fn base64Encode(alloc: Allocator, input: []const u8) ![]const u8 { const encoder = base64.standard.Encoder; const encode_len = encoder.calcSize(input.len);