1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-23 15:49:44 -05:00

fix(ext/node): add X509Certificate (#18625)

Towards #18455
This commit is contained in:
Divy Srivastava 2023-04-07 22:54:16 +05:30 committed by GitHub
parent 5d9172467e
commit a0dd0cbcb9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 626 additions and 40 deletions

128
Cargo.lock generated
View file

@ -109,6 +109,45 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "asn1-rs"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0"
dependencies = [
"asn1-rs-derive",
"asn1-rs-impl",
"displaydoc",
"nom",
"num-traits",
"rusticata-macros",
"thiserror",
"time",
]
[[package]]
name = "asn1-rs-derive"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c"
dependencies = [
"proc-macro2 1.0.56",
"quote 1.0.26",
"syn 1.0.109",
"synstructure",
]
[[package]]
name = "asn1-rs-impl"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed"
dependencies = [
"proc-macro2 1.0.56",
"quote 1.0.26",
"syn 1.0.109",
]
[[package]]
name = "ast_node"
version = "0.8.6"
@ -1108,6 +1147,7 @@ version = "0.34.0"
dependencies = [
"aes",
"cbc",
"data-encoding",
"deno_core",
"digest 0.10.6",
"ecb",
@ -1135,6 +1175,7 @@ dependencies = [
"signature",
"tokio",
"typenum",
"x509-parser",
]
[[package]]
@ -1339,6 +1380,20 @@ dependencies = [
"zeroize",
]
[[package]]
name = "der-parser"
version = "8.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e"
dependencies = [
"asn1-rs",
"displaydoc",
"nom",
"num-bigint",
"num-traits",
"rusticata-macros",
]
[[package]]
name = "derive_more"
version = "0.99.17"
@ -1388,6 +1443,17 @@ dependencies = [
"subtle",
]
[[package]]
name = "displaydoc"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
dependencies = [
"proc-macro2 1.0.56",
"quote 1.0.26",
"syn 1.0.109",
]
[[package]]
name = "dissimilar"
version = "1.0.4"
@ -2722,6 +2788,12 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.5.4"
@ -2812,6 +2884,16 @@ dependencies = [
"memoffset",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "notify"
version = "5.0.0"
@ -2910,6 +2992,15 @@ dependencies = [
"libc",
]
[[package]]
name = "oid-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff"
dependencies = [
"asn1-rs",
]
[[package]]
name = "once_cell"
version = "1.17.1"
@ -3601,6 +3692,15 @@ dependencies = [
"semver 1.0.14",
]
[[package]]
name = "rusticata-macros"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
dependencies = [
"nom",
]
[[package]]
name = "rustix"
version = "0.36.9"
@ -4745,8 +4845,10 @@ version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
dependencies = [
"itoa",
"serde",
"time-core",
"time-macros",
]
[[package]]
@ -4755,6 +4857,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
name = "time-macros"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
dependencies = [
"time-core",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
@ -5670,6 +5781,23 @@ dependencies = [
"zeroize",
]
[[package]]
name = "x509-parser"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab0c2f54ae1d92f4fcb99c0b7ccf0b1e3451cbd395e5f115ccbdbcb18d4f634"
dependencies = [
"asn1-rs",
"data-encoding",
"der-parser",
"lazy_static",
"nom",
"oid-registry",
"rusticata-macros",
"thiserror",
"time",
]
[[package]]
name = "xattr"
version = "0.2.3"

View file

@ -232,6 +232,7 @@
"test-crypto-hmac.js",
"test-crypto-prime.js",
"test-crypto-secret-keygen.js",
"test-crypto-x509.js",
"test-dgram-close-during-bind.js",
"test-dgram-close-signal.js",
"test-diagnostics-channel-has-subscribers.js",

View file

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDlDCCAnygAwIBAgIUSrFsjf1qfQ0t/KvfnEsOksatAikwDQYJKoZIhvcNAQEL
BQAwejELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjEPMA0G
A1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMQwwCgYDVQQDDANjYTExIDAe
BgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMCAXDTIyMDkwMzIxNDAzN1oY
DzIyOTYwNjE3MjE0MDM3WjB6MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExCzAJ
BgNVBAcMAlNGMQ8wDQYDVQQKDAZKb3llbnQxEDAOBgNVBAsMB05vZGUuanMxDDAK
BgNVBAMMA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNvf4OGGep+ak+4DNjbuNgy0S/
AZPxahEFp4gpbcvsi9YLOPZ31qpilQeQf7d27scIZ02Qx1YBAzljxELB8H/ZxuYS
cQK0s+DNP22xhmgwMWznO7TezkHP5ujN2UkbfbUpfUxGFgncXeZf9wR7yFWppeHi
RWNBOgsvY7sTrS12kXjWGjqntF7xcEDHc7h+KyF6ZjVJZJCnP6pJEQ+rUjd51eCZ
Xt4WjowLnQiCS1VKzXiP83a++Ma1BKKkUitTR112/Uwd5eGoiByhmLzb/BhxnHJN
07GXjhlMItZRm/jfbZsx1mwnNOO3tx4r08l+DaqkinIadvazs+1ugCaKQn8xAgMB
AAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFqG0RXURDam
56x5accdg9sY5zEGP5VQhkK3ZDc2NyNNa25rwvrjCpO+e0OSwKAmm4aX6iIf2woY
wF2f9swWYzxn9CG4fDlUA8itwlnHxupeL4fGMTYb72vf31plUXyBySRsTwHwBloc
F7KvAZpYYKN9EMH1S/267By6H2I33BT/Ethv//n8dSfmuCurR1kYRaiOC4PVeyFk
B3sj8TtolrN0y/nToWUhmKiaVFnDx3odQ00yhmxR3t21iB7yDkko6D8Vf2dVC4j/
YYBVprXGlTP/hiYRLDoP20xKOYznx5cvHPJ9p+lVcOZUJsJj/Iy750+2n5UiBmXt
lz88C25ucKA=
-----END CERTIFICATE-----

View file

@ -0,0 +1,109 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const {
X509Certificate,
} = require('crypto');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const { readFileSync } = require('fs');
const cert = readFileSync(fixtures.path('keys', 'agent1-cert.pem'));
const ca = readFileSync(fixtures.path('keys', 'ca1-cert.pem'));
[1, {}, false, null].forEach((i) => {
assert.throws(() => new X509Certificate(i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
const subjectCheck = `C=US
ST=CA
L=SF
O=Joyent
OU=Node.js
CN=agent1
Email=ry@tinyclouds.org`;
const issuerCheck = `C=US
ST=CA
L=SF
O=Joyent
OU=Node.js
CN=ca1
Email=ry@tinyclouds.org`;
let infoAccessCheck = `OCSP - URI:http://ocsp.nodejs.org/
CA Issuers - URI:http://ca.nodejs.org/ca.cert`;
if (!common.hasOpenSSL3)
infoAccessCheck += '\n';
const der = Buffer.from(
'308203e8308202d0a0030201020214147d36c1c2f74206de9fab5f2226d78adb00a42630' +
'0d06092a864886f70d01010b0500307a310b3009060355040613025553310b3009060355' +
'04080c024341310b300906035504070c025346310f300d060355040a0c064a6f79656e74' +
'3110300e060355040b0c074e6f64652e6a73310c300a06035504030c036361313120301e' +
'06092a864886f70d010901161172794074696e79636c6f7564732e6f72673020170d3232' +
'303930333231343033375a180f32323936303631373231343033375a307d310b30090603' +
'55040613025553310b300906035504080c024341310b300906035504070c025346310f30' +
'0d060355040a0c064a6f79656e743110300e060355040b0c074e6f64652e6a73310f300d' +
'06035504030c066167656e74313120301e06092a864886f70d010901161172794074696e' +
'79636c6f7564732e6f726730820122300d06092a864886f70d01010105000382010f0030' +
'82010a0282010100d456320afb20d3827093dc2c4284ed04dfbabd56e1ddae529e28b790' +
'cd4256db273349f3735ffd337c7a6363ecca5a27b7f73dc7089a96c6d886db0c62388f1c' +
'dd6a963afcd599d5800e587a11f908960f84ed50ba25a28303ecda6e684fbe7baedc9ce8' +
'801327b1697af25097cee3f175e400984c0db6a8eb87be03b4cf94774ba56fffc8c63c68' +
'd6adeb60abbe69a7b14ab6a6b9e7baa89b5adab8eb07897c07f6d4fa3d660dff574107d2' +
'8e8f63467a788624c574197693e959cea1362ffae1bba10c8c0d88840abfef103631b2e8' +
'f5c39b5548a7ea57e8a39f89291813f45a76c448033a2b7ed8403f4baa147cf35e2d2554' +
'aa65ce49695797095bf4dc6b0203010001a361305f305d06082b06010505070101045130' +
'4f302306082b060105050730018617687474703a2f2f6f6373702e6e6f64656a732e6f72' +
'672f302806082b06010505073002861c687474703a2f2f63612e6e6f64656a732e6f7267' +
'2f63612e63657274300d06092a864886f70d01010b05000382010100c3349810632ccb7d' +
'a585de3ed51e34ed154f0f7215608cf2701c00eda444dc2427072c8aca4da6472c1d9e68' +
'f177f99a90a8b5dbf3884586d61cb1c14ea7016c8d38b70d1b46b42947db30edc1e9961e' +
'd46c0f0e35da427bfbe52900771817e733b371adf19e12137235141a34347db0dfc05579' +
'8b1f269f3bdf5e30ce35d1339d56bb3c570de9096215433047f87ca42447b44e7e6b5d0e' +
'48f7894ab186f85b6b1a74561b520952fea888617f32f582afce1111581cd63efcc68986' +
'00d248bb684dedb9c3d6710c38de9e9bc21f9c3394b729d5f707d64ea890603e5989f8fa' +
'59c19ad1a00732e7adc851b89487cc00799dde068aa64b3b8fd976e8bc113ef2',
'hex');
{
const x509 = new X509Certificate(cert);
assert(!x509.ca);
assert.strictEqual(x509.subject, subjectCheck);
assert.strictEqual(x509.subjectAltName, undefined);
assert.strictEqual(x509.issuer, issuerCheck);
assert.strictEqual(x509.validFrom, 'Sep 3 21:40:37 2022 +00:00');
assert.strictEqual(x509.validTo, 'Jun 17 21:40:37 2296 +00:00');
assert.strictEqual(
x509.fingerprint,
'8B:89:16:C4:99:87:D2:13:1A:64:94:36:38:A5:32:01:F0:95:3B:53');
assert.strictEqual(
x509.fingerprint256,
'2C:62:59:16:91:89:AB:90:6A:3E:98:88:A6:D3:C5:58:58:6C:AE:FF:9C:33:' +
'22:7C:B6:77:D3:34:E7:53:4B:05'
);
assert.strictEqual(
x509.fingerprint512,
'0B:6F:D0:4D:6B:22:53:99:66:62:51:2D:2C:96:F2:58:3F:95:1C:CC:4C:44:' +
'9D:B5:59:AA:AD:A8:F6:2A:24:8A:BB:06:A5:26:42:52:30:A3:37:61:30:A9:' +
'5A:42:63:E0:21:2F:D6:70:63:07:96:6F:27:A7:78:12:08:02:7A:8B'
);
assert.strictEqual(x509.keyUsage, undefined);
assert.strictEqual(x509.serialNumber, '147D36C1C2F74206DE9FAB5F2226D78ADB00A426');
assert.strictEqual(x509.checkEmail('ry@tinyclouds.org'), 'ry@tinyclouds.org');
assert.strictEqual(x509.checkEmail('sally@example.com'), undefined);
}

View file

@ -16,6 +16,7 @@ path = "lib.rs"
[dependencies]
aes.workspace = true
cbc.workspace = true
data-encoding = "2.3.3"
deno_core.workspace = true
digest = { version = "0.10.5", features = ["core-api", "std"] }
ecb.workspace = true
@ -43,3 +44,4 @@ sha3 = "0.10.5"
signature.workspace = true
tokio.workspace = true
typenum = "1.15.0"
x509-parser = "0.15.0"

View file

@ -23,6 +23,7 @@ use rsa::RsaPublicKey;
mod cipher;
mod digest;
mod primes;
pub mod x509;
#[op]
pub fn op_node_check_prime(num: serde_v8::BigInt, checks: usize) -> bool {

315
ext/node/crypto/x509.rs Normal file
View file

@ -0,0 +1,315 @@
use deno_core::error::bad_resource_id;
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use std::borrow::Cow;
use x509_parser::der_parser::asn1_rs::Any;
use x509_parser::der_parser::asn1_rs::Tag;
use x509_parser::der_parser::oid::Oid;
use x509_parser::extensions;
use x509_parser::pem;
use x509_parser::prelude::*;
use digest::Digest;
struct Certificate {
_buf: Vec<u8>,
pem: Option<pem::Pem>,
cert: X509Certificate<'static>,
}
impl Certificate {
fn fingerprint<D: Digest>(&self) -> Option<String> {
self.pem.as_ref().map(|pem| {
let mut hasher = D::new();
hasher.update(&pem.contents);
let bytes = hasher.finalize();
// OpenSSL returns colon separated upper case hex values.
let mut hex = String::with_capacity(bytes.len() * 2);
for byte in bytes {
hex.push_str(&format!("{:02X}:", byte));
}
hex.pop();
hex
})
}
}
impl std::ops::Deref for Certificate {
type Target = X509Certificate<'static>;
fn deref(&self) -> &Self::Target {
&self.cert
}
}
impl Resource for Certificate {
fn name(&self) -> Cow<str> {
"x509Certificate".into()
}
}
#[op]
pub fn op_node_x509_parse(
state: &mut OpState,
buf: &[u8],
) -> Result<u32, AnyError> {
let pem = match pem::parse_x509_pem(buf) {
Ok((_, pem)) => Some(pem),
Err(_) => None,
};
let cert = pem
.as_ref()
.map(|pem| pem.parse_x509())
.unwrap_or_else(|| X509Certificate::from_der(buf).map(|(_, cert)| cert))?;
let cert = Certificate {
_buf: buf.to_vec(),
// SAFETY: Extending the lifetime of the certificate. Backing buffer is
// owned by the resource.
cert: unsafe { std::mem::transmute(cert) },
pem,
};
let rid = state.resource_table.add(cert);
Ok(rid)
}
#[op]
pub fn op_node_x509_ca(
state: &mut OpState,
rid: u32,
) -> Result<bool, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
Ok(cert.is_ca())
}
#[op]
pub fn op_node_x509_check_email(
state: &mut OpState,
rid: u32,
email: &str,
) -> Result<bool, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
let subject = cert.subject();
if subject
.iter_email()
.any(|e| e.as_str().unwrap_or("") == email)
{
return Ok(true);
}
let subject_alt = cert
.extensions()
.iter()
.find(|e| e.oid == x509_parser::oid_registry::OID_X509_EXT_SUBJECT_ALT_NAME)
.and_then(|e| match e.parsed_extension() {
extensions::ParsedExtension::SubjectAlternativeName(s) => Some(s),
_ => None,
});
if let Some(subject_alt) = subject_alt {
for name in &subject_alt.general_names {
dbg!(name);
if let extensions::GeneralName::RFC822Name(n) = name {
if *n == email {
return Ok(true);
}
}
}
}
Ok(false)
}
#[op]
pub fn op_node_x509_fingerprint(
state: &mut OpState,
rid: u32,
) -> Result<Option<String>, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
Ok(cert.fingerprint::<sha1::Sha1>())
}
#[op]
pub fn op_node_x509_fingerprint256(
state: &mut OpState,
rid: u32,
) -> Result<Option<String>, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
Ok(cert.fingerprint::<sha2::Sha256>())
}
#[op]
pub fn op_node_x509_fingerprint512(
state: &mut OpState,
rid: u32,
) -> Result<Option<String>, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
Ok(cert.fingerprint::<sha2::Sha512>())
}
#[op]
pub fn op_node_x509_get_issuer(
state: &mut OpState,
rid: u32,
) -> Result<String, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
Ok(x509name_to_string(cert.issuer(), oid_registry())?)
}
#[op]
pub fn op_node_x509_get_subject(
state: &mut OpState,
rid: u32,
) -> Result<String, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
Ok(x509name_to_string(cert.subject(), oid_registry())?)
}
// Attempt to convert attribute to string. If type is not a string, return value is the hex
// encoding of the attribute value
fn attribute_value_to_string(
attr: &Any,
_attr_type: &Oid,
) -> Result<String, X509Error> {
// TODO: replace this with helper function, when it is added to asn1-rs
match attr.tag() {
Tag::NumericString
| Tag::BmpString
| Tag::VisibleString
| Tag::PrintableString
| Tag::GeneralString
| Tag::ObjectDescriptor
| Tag::GraphicString
| Tag::T61String
| Tag::VideotexString
| Tag::Utf8String
| Tag::Ia5String => {
let s = core::str::from_utf8(attr.data)
.map_err(|_| X509Error::InvalidAttributes)?;
Ok(s.to_owned())
}
_ => {
// type is not a string, get slice and convert it to base64
Ok(data_encoding::HEXUPPER.encode(attr.as_bytes()))
}
}
}
fn x509name_to_string(
name: &X509Name,
oid_registry: &oid_registry::OidRegistry,
) -> Result<String, x509_parser::error::X509Error> {
name.iter_rdn().fold(Ok(String::new()), |acc, rdn| {
acc.and_then(|mut _vec| {
rdn
.iter()
.fold(Ok(String::new()), |acc2, attr| {
acc2.and_then(|mut _vec2| {
let val_str =
attribute_value_to_string(attr.attr_value(), attr.attr_type())?;
// look ABBREV, and if not found, use shortname
let abbrev = match oid2abbrev(attr.attr_type(), oid_registry) {
Ok(s) => String::from(s),
_ => format!("{:?}", attr.attr_type()),
};
let rdn = format!("{}={}", abbrev, val_str);
match _vec2.len() {
0 => Ok(rdn),
_ => Ok(_vec2 + " + " + &rdn),
}
})
})
.map(|v| match _vec.len() {
0 => v,
_ => _vec + "\n" + &v,
})
})
})
}
#[op]
pub fn op_node_x509_get_valid_from(
state: &mut OpState,
rid: u32,
) -> Result<String, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
Ok(cert.validity().not_before.to_string())
}
#[op]
pub fn op_node_x509_get_valid_to(
state: &mut OpState,
rid: u32,
) -> Result<String, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
Ok(cert.validity().not_after.to_string())
}
#[op]
pub fn op_node_x509_get_serial_number(
state: &mut OpState,
rid: u32,
) -> Result<String, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
let mut s = cert.serial.to_str_radix(16);
s.make_ascii_uppercase();
Ok(s)
}
#[op]
pub fn op_node_x509_key_usage(
state: &mut OpState,
rid: u32,
) -> Result<u16, AnyError> {
let cert = state
.resource_table
.get::<Certificate>(rid)
.or_else(|_| Err(bad_resource_id()))?;
let key_usage = cert
.extensions()
.iter()
.find(|e| e.oid == x509_parser::oid_registry::OID_X509_EXT_KEY_USAGE)
.and_then(|e| match e.parsed_extension() {
extensions::ParsedExtension::KeyUsage(k) => Some(k),
_ => None,
});
Ok(key_usage.map(|k| k.flags).unwrap_or(0))
}

