Skip to content

Commit bf36ff9

Browse files
Merge pull request #593 from lightpanda-io/crypto_web_api
add crypto web api
2 parents 8eadccd + ddd0a42 commit bf36ff9

File tree

4 files changed

+96
-2
lines changed

4 files changed

+96
-2
lines changed

src/browser/crypto/crypto.zig

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
2+
//
3+
// Francis Bouvier <[email protected]>
4+
// Pierre Tachoire <[email protected]>
5+
//
6+
// This program is free software: you can redistribute it and/or modify
7+
// it under the terms of the GNU Affero General Public License as
8+
// published by the Free Software Foundation, either version 3 of the
9+
// License, or (at your option) any later version.
10+
//
11+
// This program is distributed in the hope that it will be useful,
12+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// GNU Affero General Public License for more details.
15+
//
16+
// You should have received a copy of the GNU Affero General Public License
17+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
19+
const std = @import("std");
20+
const uuidv4 = @import("../../id.zig").uuidv4;
21+
22+
// https://w3c.github.io/webcrypto/#crypto-interface
23+
pub const Crypto = struct {
24+
pub fn _getRandomValues(_: *const Crypto, into: RandomValues) !void {
25+
const buf = into.asBuffer();
26+
if (buf.len > 65_536) {
27+
return error.QuotaExceededError;
28+
}
29+
std.crypto.random.bytes(buf);
30+
}
31+
32+
pub fn _randomUUID(_: *const Crypto) [36]u8 {
33+
var hex: [36]u8 = undefined;
34+
uuidv4(&hex);
35+
return hex;
36+
}
37+
};
38+
39+
const RandomValues = union(enum) {
40+
int8: []i8,
41+
uint8: []u8,
42+
int16: []i16,
43+
uint16: []u16,
44+
int32: []i32,
45+
uint32: []u32,
46+
int64: []i64,
47+
uint64: []u64,
48+
49+
fn asBuffer(self: RandomValues) []u8 {
50+
switch (self) {
51+
.int8 => |b| return (@as([]u8, @ptrCast(b)))[0..b.len],
52+
.uint8 => |b| return (@as([]u8, @ptrCast(b)))[0..b.len],
53+
.int16 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 2],
54+
.uint16 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 2],
55+
.int32 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 4],
56+
.uint32 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 4],
57+
.int64 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 8],
58+
.uint64 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 8],
59+
}
60+
}
61+
};
62+
63+
const testing = @import("../../testing.zig");
64+
test "Browser.Crypto" {
65+
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
66+
defer runner.deinit();
67+
68+
try runner.testCases(&.{
69+
.{ "const a = crypto.randomUUID();", "undefined" },
70+
.{ "const b = crypto.randomUUID();", "undefined" },
71+
.{ "a.length;", "36" },
72+
.{ "a.length;", "36" },
73+
.{ "a == b;", "false" },
74+
}, .{});
75+
76+
try runner.testCases(&.{
77+
.{ "try { crypto.getRandomValues(new BigUint64Array(8193)) } catch(e) { e.message == 'QuotaExceededError' }", "true" },
78+
.{ "let r1 = new Int32Array(5)", "undefined" },
79+
.{ "crypto.getRandomValues(r1)", "undefined" },
80+
.{ "new Set(r1).size", "5" },
81+
}, .{});
82+
}

src/browser/env.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const HttpClient = @import("../http/client.zig").Client;
1010
const Renderer = @import("browser.zig").Renderer;
1111

1212
const Interfaces = generate.Tuple(.{
13+
@import("crypto/crypto.zig").Crypto,
1314
@import("console/console.zig").Console,
1415
@import("dom/dom.zig").Interfaces,
1516
@import("events/event.zig").Interfaces,

src/browser/html/window.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const SessionState = @import("../env.zig").SessionState;
2525
const Navigator = @import("navigator.zig").Navigator;
2626
const History = @import("history.zig").History;
2727
const Location = @import("location.zig").Location;
28+
const Crypto = @import("../crypto/crypto.zig").Crypto;
2829
const Console = @import("../console/console.zig").Console;
2930
const EventTarget = @import("../dom/event_target.zig").EventTarget;
3031

@@ -49,6 +50,7 @@ pub const Window = struct {
4950
timeoutid: u32 = 0,
5051
timeoutids: [512]u64 = undefined,
5152

53+
crypto: Crypto = .{},
5254
console: Console = .{},
5355
navigator: Navigator = .{},
5456

@@ -91,6 +93,10 @@ pub const Window = struct {
9193
return &self.console;
9294
}
9395

96+
pub fn get_crypto(self: *Window) *Crypto {
97+
return &self.crypto;
98+
}
99+
94100
pub fn get_self(self: *Window) *Window {
95101
return self;
96102
}

src/runtime/js.zig

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2532,9 +2532,14 @@ fn Caller(comptime E: type) type {
25322532

25332533
// We settle for just probing the first value. Ok, actually
25342534
// not tricky in this case either.
2535-
const context = self.contxt;
2535+
const context = self.context;
25362536
const js_obj = js_arr.castTo(v8.Object);
2537-
return self.probeJsValueToZig(named_function, ptr.child, try js_obj.getAtIndex(context, 0));
2537+
switch (try self.probeJsValueToZig(named_function, ptr.child, try js_obj.getAtIndex(context, 0))) {
2538+
.value, .ok => return .{ .ok = {} },
2539+
.compatible => return .{ .compatible = {} },
2540+
.coerce => return .{ .coerce = {} },
2541+
.invalid => return .{ .invalid = {} },
2542+
}
25382543
},
25392544
else => {},
25402545
},

0 commit comments

Comments
 (0)