parse keyUsages properly

This commit is contained in:
Halil Durak
2026-01-12 17:16:36 +03:00
parent 9945a5f9cc
commit fd26ae4b5b

View File

@@ -101,6 +101,7 @@ pub const CryptoKey = struct {
pub const Type = enum(u8) { hmac, rsa }; pub const Type = enum(u8) { hmac, rsa };
/// Changing the names of fields would affect bitmask creation.
pub const Usages = struct { pub const Usages = struct {
// zig fmt: off // zig fmt: off
pub const encrypt = 0x001; pub const encrypt = 0x001;
@@ -121,20 +122,52 @@ pub const CryptoKey = struct {
page: *Page, page: *Page,
) !*CryptoKey { ) !*CryptoKey {
// TODO. // TODO.
_ = key_usages;
return switch (algorithm) { return switch (algorithm) {
.hmac_key_gen => |hmac| try initHMAC(hmac, extractable, page), .hmac_key_gen => |hmac| initHMAC(hmac, extractable, key_usages, page),
else => @panic("NYI"), else => @panic("NYI"),
}; };
} }
fn initHMAC(algorithm: Params.HmacKeyGen, extractable: bool, page: *Page) !*CryptoKey { /// Create a bitmask out of `key_usages`.-
fn createUsagesMask(usages: []const []const u8) !u8 {
const decls = @typeInfo(Usages).@"struct".decls;
var mask: u8 = 0;
iter_usages: for (usages) |usage| {
inline for (decls) |decl| {
if (std.mem.eql(u8, decl.name, usage)) {
mask |= @field(Usages, decl.name);
continue :iter_usages;
}
}
// Unknown usage if got here, report error.
return error.SyntaxError;
}
return mask;
}
inline fn canSign(self: *const CryptoKey) bool {
return self._usages & Usages.sign != 0;
}
inline fn canVerify(self: *const CryptoKey) bool {
return self._usages & Usages.verify != 0;
}
fn initHMAC(
algorithm: Params.HmacKeyGen,
extractable: bool,
key_usages: []const []const u8,
page: *Page,
) !*CryptoKey {
const hash = switch (algorithm.hash) { const hash = switch (algorithm.hash) {
.string => |str| str, .string => |str| str,
.object => |obj| obj.name, .object => |obj| obj.name,
}; };
// Find digest. // Find digest.
const digest = try getDigest(hash); const digest = try getDigest(hash);
// Calculate usages mask and check if its correct.
const usages_mask = try createUsagesMask(key_usages);
const block_size: usize = blk: { const block_size: usize = blk: {
// Caller provides this in bits, not bytes. // Caller provides this in bits, not bytes.
@@ -155,13 +188,17 @@ pub const CryptoKey = struct {
return page._factory.create(CryptoKey{ return page._factory.create(CryptoKey{
._type = .hmac, ._type = .hmac,
._extractable = extractable, ._extractable = extractable,
._usages = 0, ._usages = usages_mask,
._key = key, ._key = key,
._digest = digest, ._digest = digest,
}); });
} }
fn signHMAC(self: *const CryptoKey, data: []const u8, page: *Page) !js.Promise { fn signHMAC(self: *const CryptoKey, data: []const u8, page: *Page) !js.ArrayBuffer {
if (!self.canSign()) {
return error.InvalidAccessError;
}
const buffer = try page.arena.alloc(u8, crypto.EVP_MD_size(self._digest)); const buffer = try page.arena.alloc(u8, crypto.EVP_MD_size(self._digest));
errdefer page.arena.free(buffer); errdefer page.arena.free(buffer);
var out_len: u32 = 0; var out_len: u32 = 0;
@@ -177,9 +214,10 @@ pub const CryptoKey = struct {
); );
if (signed != null) { if (signed != null) {
return page.js.resolvePromise(js.ArrayBuffer{ .values = buffer[0..out_len] }); return js.ArrayBuffer{ .values = buffer[0..out_len] };
} }
// Not DOM exception, failed on our side.
return error.Invalid; return error.Invalid;
} }
@@ -189,6 +227,10 @@ pub const CryptoKey = struct {
data: []const u8, data: []const u8,
page: *Page, page: *Page,
) !js.Promise { ) !js.Promise {
if (!self.canVerify()) {
return error.InvalidAccessError;
}
var buffer: [crypto.EVP_MAX_MD_BLOCK_SIZE]u8 = undefined; var buffer: [crypto.EVP_MAX_MD_BLOCK_SIZE]u8 = undefined;
var out_len: u32 = 0; var out_len: u32 = 0;
// Try to sign. // Try to sign.
@@ -230,8 +272,27 @@ pub fn generateKey(
extractable: bool, extractable: bool,
key_usages: []const []const u8, key_usages: []const []const u8,
page: *Page, page: *Page,
) !*CryptoKey { ) !js.Promise {
return CryptoKey.init(algorithm, extractable, key_usages, page); const key = CryptoKey.init(algorithm, extractable, key_usages, page) catch |err| {
return page.js.rejectPromise(@errorName(err));
};
return page.js.resolvePromise(key);
}
/// Exports a key: that is, it takes as input a CryptoKey object and gives you
/// the key in an external, portable format.
pub fn exportKey(
_: *const SubtleCrypto,
format: []const u8,
key: *CryptoKey,
page: *Page,
) !js.Promise {
if (std.mem.eql(u8, format, "raw")) {
return page.js.resolvePromise(js.ArrayBuffer{ .values = key._key });
}
return page.js.rejectPromise(@errorName(error.NotSupported));
} }
const SignatureAlgorithm = union(enum) { const SignatureAlgorithm = union(enum) {
@@ -262,12 +323,21 @@ pub fn sign(
data: []const u8, // ArrayBuffer. data: []const u8, // ArrayBuffer.
page: *Page, page: *Page,
) !js.Promise { ) !js.Promise {
// Verify algorithm.
if (!algorithm.isHMAC()) return error.InvalidAccess;
return switch (key._type) { return switch (key._type) {
.hmac => key.signHMAC(data, page), .hmac => {
else => return error.InvalidAccess, // Verify algorithm.
if (!algorithm.isHMAC()) {
return page.js.rejectPromise(@errorName(error.InvalidAccessError));
}
// Call sign for HMAC.
const result = key.signHMAC(data, page) catch |err| {
return page.js.rejectPromise(@errorName(err));
};
return page.js.resolvePromise(result);
},
else => return page.js.rejectPromise(@errorName(error.InvalidAccessError)),
}; };
} }
@@ -298,7 +368,8 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain(); pub const prototype_chain = bridge.prototypeChain();
}; };
pub const generateKey = bridge.function(SubtleCrypto.generateKey, .{}); pub const generateKey = bridge.function(SubtleCrypto.generateKey, .{ .dom_exception = true });
pub const exportKey = bridge.function(SubtleCrypto.exportKey, .{ .dom_exception = true });
pub const sign = bridge.function(SubtleCrypto.sign, .{ .dom_exception = true, .as_typed_array = false }); pub const sign = bridge.function(SubtleCrypto.sign, .{ .dom_exception = true, .as_typed_array = false });
pub const verify = bridge.function(SubtleCrypto.verify, .{}); pub const verify = bridge.function(SubtleCrypto.verify, .{ .dom_exception = true });
}; };