diff options
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | build.zig | 9 | ||||
-rw-r--r-- | integration/bunjs-only-snippets/crypto.test.js | 28 | ||||
-rw-r--r-- | src/boringssl.zig (renamed from src/deps/boringssl.zig) | 16 | ||||
m--------- | src/deps/boringssl | 0 | ||||
-rw-r--r-- | src/deps/boringssl.translated.zig | 56 | ||||
-rw-r--r-- | src/fallback.version | 2 | ||||
-rw-r--r-- | src/io/io_darwin.zig | 52 | ||||
-rw-r--r-- | src/javascript/jsc/api/bun.zig | 214 | ||||
-rw-r--r-- | src/javascript/jsc/base.zig | 136 | ||||
-rw-r--r-- | src/sha.zig | 145 |
11 files changed, 601 insertions, 70 deletions
@@ -548,7 +548,12 @@ fetch: $(ZIG) build -Drelease-fast fetch-obj $(CXX) $(PACKAGE_DIR)/fetch.o -g $(OPTIMIZATION_LEVEL) -o ./misctools/fetch $(DEFAULT_LINKER_FLAGS) -lc $(ARCHIVE_FILES) rm -rf $(PACKAGE_DIR)/fetch.o - + +sha: + $(ZIG) build -Drelease-fast sha-bench-obj + $(CXX) $(PACKAGE_DIR)/sha.o -g $(OPTIMIZATION_LEVEL) -o ./misctools/sha $(DEFAULT_LINKER_FLAGS) -lc $(ARCHIVE_FILES) + rm -rf $(PACKAGE_DIR)/sha.o + fetch-debug: $(ZIG) build fetch-obj $(CXX) $(DEBUG_PACKAGE_DIR)/fetch.o -g $(OPTIMIZATION_LEVEL) -o ./misctools/fetch $(DEFAULT_LINKER_FLAGS) -lc $(ARCHIVE_FILES) @@ -979,7 +984,7 @@ mimalloc: -DMI_OSX_INTERPOSE=OFF \ -DMI_BUILD_OBJECT=ON \ -DMI_USE_CXX=ON \ - -DMI_OVERRIDE=OFF \ + -DMI_OVERRIDE=ON \ -DCMAKE_C_FLAGS="$(CFLAGS)" \ -DCMAKE_CXX_FLAGS="$(CFLAGS)" \ ${MIMALLOC_OVERRIDE_FLAG} \ @@ -1173,7 +1178,7 @@ endif build-unit: @rm -rf zig-out/bin/$(testname) @mkdir -p zig-out/bin - zig test $(realpath $(testpath)) \ + zig test $(realpath $(testpath)) \ $(testfilterflag) \ $(PACKAGE_MAP) \ --main-pkg-path $(BUN_DIR) \ @@ -1225,7 +1230,7 @@ copy-to-bun-release-dir-bin: cp -r $(PACKAGE_DIR)/bun-profile $(BUN_RELEASE_DIR)/bun-profile -PACKAGE_MAP = --pkg-begin thread_pool $(BUN_DIR)/src/thread_pool.zig --pkg-begin io $(BUN_DIR)/src/io/io_$(OS_NAME).zig --pkg-end --pkg-begin http $(BUN_DIR)/src/http_client_async.zig --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin boringssl $(BUN_DIR)/src/deps/boringssl.zig --pkg-end --pkg-begin thread_pool $(BUN_DIR)/src/thread_pool.zig --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin http $(BUN_DIR)/src/http_client_async.zig --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin boringssl $(BUN_DIR)/src/deps/boringssl.zig --pkg-end --pkg-begin thread_pool $(BUN_DIR)/src/thread_pool.zig --pkg-end --pkg-end --pkg-end --pkg-end --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin clap $(BUN_DIR)/src/deps/zig-clap/clap.zig --pkg-end --pkg-begin http $(BUN_DIR)/src/http_client_async.zig --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin boringssl $(BUN_DIR)/src/deps/boringssl.zig --pkg-end --pkg-begin thread_pool $(BUN_DIR)/src/thread_pool.zig --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin http $(BUN_DIR)/src/http_client_async.zig --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin boringssl $(BUN_DIR)/src/deps/boringssl.zig --pkg-end --pkg-begin thread_pool $(BUN_DIR)/src/thread_pool.zig --pkg-end --pkg-end --pkg-end --pkg-end --pkg-begin boringssl $(BUN_DIR)/src/deps/boringssl.zig --pkg-end --pkg-begin javascript_core $(BUN_DIR)/src/jsc.zig --pkg-begin http $(BUN_DIR)/src/http_client_async.zig --pkg-end --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-end +PACKAGE_MAP = --pkg-begin thread_pool $(BUN_DIR)/src/thread_pool.zig --pkg-begin io $(BUN_DIR)/src/io/io_$(OS_NAME).zig --pkg-end --pkg-begin http $(BUN_DIR)/src/http_client_async.zig --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin boringssl $(BUN_DIR)/src/boringssl.zig --pkg-end --pkg-begin thread_pool $(BUN_DIR)/src/thread_pool.zig --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin http $(BUN_DIR)/src/http_client_async.zig --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin boringssl $(BUN_DIR)/src/boringssl.zig --pkg-end --pkg-begin thread_pool $(BUN_DIR)/src/thread_pool.zig --pkg-end --pkg-end --pkg-end --pkg-end --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin clap $(BUN_DIR)/src/deps/zig-clap/clap.zig --pkg-end --pkg-begin http $(BUN_DIR)/src/http_client_async.zig --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin boringssl $(BUN_DIR)/src/boringssl.zig --pkg-end --pkg-begin thread_pool $(BUN_DIR)/src/thread_pool.zig --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin http $(BUN_DIR)/src/http_client_async.zig --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-begin io $(BUN_DIR)/src/io/io_darwin.zig --pkg-end --pkg-begin boringssl $(BUN_DIR)/src/boringssl.zig --pkg-end --pkg-begin thread_pool $(BUN_DIR)/src/thread_pool.zig --pkg-end --pkg-end --pkg-end --pkg-end --pkg-begin boringssl $(BUN_DIR)/src/boringssl.zig --pkg-end --pkg-begin javascript_core $(BUN_DIR)/src/jsc.zig --pkg-begin http $(BUN_DIR)/src/http_client_async.zig --pkg-end --pkg-begin strings $(BUN_DIR)/src/string_immutable.zig --pkg-end --pkg-begin picohttp $(BUN_DIR)/src/deps/picohttp.zig --pkg-end --pkg-end bun: vendor identifier-cache build-obj bun-link-lld-release bun-codesign-release-local @@ -43,7 +43,7 @@ const color_map = std.ComptimeStringMap([]const u8, .{ fn addInternalPackages(step: *std.build.LibExeObjStep, _: std.mem.Allocator, target: anytype) !void { var boringssl: std.build.Pkg = .{ .name = "boringssl", - .path = pkgPath("src/deps/boringssl.zig"), + .path = pkgPath("src/boringssl.zig"), }; var datetime: std.build.Pkg = .{ @@ -405,6 +405,13 @@ pub fn build(b: *std.build.Builder) !void { } { + const headers_step = b.step("sha-bench-obj", "Build sha bench"); + var headers_obj: *std.build.LibExeObjStep = b.addObject("sha", "src/sha.zig"); + defer headers_step.dependOn(&headers_obj.step); + try configureObjectStep(b, headers_obj, target, obj.main_pkg_path.?); + } + + { const headers_step = b.step("vlq-bench", "Build vlq bench"); var headers_obj: *std.build.LibExeObjStep = b.addExecutable("vlq-bench", "src/sourcemap/vlq_bench.zig"); defer headers_step.dependOn(&headers_obj.step); diff --git a/integration/bunjs-only-snippets/crypto.test.js b/integration/bunjs-only-snippets/crypto.test.js new file mode 100644 index 000000000..344d935d5 --- /dev/null +++ b/integration/bunjs-only-snippets/crypto.test.js @@ -0,0 +1,28 @@ +import { it, expect } from "bun:test"; + +for (let Hasher of [ + Bun.SHA1, + Bun.SHA256, + Bun.SHA384, + Bun.SHA512, + Bun.SHA512_256, +]) { + it(`${Hasher.name} instance`, () => { + var buf = new Uint8Array(256); + const result = new Hasher(); + result.update("hello world"); + result.final(buf); + }); +} + +for (let HashFn of [ + Bun.sha1, + Bun.sha256, + Bun.sha384, + Bun.sha512, + Bun.sha512_256, +]) { + it(`${HashFn.name} instance`, () => { + HashFn("hello world"); + }); +} diff --git a/src/deps/boringssl.zig b/src/boringssl.zig index 2089bc1d2..b0a52e358 100644 --- a/src/deps/boringssl.zig +++ b/src/boringssl.zig @@ -1,7 +1,8 @@ -const boring = @import("./boringssl.translated.zig"); +const boring = @import("./deps/boringssl.translated.zig"); pub usingnamespace boring; const std = @import("std"); -const global = @import("../global.zig"); + +const builtin = @import("builtin"); var loaded = false; pub fn load() void { if (loaded) return; @@ -52,16 +53,19 @@ pub fn initClient() *boring.SSL { // may result in deadlocks, crashes, or memory corruption. export fn OPENSSL_memory_alloc(size: usize) ?*anyopaque { + const global = @import("./global.zig"); return global.Global.Mimalloc.mi_malloc(size); } // BoringSSL always expects memory to be zero'd export fn OPENSSL_memory_free(ptr: *anyopaque) void { + const global = @import("./global.zig"); @memset(@ptrCast([*]u8, ptr), 0, global.Global.Mimalloc.mi_usable_size(ptr)); global.Global.Mimalloc.mi_free(ptr); } export fn OPENSSL_memory_get_size(ptr: ?*const anyopaque) usize { + const global = @import("./global.zig"); return global.Global.Mimalloc.mi_usable_size(ptr); } @@ -70,7 +74,9 @@ test "load" { } comptime { - _ = OPENSSL_memory_alloc; - _ = OPENSSL_memory_free; - _ = OPENSSL_memory_get_size; + if (!builtin.is_test) { + _ = OPENSSL_memory_alloc; + _ = OPENSSL_memory_free; + _ = OPENSSL_memory_get_size; + } } diff --git a/src/deps/boringssl b/src/deps/boringssl -Subproject b3ed071ecc4efb77afd0a025ea1078da19578bf +Subproject fa3fbda07bbf70925453d6a3c25a7aa455aa1ce diff --git a/src/deps/boringssl.translated.zig b/src/deps/boringssl.translated.zig index b30d99e7e..17bb1af9d 100644 --- a/src/deps/boringssl.translated.zig +++ b/src/deps/boringssl.translated.zig @@ -2747,34 +2747,34 @@ pub extern fn RSA_verify_PKCS1_PSS(rsa: ?*const RSA, mHash: [*c]const u8, Hash: pub extern fn RSA_padding_add_PKCS1_OAEP(to: [*c]u8, to_len: usize, from: [*c]const u8, from_len: usize, param: [*c]const u8, param_len: usize) c_int; pub extern fn RSA_print(bio: [*c]BIO, rsa: ?*const RSA, indent: c_int) c_int; pub extern fn RSA_get0_pss_params(rsa: ?*const RSA) [*c]const RSA_PSS_PARAMS; -pub extern fn SHA1_Init(sha: [*c]SHA_CTX) c_int; -pub extern fn SHA1_Update(sha: [*c]SHA_CTX, data: ?*const anyopaque, len: usize) c_int; -pub extern fn SHA1_Final(out: [*c]u8, sha: [*c]SHA_CTX) c_int; -pub extern fn SHA1(data: [*c]const u8, len: usize, out: [*c]u8) [*c]u8; -pub extern fn SHA1_Transform(sha: [*c]SHA_CTX, block: [*c]const u8) void; -pub extern fn SHA224_Init(sha: [*c]SHA256_CTX) c_int; -pub extern fn SHA224_Update(sha: [*c]SHA256_CTX, data: ?*const anyopaque, len: usize) c_int; -pub extern fn SHA224_Final(out: [*c]u8, sha: [*c]SHA256_CTX) c_int; -pub extern fn SHA224(data: [*c]const u8, len: usize, out: [*c]u8) [*c]u8; -pub extern fn SHA256_Init(sha: [*c]SHA256_CTX) c_int; -pub extern fn SHA256_Update(sha: [*c]SHA256_CTX, data: ?*const anyopaque, len: usize) c_int; -pub extern fn SHA256_Final(out: [*c]u8, sha: [*c]SHA256_CTX) c_int; -pub extern fn SHA256(data: [*c]const u8, len: usize, out: [*c]u8) [*c]u8; -pub extern fn SHA256_Transform(sha: [*c]SHA256_CTX, block: [*c]const u8) void; -pub extern fn SHA256_TransformBlocks(state: [*c]u32, data: [*c]const u8, num_blocks: usize) void; -pub extern fn SHA384_Init(sha: [*c]SHA512_CTX) c_int; -pub extern fn SHA384_Update(sha: [*c]SHA512_CTX, data: ?*const anyopaque, len: usize) c_int; -pub extern fn SHA384_Final(out: [*c]u8, sha: [*c]SHA512_CTX) c_int; -pub extern fn SHA384(data: [*c]const u8, len: usize, out: [*c]u8) [*c]u8; -pub extern fn SHA512_Init(sha: [*c]SHA512_CTX) c_int; -pub extern fn SHA512_Update(sha: [*c]SHA512_CTX, data: ?*const anyopaque, len: usize) c_int; -pub extern fn SHA512_Final(out: [*c]u8, sha: [*c]SHA512_CTX) c_int; -pub extern fn SHA512(data: [*c]const u8, len: usize, out: [*c]u8) [*c]u8; -pub extern fn SHA512_Transform(sha: [*c]SHA512_CTX, block: [*c]const u8) void; -pub extern fn SHA512_256_Init(sha: [*c]SHA512_CTX) c_int; -pub extern fn SHA512_256_Update(sha: [*c]SHA512_CTX, data: ?*const anyopaque, len: usize) c_int; -pub extern fn SHA512_256_Final(out: [*c]u8, sha: [*c]SHA512_CTX) c_int; -pub extern fn SHA512_256(data: [*c]const u8, len: usize, out: [*c]u8) [*c]u8; +pub extern fn SHA1_Init(sha: *SHA_CTX) c_int; +pub extern fn SHA1_Update(sha: *SHA_CTX, data: ?*const anyopaque, len: usize) c_int; +pub extern fn SHA1_Final(out: [*]u8, sha: *SHA_CTX) c_int; +pub extern fn SHA1(data: [*]const u8, len: usize, out: [*]u8) [*]u8; +pub extern fn SHA1_Transform(sha: *SHA_CTX, block: [*]const u8) void; +pub extern fn SHA224_Init(sha: *SHA256_CTX) c_int; +pub extern fn SHA224_Update(sha: *SHA256_CTX, data: ?*const anyopaque, len: usize) c_int; +pub extern fn SHA224_Final(out: [*]u8, sha: *SHA256_CTX) c_int; +pub extern fn SHA224(data: [*]const u8, len: usize, out: [*]u8) [*]u8; +pub extern fn SHA256_Init(sha: *SHA256_CTX) c_int; +pub extern fn SHA256_Update(sha: *SHA256_CTX, data: ?*const anyopaque, len: usize) c_int; +pub extern fn SHA256_Final(out: [*]u8, sha: *SHA256_CTX) c_int; +pub extern fn SHA256(data: [*]const u8, len: usize, out: [*]u8) [*]u8; +pub extern fn SHA256_Transform(sha: *SHA256_CTX, block: [*]const u8) void; +pub extern fn SHA256_TransformBlocks(state: [*c]u32, data: [*]const u8, num_blocks: usize) void; +pub extern fn SHA384_Init(sha: *SHA512_CTX) c_int; +pub extern fn SHA384_Update(sha: *SHA512_CTX, data: ?*const anyopaque, len: usize) c_int; +pub extern fn SHA384_Final(out: [*]u8, sha: *SHA512_CTX) c_int; +pub extern fn SHA384(data: [*]const u8, len: usize, out: [*]u8) [*]u8; +pub extern fn SHA512_Init(sha: *SHA512_CTX) c_int; +pub extern fn SHA512_Update(sha: *SHA512_CTX, data: ?*const anyopaque, len: usize) c_int; +pub extern fn SHA512_Final(out: [*]u8, sha: *SHA512_CTX) c_int; +pub extern fn SHA512(data: [*]const u8, len: usize, out: [*]u8) [*]u8; +pub extern fn SHA512_Transform(sha: *SHA512_CTX, block: [*]const u8) void; +pub extern fn SHA512_256_Init(sha: *SHA512_CTX) c_int; +pub extern fn SHA512_256_Update(sha: *SHA512_CTX, data: ?*const anyopaque, len: usize) c_int; +pub extern fn SHA512_256_Final(out: [*]u8, sha: *SHA512_CTX) c_int; +pub extern fn SHA512_256(data: [*]const u8, len: usize, out: [*]u8) [*]u8; pub extern fn X509_ALGOR_new() [*c]X509_ALGOR; pub extern fn X509_ALGOR_free(a: [*c]X509_ALGOR) void; pub extern fn d2i_X509_ALGOR(a: [*c][*c]X509_ALGOR, in: [*c][*c]const u8, len: c_long) [*c]X509_ALGOR; diff --git a/src/fallback.version b/src/fallback.version index b07ec851e..36b21d826 100644 --- a/src/fallback.version +++ b/src/fallback.version @@ -1 +1 @@ -3c32b2da4ba87f18
\ No newline at end of file +871e1d1d6a2e7805
\ No newline at end of file diff --git a/src/io/io_darwin.zig b/src/io/io_darwin.zig index b399ce62f..17cf9cca6 100644 --- a/src/io/io_darwin.zig +++ b/src/io/io_darwin.zig @@ -484,9 +484,10 @@ timeouts: FIFO(Completion) = .{}, completed: FIFO(Completion) = .{}, io_pending: FIFO(Completion) = .{}, last_event_fd: std.atomic.Atomic(u32) = std.atomic.Atomic(u32).init(32), +pending_count: usize = 0, pub fn hasNoWork(this: *IO) bool { - return this.io_inflight == 0 and this.io_pending.peek() == null and this.completed.peek() == null and this.timeouts.peek() == null; + return this.pending_count == 0 and this.io_inflight == 0 and this.io_pending.peek() == null and this.completed.peek() == null and this.timeouts.peek() == null; } pub fn init(_: u12, _: u32) !IO { @@ -523,7 +524,7 @@ pub fn run_for_ns(self: *IO, nanoseconds: u63) !void { }.callback; // Submit a timeout which sets the timed_out value to true to terminate the loop below. - self.timeout( + self.timeoutInternal( *bool, &timed_out, on_timeout, @@ -756,6 +757,21 @@ fn submit( operation_data: anytype, comptime OperationImpl: type, ) void { + submitWithIncrementPending(self, context, callback, completion, operation_tag, operation_data, OperationImpl, true); +} + +fn submitWithIncrementPending( + self: *IO, + context: anytype, + comptime callback: anytype, + completion: *Completion, + comptime operation_tag: std.meta.Tag(Operation), + operation_data: anytype, + comptime OperationImpl: type, + comptime increment_pending: bool, +) void { + if (comptime increment_pending) + self.pending_count += 1; const Context = @TypeOf(context); const onCompleteFn = struct { fn onComplete(io: *IO, _completion: *Completion) void { @@ -778,6 +794,9 @@ fn submit( else => {}, } + if (comptime increment_pending) + io.pending_count -= 1; + // Complete the Completion return callback( @intToPtr(Context, @ptrToInt(_completion.context)), @@ -1334,6 +1353,35 @@ pub fn timeout( ); } +fn timeoutInternal( + self: *IO, + comptime Context: type, + context: Context, + comptime callback: fn ( + context: Context, + completion: *Completion, + result: TimeoutError!void, + ) void, + completion: *Completion, + nanoseconds: u63, +) void { + self.submitWithIncrementPending( + context, + callback, + completion, + .timeout, + .{ + .expires = self.time.monotonic() + nanoseconds, + }, + struct { + fn doOperation(_: anytype) TimeoutError!void { + return; // timeouts don't have errors for now + } + }, + false, + ); +} + pub const WriteError = os.PWriteError; pub fn write( diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig index f0d483e1f..011314644 100644 --- a/src/javascript/jsc/api/bun.zig +++ b/src/javascript/jsc/api/bun.zig @@ -1118,6 +1118,21 @@ pub const Class = NewClass( .rfn = JSC.WebCore.Blob.writeFile, .ts = d.ts{}, }, + .sha1 = .{ + .rfn = JSC.wrapWithHasContainer(Crypto.SHA1, "hash", false, false), + }, + .sha256 = .{ + .rfn = JSC.wrapWithHasContainer(Crypto.SHA256, "hash", false, false), + }, + .sha384 = .{ + .rfn = JSC.wrapWithHasContainer(Crypto.SHA384, "hash", false, false), + }, + .sha512 = .{ + .rfn = JSC.wrapWithHasContainer(Crypto.SHA512, "hash", false, false), + }, + .sha512_256 = .{ + .rfn = JSC.wrapWithHasContainer(Crypto.SHA512_256, "hash", false, false), + }, }, .{ .main = .{ @@ -1174,9 +1189,208 @@ pub const Class = NewClass( .unsafe = .{ .get = getUnsafe, }, + .SHA1 = .{ + .get = Crypto.SHA1.getter, + }, + .SHA256 = .{ + .get = Crypto.SHA256.getter, + }, + .SHA384 = .{ + .get = Crypto.SHA384.getter, + }, + .SHA512 = .{ + .get = Crypto.SHA512.getter, + }, + .SHA512_256 = .{ + .get = Crypto.SHA512_256.getter, + }, }, ); +pub const Crypto = struct { + const Hashers = @import("../../../sha.zig"); + + fn CryptoHasher(comptime Hasher: type, comptime name: [:0]const u8, cached_constructor_name: []const u8) type { + return struct { + hashing: Hasher = Hasher{}, + + pub fn byteLength( + _: void, + _: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, + ) js.JSValueRef { + return JSC.JSValue.jsNumber(@as(u16, Hasher.digest)).asObjectRef(); + } + + pub fn byteLength2( + _: *@This(), + _: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, + ) js.JSValueRef { + return JSC.JSValue.jsNumber(@as(u16, Hasher.digest)).asObjectRef(); + } + + pub const Constructor = JSC.NewConstructor( + @This(), + .{ + .hash = .{ + .rfn = JSC.wrapSync(@This(), "hash"), + }, + .constructor = .{ .rfn = constructor }, + }, + .{ + .byteLength = .{ + .get = byteLength, + }, + }, + ); + + pub const Class = JSC.NewClass( + @This(), + .{ + .name = name, + }, + .{ + .update = .{ + .rfn = JSC.wrapSync(@This(), "update"), + }, + .final = .{ + .rfn = JSC.wrapSync(@This(), "final"), + }, + .finalize = finalize, + }, + .{ + .byteLength = .{ + .get = byteLength2, + }, + }, + ); + + pub fn hash( + globalThis: *JSGlobalObject, + input: JSC.Node.StringOrBuffer, + output: ?JSC.ArrayBuffer, + exception: JSC.C.ExceptionRef, + ) JSC.JSValue { + var output_digest_buf: Hasher.Digest = undefined; + var output_digest_slice: *Hasher.Digest = &output_digest_buf; + if (output) |output_buf| { + var bytes = output_buf.byteSlice(); + if (bytes.len < Hasher.digest) { + JSC.JSError( + bun.default_allocator, + comptime std.fmt.comptimePrint("TypedArray must be at least {d} bytes", .{Hasher.digest}), + .{}, + globalThis.ref(), + exception, + ); + return JSC.JSValue.zero; + } + output_digest_slice = bytes[0..Hasher.digest]; + } + + Hasher.hash(input.slice(), output_digest_slice); + + if (output) |output_buf| { + return output_buf.value; + } else { + var array_buffer_out = JSC.ArrayBuffer.fromBytes(bun.default_allocator.dupe(u8, output_digest_slice) catch unreachable, .Uint8Array); + return array_buffer_out.toJSUnchecked(globalThis.ref(), exception); + } + } + + pub fn constructor( + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSObjectRef { + var this = bun.default_allocator.create(@This()) catch { + JSC.JSError(bun.default_allocator, "Failed to create new object", .{}, ctx, exception); + return null; + }; + + this.* = .{ .hashing = Hasher.init() }; + return @This().Class.make(ctx, this); + } + + pub fn getter( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, + ) js.JSValueRef { + var existing = ctx.ptr().getCachedObject(&ZigString.init(cached_constructor_name)); + if (existing.isEmpty()) { + return ctx.ptr().putCachedObject( + &ZigString.init(cached_constructor_name), + JSC.JSValue.fromRef(@This().Constructor.constructor(ctx)), + ).asObjectRef(); + } + + return existing.asObjectRef(); + } + + pub fn update(this: *@This(), buffer: JSC.Node.StringOrBuffer) JSC.JSValue { + this.hashing.update(buffer.slice()); + return JSC.JSValue.jsUndefined(); + } + + pub fn final(this: *@This(), globalThis: *JSGlobalObject, exception: JSC.C.ExceptionRef, output: ?JSC.ArrayBuffer) JSC.JSValue { + var output_digest_buf: Hasher.Digest = undefined; + var output_digest_slice: *Hasher.Digest = &output_digest_buf; + if (output) |output_buf| { + var bytes = output_buf.byteSlice(); + if (bytes.len < Hasher.digest) { + JSC.JSError( + bun.default_allocator, + comptime std.fmt.comptimePrint("TypedArray must be at least {d} bytes", .{@as(usize, Hasher.digest)}), + .{}, + globalThis.ref(), + exception, + ); + return JSC.JSValue.zero; + } + output_digest_slice = bytes[0..Hasher.digest]; + } else { + output_digest_buf = comptime brk: { + var bytes: Hasher.Digest = undefined; + var i: usize = 0; + while (i < Hasher.digest) { + bytes[i] = 0; + i += 1; + } + break :brk bytes; + }; + } + + this.hashing.final(output_digest_slice); + if (output) |output_buf| { + return output_buf.value; + } else { + var array_buffer_out = JSC.ArrayBuffer.fromBytes(bun.default_allocator.dupe(u8, &output_digest_buf) catch unreachable, .Uint8Array); + return array_buffer_out.toJSUnchecked(globalThis.ref(), exception); + } + } + + pub fn finalize(this: *@This()) void { + VirtualMachine.vm.allocator.destroy(this); + } + }; + } + + pub const SHA1 = CryptoHasher(Hashers.SHA1, "SHA1", "Bun_SHA1"); + pub const SHA256 = CryptoHasher(Hashers.SHA256, "SHA256", "Bun_SHA256"); + pub const SHA384 = CryptoHasher(Hashers.SHA384, "SHA384", "Bun_SHA384"); + pub const SHA512 = CryptoHasher(Hashers.SHA512, "SHA512", "Bun_SHA512"); + pub const SHA512_256 = CryptoHasher(Hashers.SHA512_256, "SHA512_256", "Bun_SHA512_256"); +}; + pub fn serve( _: void, ctx: js.JSContextRef, diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig index 70feecb76..9c07e9b37 100644 --- a/src/javascript/jsc/base.zig +++ b/src/javascript/jsc/base.zig @@ -2248,6 +2248,29 @@ pub const ArrayBuffer = extern struct { return ArrayBuffer{ .offset = 0, .len = @intCast(u32, bytes.len), .byte_len = @intCast(u32, bytes.len), .typed_array_type = typed_array_type, .ptr = bytes.ptr }; } + pub fn toJSUnchecked(this: ArrayBuffer, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.JSValue { + if (this.typed_array_type == .ArrayBuffer) { + return JSC.JSValue.fromRef(JSC.C.JSObjectMakeArrayBufferWithBytesNoCopy( + ctx, + this.ptr, + this.byte_len, + MarkedArrayBuffer_deallocator, + @intToPtr(*anyopaque, @ptrToInt(&bun.default_allocator)), + exception, + )); + } + + return JSC.JSValue.fromRef(JSC.C.JSObjectMakeTypedArrayWithBytesNoCopy( + ctx, + this.typed_array_type.toC(), + this.ptr, + this.byte_len, + MarkedArrayBuffer_deallocator, + @intToPtr(*anyopaque, @ptrToInt(&bun.default_allocator)), + exception, + )); + } + pub fn toJS(this: ArrayBuffer, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.JSValue { if (!this.value.isEmpty()) { return this.value; @@ -2277,26 +2300,7 @@ pub const ArrayBuffer = extern struct { )); } - if (this.typed_array_type == .ArrayBuffer) { - return JSC.JSValue.fromRef(JSC.C.JSObjectMakeArrayBufferWithBytesNoCopy( - ctx, - this.ptr, - this.byte_len, - MarkedArrayBuffer_deallocator, - @intToPtr(*anyopaque, @ptrToInt(&bun.default_allocator)), - exception, - )); - } - - return JSC.JSValue.fromRef(JSC.C.JSObjectMakeTypedArrayWithBytesNoCopy( - ctx, - this.typed_array_type.toC(), - this.ptr, - this.byte_len, - MarkedArrayBuffer_deallocator, - @intToPtr(*anyopaque, @ptrToInt(&bun.default_allocator)), - exception, - )); + return this.toJSUnchecked(ctx, exception); } pub fn toJSWithContext( @@ -2552,6 +2556,11 @@ const Server = JSC.API.Server; const SSLServer = JSC.API.SSLServer; const DebugServer = JSC.API.DebugServer; const DebugSSLServer = JSC.API.DebugSSLServer; +const SHA1 = JSC.API.Bun.Crypto.SHA1; +const SHA256 = JSC.API.Bun.Crypto.SHA256; +const SHA384 = JSC.API.Bun.Crypto.SHA384; +const SHA512 = JSC.API.Bun.Crypto.SHA512; +const SHA512_256 = JSC.API.Bun.Crypto.SHA512_256; pub const JSPrivateDataPtr = TaggedPointerUnion(.{ AttributeIterator, @@ -2589,6 +2598,11 @@ pub const JSPrivateDataPtr = TaggedPointerUnion(.{ TextEncoder, TimeoutTask, Transpiler, + SHA1, + SHA256, + SHA384, + SHA512, + SHA512_256, }); pub inline fn GetJSPrivateData(comptime Type: type, ref: js.JSObjectRef) ?*Type { @@ -2678,9 +2692,9 @@ fn SetterType(comptime Container: type) type { ) bool; } -fn MethodType(comptime Container: type) type { +fn MethodType(comptime Container: type, comptime has_container: bool) type { return fn ( - this: *Container, + this: if (has_container) *Container else void, ctx: js.JSContextRef, thisObject: js.JSObjectRef, target: js.JSObjectRef, @@ -2692,14 +2706,14 @@ fn MethodType(comptime Container: type) type { pub fn wrapSync( comptime Container: type, comptime name: string, -) MethodType(Container) { +) MethodType(Container, true) { return wrap(Container, name, false); } pub fn wrapAsync( comptime Container: type, comptime name: string, -) MethodType(Container) { +) MethodType(Container, true) { return wrap(Container, name, true); } @@ -2707,13 +2721,23 @@ pub fn wrap( comptime Container: type, comptime name: string, comptime maybe_async: bool, -) MethodType(Container) { +) MethodType(Container, true) { + return wrapWithHasContainer(Container, name, maybe_async, true); +} + +pub fn wrapWithHasContainer( + comptime Container: type, + comptime name: string, + comptime maybe_async: bool, + comptime has_container: bool, +) MethodType(Container, has_container) { return struct { const FunctionType = @TypeOf(@field(Container, name)); const FunctionTypeInfo: std.builtin.TypeInfo.Fn = @typeInfo(FunctionType).Fn; + const Args = std.meta.ArgsTuple(FunctionType); pub fn callback( - this: *Container, + this: if (has_container) *Container else void, ctx: js.JSContextRef, _: js.JSObjectRef, thisObject: js.JSObjectRef, @@ -2721,12 +2745,11 @@ pub fn wrap( exception: js.ExceptionRef, ) js.JSObjectRef { var iter = JSC.Node.ArgumentsSlice.from(arguments); - var args: std.meta.ArgsTuple(FunctionType) = undefined; + var args: Args = undefined; comptime var i: usize = 0; inline while (i < FunctionTypeInfo.args.len) : (i += 1) { const ArgType = comptime FunctionTypeInfo.args[i].arg_type.?; - switch (comptime ArgType) { *Container => { args[i] = this; @@ -2734,14 +2757,63 @@ pub fn wrap( *JSC.JSGlobalObject => { args[i] = ctx.ptr(); }, + JSC.Node.StringOrBuffer => { + const arg = iter.nextEat() orelse { + exception.* = JSC.toInvalidArguments("expected string or buffer", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + }; + args[i] = JSC.Node.StringOrBuffer.fromJS(ctx.ptr(), iter.arena.allocator(), arg, exception) orelse { + exception.* = JSC.toInvalidArguments("expected string or buffer", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + }; + }, + ?JSC.Node.StringOrBuffer => { + if (iter.nextEat()) |arg| { + args[i] = JSC.Node.StringOrBuffer.fromJS(ctx.ptr(), iter.arena.allocator(), arg, exception) orelse { + exception.* = JSC.toInvalidArguments("expected string or buffer", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + }; + } else { + args[i] = null; + } + }, + JSC.ArrayBuffer => { + if (iter.nextEat()) |arg| { + args[i] = arg.asArrayBuffer(ctx.ptr()) orelse { + exception.* = JSC.toInvalidArguments("expected TypedArray", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + }; + } else { + exception.* = JSC.toInvalidArguments("expected TypedArray", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + } + }, + ?JSC.ArrayBuffer => { + if (iter.nextEat()) |arg| { + args[i] = arg.asArrayBuffer(ctx.ptr()) orelse { + exception.* = JSC.toInvalidArguments("expected TypedArray", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + }; + } else { + args[i] = null; + } + }, ZigString => { var string_value = iter.protectEatNext() orelse { JSC.throwInvalidArguments("Missing argument", .{}, ctx, exception); + iter.deinit(); return null; }; if (string_value.isUndefinedOrNull()) { JSC.throwInvalidArguments("Expected string", .{}, ctx, exception); + iter.deinit(); return null; } @@ -2759,18 +2831,22 @@ pub fn wrap( *Response => { args[i] = (iter.protectEatNext() orelse { JSC.throwInvalidArguments("Missing Response object", .{}, ctx, exception); + iter.deinit(); return null; }).as(Response) orelse { JSC.throwInvalidArguments("Expected Response object", .{}, ctx, exception); + iter.deinit(); return null; }; }, *Request => { args[i] = (iter.protectEatNext() orelse { JSC.throwInvalidArguments("Missing Request object", .{}, ctx, exception); + iter.deinit(); return null; }).as(Request) orelse { JSC.throwInvalidArguments("Expected Request object", .{}, ctx, exception); + iter.deinit(); return null; }; }, @@ -2778,6 +2854,7 @@ pub fn wrap( args[i] = thisObject; if (!JSValue.fromRef(thisObject).isCell() or !JSValue.fromRef(thisObject).isObject()) { JSC.throwInvalidArguments("Expected object", .{}, ctx, exception); + iter.deinit(); return null; } }, @@ -2787,6 +2864,7 @@ pub fn wrap( JSValue => { const val = iter.protectEatNext() orelse { JSC.throwInvalidArguments("Missing argument", .{}, ctx, exception); + iter.deinit(); return null; }; args[i] = val; @@ -2796,7 +2874,7 @@ pub fn wrap( } var result: JSValue = @call(.{}, @field(Container, name), args); - if (result.isError()) { + if (!result.isEmptyOrUndefinedOrNull() and result.isError()) { exception.* = result.asObjectRef(); iter.deinit(); return null; diff --git a/src/sha.zig b/src/sha.zig new file mode 100644 index 000000000..525392a30 --- /dev/null +++ b/src/sha.zig @@ -0,0 +1,145 @@ +const BoringSSL = @import("boringssl"); +const std = @import("std"); + +fn NewHasher(comptime digest_size: comptime_int, comptime ContextType: type, comptime Full: anytype, comptime Init: anytype, comptime Update: anytype, comptime Final: anytype) type { + return struct { + hasher: ContextType = undefined, + + pub const Digest = [digest_size]u8; + pub const digest: comptime_int = digest_size; + + pub fn init() @This() { + var this: @This() = .{ + .hasher = undefined, + }; + + std.debug.assert(Init(&this.hasher) == 1); + return this; + } + + pub fn hash(bytes: []const u8, out: *Digest) void { + _ = Full(bytes.ptr, bytes.len, out); + } + + pub fn update(this: *@This(), data: []const u8) void { + std.debug.assert(Update(&this.hasher, data.ptr, data.len) == 1); + } + + pub fn final(this: *@This(), out: *Digest) void { + std.debug.assert(Final(out, &this.hasher) == 1); + } + }; +} + +pub const SHA1 = NewHasher( + std.crypto.hash.Sha1.digest_length, + BoringSSL.SHA_CTX, + BoringSSL.SHA1, + BoringSSL.SHA1_Init, + BoringSSL.SHA1_Update, + BoringSSL.SHA1_Final, +); + +pub const SHA512 = NewHasher( + std.crypto.hash.sha2.Sha512.digest_length, + BoringSSL.SHA512_CTX, + BoringSSL.SHA512, + BoringSSL.SHA512_Init, + BoringSSL.SHA512_Update, + BoringSSL.SHA512_Final, +); + +pub const SHA384 = NewHasher( + std.crypto.hash.sha2.Sha384.digest_length, + BoringSSL.SHA512_CTX, + BoringSSL.SHA384, + BoringSSL.SHA384_Init, + BoringSSL.SHA384_Update, + BoringSSL.SHA384_Final, +); + +pub const SHA256 = NewHasher( + std.crypto.hash.sha2.Sha256.digest_length, + BoringSSL.SHA256_CTX, + BoringSSL.SHA256, + BoringSSL.SHA256_Init, + BoringSSL.SHA256_Update, + BoringSSL.SHA256_Final, +); + +pub const SHA512_256 = NewHasher( + std.crypto.hash.sha2.Sha512256.digest_length, + BoringSSL.SHA512_CTX, + BoringSSL.SHA512_256, + BoringSSL.SHA512_256_Init, + BoringSSL.SHA512_256_Update, + BoringSSL.SHA512_256_Final, +); + +pub fn main() anyerror!void { + var file = try std.fs.cwd().openFileZ(std.os.argv[std.os.argv.len - 1], .{}); + var bytes = try file.readToEndAlloc(std.heap.c_allocator, std.math.maxInt(usize)); + + const boring = [_]type{ + SHA1, + SHA512, + SHA384, + SHA256, + SHA512_256, + }; + + const zig = [_]type{ + std.crypto.hash.Sha1, + std.crypto.hash.sha2.Sha512, + std.crypto.hash.sha2.Sha384, + std.crypto.hash.sha2.Sha256, + std.crypto.hash.sha2.Sha512256, + }; + + const labels = [_][]const u8{ + "SHA1", + "SHA512", + "SHA384", + "SHA256", + "SHA512_256", + }; + + inline for (boring) |BoringHasher, i| { + const ZigHasher = zig[i]; + std.debug.print( + comptime labels[i] ++ " - hashing {.3f}:\n", + .{std.fmt.fmtIntSizeBin(bytes.len)}, + ); + var digest1: BoringHasher.Digest = undefined; + var digest2: BoringHasher.Digest = undefined; + var clock1 = try std.time.Timer.start(); + ZigHasher.hash(bytes, &digest1, .{}); + const zig_time = clock1.read(); + + var clock2 = try std.time.Timer.start(); + BoringHasher.hash(bytes, &digest2); + const boring_time = clock2.read(); + + std.debug.print( + " zig: {}\n", + .{std.fmt.fmtDuration(zig_time)}, + ); + std.debug.print( + " boring: {}\n\n", + .{std.fmt.fmtDuration(boring_time)}, + ); + + if (!std.mem.eql(u8, &digest1, &digest2)) { + @panic("\ndigests don't match! for " ++ labels[i]); + } + } +} + +test "sha256" { + const value: []const u8 = "hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world! hello, world!"; + var hash: SHA256.Digest = undefined; + var hash2: SHA256.Digest = undefined; + SHA256.hash(value, &hash); + std.crypto.hash.sha2.Sha256.hash(value, &hash2, .{}); + try std.testing.expectEqual(hash, hash2); +} |