Skip to content

Commit

Permalink
Implement X509Certificate in node:crypto (#16173)
Browse files Browse the repository at this point in the history
Co-authored-by: Jarred-Sumner <[email protected]>
Co-authored-by: Dylan Conway <[email protected]>
Co-authored-by: dylan-conway <[email protected]>
  • Loading branch information
4 people authored Jan 17, 2025
1 parent 399ec3e commit 7242c1b
Show file tree
Hide file tree
Showing 116 changed files with 15,222 additions and 1,255 deletions.
3 changes: 3 additions & 0 deletions .clangd
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ CompileFlags:

Diagnostics:
UnusedIncludes: None

HeaderInsertion:
IncludeBlocks: Preserve # Do not auto-include headers.
3 changes: 3 additions & 0 deletions packages/bun-types/bun.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ declare module "bun" {
import type { Encoding as CryptoEncoding } from "crypto";
import type { CipherNameAndProtocol, EphemeralKeyInfo, PeerCertificate } from "tls";
import type { Stats } from "node:fs";
import type { X509Certificate } from "node:crypto";
interface Env {
NODE_ENV?: string;
/**
Expand Down Expand Up @@ -5372,6 +5373,7 @@ declare module "bun" {
* socket has been destroyed, `null` will be returned.
*/
getCertificate(): PeerCertificate | object | null;
getX509Certificate(): X509Certificate | undefined;

/**
* Returns an object containing information on the negotiated cipher suite.
Expand Down Expand Up @@ -5410,6 +5412,7 @@ declare module "bun" {
* @return A certificate object.
*/
getPeerCertificate(): PeerCertificate;
getPeerX509Certificate(): X509Certificate;

/**
* See [SSL\_get\_shared\_sigalgs](https://www.openssl.org/docs/man1.1.1/man3/SSL_get_shared_sigalgs.html) for more information.
Expand Down
58 changes: 40 additions & 18 deletions src/bun.js/api/BunObject.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,7 @@ pub const Crypto = struct {
sha512,
@"sha512-224",
@"sha512-256",

@"sha3-224",
@"sha3-256",
@"sha3-384",
Expand All @@ -1139,6 +1140,7 @@ pub const Crypto = struct {
.blake2b512 => BoringSSL.EVP_blake2b512(),
.md4 => BoringSSL.EVP_md4(),
.md5 => BoringSSL.EVP_md5(),
.ripemd160 => BoringSSL.EVP_ripemd160(),
.sha1 => BoringSSL.EVP_sha1(),
.sha224 => BoringSSL.EVP_sha224(),
.sha256 => BoringSSL.EVP_sha256(),
Expand Down Expand Up @@ -1359,28 +1361,37 @@ pub const Crypto = struct {
return globalThis.throwNotEnoughArguments("pbkdf2", 5, arguments.len);
}

if (!arguments[3].isAnyInt()) {
return globalThis.throwInvalidArgumentTypeValue("keylen", "integer", arguments[3]);
if (!arguments[3].isNumber()) {
return globalThis.throwInvalidArgumentTypeValue("keylen", "number", arguments[3]);
}

const length = arguments[3].coerce(i64, globalThis);
const keylen_num = arguments[3].asNumber();

if (std.math.isInf(keylen_num) or std.math.isNan(keylen_num)) {
return globalThis.throwRangeError(keylen_num, .{
.field_name = "keylen",
.msg = "an integer",
});
}

if (!globalThis.hasException() and (length < 0 or length > std.math.maxInt(i32))) {
return globalThis.throwInvalidArguments("keylen must be > 0 and < {d}", .{std.math.maxInt(i32)});
if (keylen_num < 0 or keylen_num > std.math.maxInt(i32)) {
return globalThis.throwRangeError(keylen_num, .{ .field_name = "keylen", .min = 0, .max = std.math.maxInt(i32) });
}

const keylen: i32 = @intFromFloat(keylen_num);

if (globalThis.hasException()) {
return error.JSError;
}

if (!arguments[2].isAnyInt()) {
return globalThis.throwInvalidArgumentTypeValue("iteration count", "integer", arguments[2]);
return globalThis.throwInvalidArgumentTypeValue("iterations", "number", arguments[2]);
}

const iteration_count = arguments[2].coerce(i64, globalThis);

if (!globalThis.hasException() and (iteration_count < 1 or iteration_count > std.math.maxInt(u32))) {
return globalThis.throwInvalidArguments("iteration count must be >= 1 and <= maxInt", .{});
if (!globalThis.hasException() and (iteration_count < 1 or iteration_count > std.math.maxInt(i32))) {
return globalThis.throwRangeError(iteration_count, .{ .field_name = "iterations", .min = 1, .max = std.math.maxInt(i32) + 1 });
}

if (globalThis.hasException()) {
Expand All @@ -1389,23 +1400,28 @@ pub const Crypto = struct {

const algorithm = brk: {
if (!arguments[4].isString()) {
return globalThis.throwInvalidArgumentTypeValue("algorithm", "string", arguments[4]);
return globalThis.throwInvalidArgumentTypeValue("digest", "string", arguments[4]);
}

break :brk EVP.Algorithm.map.fromJSCaseInsensitive(globalThis, arguments[4]) orelse {
if (!globalThis.hasException()) {
const slice = arguments[4].toSlice(globalThis, bun.default_allocator);
defer slice.deinit();
const name = slice.slice();
return globalThis.ERR_CRYPTO_INVALID_DIGEST("Unsupported algorithm \"{s}\"", .{name}).throw();
invalid: {
switch (EVP.Algorithm.map.fromJSCaseInsensitive(globalThis, arguments[4]) orelse break :invalid) {
.shake128, .shake256, .@"sha3-224", .@"sha3-256", .@"sha3-384", .@"sha3-512" => break :invalid,
else => |alg| break :brk alg,
}
return error.JSError;
};
}

if (!globalThis.hasException()) {
const slice = arguments[4].toSlice(globalThis, bun.default_allocator);
defer slice.deinit();
const name = slice.slice();
return globalThis.ERR_CRYPTO_INVALID_DIGEST("Invalid digest: {s}", .{name}).throw();
}
return error.JSError;
};

var out = PBKDF2{
.iteration_count = @intCast(iteration_count),
.length = @truncate(length),
.length = keylen,
.algorithm = algorithm,
};
defer {
Expand Down Expand Up @@ -1436,6 +1452,12 @@ pub const Crypto = struct {
return globalThis.throwInvalidArguments("password is too long", .{});
}

if (is_async) {
if (!arguments[5].isFunction()) {
return globalThis.throwInvalidArgumentTypeValue("callback", "function", arguments[5]);
}
}

return out;
}
};
Expand Down
33 changes: 33 additions & 0 deletions src/bun.js/api/bun/socket.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3218,6 +3218,39 @@ fn NewSocket(comptime ssl: bool) type {
return JSValue.jsUndefined();
}

pub fn getPeerX509Certificate(
this: *This,
globalObject: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) bun.JSError!JSValue {
if (comptime ssl == false) {
return JSValue.jsUndefined();
}
const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined();
const cert = BoringSSL.SSL_get_peer_certificate(ssl_ptr);
if (cert) |x509| {
return X509.toJSObject(x509, globalObject);
}
return JSValue.jsUndefined();
}

pub fn getX509Certificate(
this: *This,
globalObject: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) bun.JSError!JSValue {
if (comptime ssl == false) {
return JSValue.jsUndefined();
}

const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined();
const cert = BoringSSL.SSL_get_certificate(ssl_ptr);
if (cert) |x509| {
return X509.toJSObject(x509.ref(), globalObject);
}
return JSValue.jsUndefined();
}

pub fn getServername(
this: *This,
globalObject: *JSC.JSGlobalObject,
Expand Down
Loading

0 comments on commit 7242c1b

Please sign in to comment.