View file

@ -194,6 +194,18 @@ deno_core::extension!(deno_node,
crypto::op_node_generate_secret,
crypto::op_node_generate_secret_async,
crypto::op_node_sign,
crypto::x509::op_node_x509_parse,
crypto::x509::op_node_x509_ca,
crypto::x509::op_node_x509_check_email,
crypto::x509::op_node_x509_fingerprint,
crypto::x509::op_node_x509_fingerprint256,
crypto::x509::op_node_x509_fingerprint512,
crypto::x509::op_node_x509_get_issuer,
crypto::x509::op_node_x509_get_subject,
crypto::x509::op_node_x509_get_valid_from,
crypto::x509::op_node_x509_get_valid_to,
crypto::x509::op_node_x509_get_serial_number,
crypto::x509::op_node_x509_key_usage,
winerror::op_node_sys_to_uv_error,
v8::op_v8_cached_data_version_tag,
v8::op_v8_get_heap_statistics,

View file

@ -5,9 +5,12 @@ import { KeyObject } from "ext:deno_node/internal/crypto/keys.ts";
import { Buffer } from "ext:deno_node/buffer.ts";
import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts";
import { isArrayBufferView } from "ext:deno_node/internal/util/types.ts";
import { validateString } from "ext:deno_node/internal/validators.mjs";
import { notImplemented } from "ext:deno_node/_utils.ts";
import { BinaryLike } from "ext:deno_node/internal/crypto/types.ts";
const { ops } = globalThis.__bootstrap.core;
// deno-lint-ignore no-explicit-any
export type PeerCertificate = any;
@ -35,6 +38,8 @@ export interface X509CheckOptions {
}
export class X509Certificate {
#handle: number;
constructor(buffer: BinaryLike) {
if (typeof buffer === "string") {
buffer = Buffer.from(buffer);
@ -48,20 +53,21 @@ export class X509Certificate {
);
}
notImplemented("crypto.X509Certificate");
this.#handle = ops.op_node_x509_parse(buffer);
}
get ca(): boolean {
notImplemented("crypto.X509Certificate.prototype.ca");
return false;
return ops.op_node_x509_ca(this.#handle);
}
checkEmail(
_email: string,
email: string,
_options?: Pick<X509CheckOptions, "subject">,
): string | undefined {
notImplemented("crypto.X509Certificate.prototype.checkEmail");
validateString(email, "email");
if (ops.op_node_x509_check_email(this.#handle, email)) {
return email;
}
}
checkHost(_name: string, _options?: X509CheckOptions): string | undefined {
@ -81,21 +87,15 @@ export class X509Certificate {
}
get fingerprint(): string {
notImplemented("crypto.X509Certificate.prototype.fingerprint");
return "";
return ops.op_node_x509_fingerprint(this.#handle);
}
get fingerprint256(): string {
notImplemented("crypto.X509Certificate.prototype.fingerprint256");
return "";
return ops.op_node_x509_fingerprint256(this.#handle);
}
get fingerprint512(): string {
notImplemented("crypto.X509Certificate.prototype.fingerprint512");
return "";
return ops.op_node_x509_fingerprint512(this.#handle);
}
get infoAccess(): string | undefined {
@ -105,21 +105,27 @@ export class X509Certificate {
}
get issuer(): string {
notImplemented("crypto.X509Certificate.prototype.issuer");
return "";
return ops.op_node_x509_get_issuer(this.#handle);
}
get issuerCertificate(): X509Certificate | undefined {
notImplemented("crypto.X509Certificate.prototype.issuerCertificate");
return {} as X509Certificate;
return undefined;
}
get keyUsage(): string[] {
notImplemented("crypto.X509Certificate.prototype.keyUsage");
return [];
get keyUsage(): string[] | undefined {
const flags = ops.op_node_x509_key_usage(this.#handle);
if (flags === 0) return undefined;
const result: string[] = [];
if (flags & 0x01) result.push("DigitalSignature");
if (flags >> 1 & 0x01) result.push("NonRepudiation");
if (flags >> 2 & 0x01) result.push("KeyEncipherment");
if (flags >> 3 & 0x01) result.push("DataEncipherment");
if (flags >> 4 & 0x01) result.push("KeyAgreement");
if (flags >> 5 & 0x01) result.push("KeyCertSign");
if (flags >> 6 & 0x01) result.push("CRLSign");
if (flags >> 7 & 0x01) result.push("EncipherOnly");
if (flags >> 8 & 0x01) result.push("DecipherOnly");
return result;
}
get publicKey(): KeyObject {
@ -135,21 +141,15 @@ export class X509Certificate {
}
get serialNumber(): string {
notImplemented("crypto.X509Certificate.prototype.serialNumber");
return "";
return ops.op_node_x509_get_serial_number(this.#handle);
}
get subject(): string {
notImplemented("crypto.X509Certificate.prototype.subject");
return "";
return ops.op_node_x509_get_subject(this.#handle);
}
get subjectAltName(): string | undefined {
notImplemented("crypto.X509Certificate.prototype.subjectAltName");
return "";
return undefined;
}
toJSON(): string {
@ -165,15 +165,11 @@ export class X509Certificate {
}
get validFrom(): string {
notImplemented("crypto.X509Certificate.prototype.validFrom");
return "";
return ops.op_node_x509_get_valid_from(this.#handle);
}
get validTo(): string {
notImplemented("crypto.X509Certificate.prototype.validTo");
return "";
return ops.op_node_x509_get_valid_to(this.#handle);
}
verify(_publicKey: KeyObject): boolean {