Skip to content

std.crypto: add DER parser and RSA #19771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions gen_wycheproof.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// transforms https://github.com/raw/C2SP/wycheproof/cd27d6419bedd83cbd24611ec54b6d4bfdb0cdca/testvectors/ecdsa_secp256r1_sha256_test.json into [_]TestVector{}
const std = @import("std");

const path = "ecdsa_secp256r1_sha256_test.json";

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();

const infile = try std.fs.cwd().openFile(path, .{});
defer infile.close();

const json_str = try infile.readToEndAlloc(allocator, 1 << 24);
defer allocator.free(json_str);
const T = struct {
testGroups: []struct { key: struct { uncompressed: []const u8 }, tests: []struct {
comment: []const u8,
msg: []const u8,
sig: []const u8,
result: []const u8,
} },
};
const Result = enum {
valid,
invalid,
acceptable,
};

const parsed = try std.json.parseFromSlice(T, allocator, json_str, .{ .ignore_unknown_fields = true });
defer parsed.deinit();

const outfile = try std.fs.cwd().createFile("./ecdsa_secp256r1_sha256_wycheproof.zig", .{});
defer outfile.close();

var writer = outfile.writer();

try writer.writeAll("// generated from " ++ path ++
\\
\\pub const Test = struct {
\\ comment: []const u8,
\\ msg: []const u8,
\\ sig: []const u8,
\\ result: enum {
\\ valid,
\\ invalid,
\\ acceptable,
\\ },
\\};
\\pub const TestGroup = struct {
\\ key: []const u8,
\\ tests: []const Test,
\\};
\\
\\pub const test_groups = [_]TestGroup{
);

const value = parsed.value;
for (value.testGroups) |group| {
try writer.print(
\\
\\ .{{
\\ .key = "{s}",
\\ .tests = &[_]Test{{
, .{group.key.uncompressed});
for (group.tests) |t| {
try writer.print(
\\
\\ .{{
\\ .comment = "{s}",
\\ .msg = "{s}",
\\ .sig = "{s}",
\\ .result = .{s},
\\ }},
, .{
t.comment,
t.msg,
t.sig,
@tagName(std.meta.stringToEnum(Result, t.result).?),
});
}
try writer.writeAll(
\\
\\ },
\\ },
);
}

try outfile.writeAll(
\\
\\};
);
}
4 changes: 4 additions & 0 deletions lib/std/crypto.zig
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ pub const errors = @import("crypto/errors.zig");
pub const tls = @import("crypto/tls.zig");
pub const Certificate = @import("crypto/Certificate.zig");

pub const rsa = @import("crypto/rsa.zig");

/// Side-channels mitigations.
pub const SideChannelsMitigations = enum {
/// No additional side-channel mitigations are applied.
Expand Down Expand Up @@ -307,6 +309,8 @@ test {
_ = errors;
_ = tls;
_ = Certificate;
_ = rsa;
_ = @import("crypto/oid.zig");
}

test "CSPRNG" {
Expand Down
21 changes: 21 additions & 0 deletions lib/std/crypto/25519/ed25519.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const der = @import("../der.zig");
const crypto = std.crypto;
const debug = std.debug;
const fmt = std.fmt;
Expand Down Expand Up @@ -213,6 +214,26 @@ pub const Ed25519 = struct {
};
}

pub fn fromDer(bytes: []const u8) !Signature {
var parser = der.Parser{ .bytes = bytes };
const seq = try parser.expectSequence();
defer parser.seek(seq.slice.end);

const r = try parser.expectPrimitive(.integer);
if (r.slice.len() > Curve.encoded_length) return error.InvalidScalar;
const s = try parser.expectPrimitive(.integer);
if (s.slice.len() > @sizeOf(CompressedScalar)) return error.InvalidScalar;

if (parser.index != seq.slice.end) return error.InvalidSequence;
if (parser.index != parser.bytes.len) return error.InvalidSequence;

var res = std.mem.zeroInit(Signature, .{});
@memcpy(res.r[res.r.len - parser.view(r).len ..], parser.view(r));
@memcpy(res.s[res.r.len - parser.view(s).len ..], parser.view(s));

return res;
}

/// Create a Verifier for incremental verification of a signature.
pub fn verifier(self: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {
return Verifier.init(self, public_key);
Expand Down
Loading