Skip to content

Commit

Permalink
Fix hashing consistency issue (#7757)
Browse files Browse the repository at this point in the history
* Fix color issue in `bun test`

* Fix hashing consistentcy issue

* Fix Windows build failure

* Update types.zig

* Fix build

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
  • Loading branch information
Jarred-Sumner and Jarred-Sumner committed Dec 21, 2023
1 parent 6b549ee commit 65e11c4
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 18 deletions.
4 changes: 2 additions & 2 deletions src/bun.js/api/bun.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2059,7 +2059,7 @@ pub const Crypto = struct {
globalThis.throwValue(instance);
return .zero;
};
return encoding.encodeWithMaxSize(globalThis, len, BoringSSL.EVP_MAX_MD_SIZE, &output_digest_buf);
return encoding.encodeWithMaxSize(globalThis, BoringSSL.EVP_MAX_MD_SIZE, output_digest_buf[0..len]);
}

fn hashToBytes(
Expand Down Expand Up @@ -2257,7 +2257,7 @@ pub const Crypto = struct {

const out = this.evp.final(globalThis.bunVM().rareData().boringEngine(), output_digest_slice);

return encoding.encodeWithMaxSize(globalThis, out.len, BoringSSL.EVP_MAX_MD_SIZE, out);
return encoding.encodeWithMaxSize(globalThis, BoringSSL.EVP_MAX_MD_SIZE, out);
}

pub fn finalize(this: *CryptoHasher) callconv(.C) void {
Expand Down
14 changes: 8 additions & 6 deletions src/bun.js/node/types.zig
Original file line number Diff line number Diff line change
Expand Up @@ -650,16 +650,18 @@ pub const Encoding = enum(u8) {
}
}

pub fn encodeWithMaxSize(encoding: Encoding, globalThis: *JSC.JSGlobalObject, size: usize, comptime max_size: usize, input: []const u8) JSC.JSValue {
pub fn encodeWithMaxSize(encoding: Encoding, globalThis: *JSC.JSGlobalObject, comptime max_size: usize, input: []const u8) JSC.JSValue {
switch (encoding) {
.base64 => {
var base64_buf: [std.base64.standard.Encoder.calcSize(max_size)]u8 = undefined;
const base64 = base64_buf[0..std.base64.standard.Encoder.calcSize(size)];
const result = JSC.ZigString.init(std.base64.standard.Encoder.encode(base64, input)).toValueGC(globalThis);
return result;
var base64_buf: [std.base64.standard.Encoder.calcSize(max_size * 4)]u8 = undefined;
const encoded_len = bun.base64.encode(&base64_buf, input);
const encoded, const bytes = bun.String.createUninitialized(.latin1, encoded_len);
defer encoded.deref();
@memcpy(@constCast(bytes), base64_buf[0..encoded_len]);
return encoded.toJS(globalThis);
},
.base64url => {
var buf: [std.base64.url_safe_no_pad.Encoder.calcSize(max_size)]u8 = undefined;
var buf: [std.base64.url_safe_no_pad.Encoder.calcSize(max_size * 4)]u8 = undefined;
const encoded = std.base64.url_safe_no_pad.Encoder.encode(&buf, input);

return JSC.ZigString.init(buf[0..encoded.len]).toValueGC(globalThis);
Expand Down
29 changes: 19 additions & 10 deletions src/cli/test_command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,24 @@ const Test = TestRunner.Test;

const uws = @import("root").bun.uws;

fn fmtStatusTextLine(comptime status: @Type(.EnumLiteral), comptime emoji: bool) []const u8 {
fn fmtStatusTextLine(comptime status: @Type(.EnumLiteral), comptime emoji_or_color: bool) []const u8 {
comptime {
return switch (emoji) {
// emoji and color might be split into two different options in the future
// some terminals support color, but not emoji.
// For now, they are the same.
return switch (emoji_or_color) {
true => switch (status) {
.pass => Output.prettyFmt("<r><green>✓<r>", true),
.fail => Output.prettyFmt("<r><red>✗<r>", true),
.skip => Output.prettyFmt("<r><yellow>»<d>", true),
.todo => Output.prettyFmt("<r><magenta>✎<r>", true),
.pass => Output.prettyFmt("<r><green>✓<r>", emoji_or_color),
.fail => Output.prettyFmt("<r><red>✗<r>", emoji_or_color),
.skip => Output.prettyFmt("<r><yellow>»<d>", emoji_or_color),
.todo => Output.prettyFmt("<r><magenta>✎<r>", emoji_or_color),
else => @compileError("Invalid status " ++ @tagName(status)),
},
else => switch (status) {
.pass => Output.prettyFmt("<r><green>(pass)<r>", true),
.fail => Output.prettyFmt("<r><red>(fail)<r>", true),
.skip => Output.prettyFmt("<r><yellow>(skip)<d>", true),
.todo => Output.prettyFmt("<r><magenta>(todo)<r>", true),
.pass => Output.prettyFmt("<r><green>(pass)<r>", emoji_or_color),
.fail => Output.prettyFmt("<r><red>(fail)<r>", emoji_or_color),
.skip => Output.prettyFmt("<r><yellow>(skip)<d>", emoji_or_color),
.todo => Output.prettyFmt("<r><magenta>(todo)<r>", emoji_or_color),
else => @compileError("Invalid status " ++ @tagName(status)),
},
};
Expand Down Expand Up @@ -193,7 +196,13 @@ pub const CommandLineReporter = struct {
writeTestStatusLine(.fail, &writer);
printTestLine(label, elapsed_ns, parent, false, writer);

// We must always reset the colors because (skip) will have set them to <d>
if (Output.enable_ansi_colors_stderr) {
writer.writeAll(Output.prettyFmt("<r>", true)) catch unreachable;
}

writer_.writeAll(this.failures_to_repeat_buf.items[initial_length..]) catch unreachable;

Output.flush();

// this.updateDots();
Expand Down
61 changes: 61 additions & 0 deletions test/js/bun/util/bun-cryptohasher.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { test, expect, describe } from "bun:test";

describe("Hash is consistent", () => {
const inputs = [
Buffer.from([
103, 87, 129, 242, 154, 82, 159, 206, 176, 124, 10, 39, 235, 214, 121, 13, 34, 155, 131, 178, 40, 34, 252, 134, 7,
203, 130, 187, 207, 49, 26, 59,
]),
Buffer.from([
68, 19, 111, 163, 85, 179, 103, 138, 17, 70, 173, 22, 247, 232, 100, 158, 148, 251, 79, 194, 31, 231, 126, 131,
16, 192, 96, 246, 28, 170, 255, 138,
]),
Buffer.from([
219, 133, 5, 84, 59, 236, 191, 241, 104, 167, 186, 223, 204, 158, 177, 43, 205, 52, 120, 28, 60, 233, 156, 159,
125, 64, 171, 91, 240, 17, 71, 210,
]),
Buffer.from([
34, 93, 2, 87, 76, 190, 175, 238, 185, 96, 201, 38, 104, 215, 236, 99, 223, 134, 157, 237, 254, 36, 49, 242, 100,
135, 198, 114, 49, 71, 220, 79,
]),
];

for (let algorithm of ["sha1", "sha256", "sha512", "md5"] as const) {
describe(algorithm, () => {
const Class = globalThis.Bun[algorithm.toUpperCase() as "SHA1" | "SHA256" | "SHA512" | "MD5"];
test("base64", () => {
for (let buffer of inputs) {
for (let i = 0; i < 200; i++) {
expect(Bun.CryptoHasher.hash(algorithm, buffer, "base64")).toEqual(
Bun.CryptoHasher.hash(algorithm, buffer, "base64"),
);

const instance1 = new Class();
instance1.update(buffer);
const instance2 = new Class();
instance2.update(buffer);

expect(instance1.digest("base64")).toEqual(instance2.digest("base64"));
}
}
});

test("hex", () => {
for (let buffer of inputs) {
for (let i = 0; i < 200; i++) {
expect(Bun.CryptoHasher.hash(algorithm, buffer, "hex")).toEqual(
Bun.CryptoHasher.hash(algorithm, buffer, "hex"),
);

const instance1 = new Class();
instance1.update(buffer);
const instance2 = new Class();
instance2.update(buffer);

expect(instance1.digest("hex")).toEqual(instance2.digest("hex"));
}
}
});
});
}
});

0 comments on commit 65e11c4

Please sign in to comment.