#include "crypto/crypto_x509.h" #include "base_object-inl.h" #include "crypto/crypto_common.h" #include "crypto/crypto_keys.h" #include "crypto/crypto_util.h" #include "env-inl.h" #include "memory_tracker-inl.h" #include "ncrypto.h" #include "node_errors.h" #include "util-inl.h" #include "v8.h" #include #include namespace node { using ncrypto::BignumPointer; using ncrypto::BIOPointer; using ncrypto::ClearErrorOnReturn; using ncrypto::DataPointer; using ncrypto::Digest; using ncrypto::ECKeyPointer; using ncrypto::SSLPointer; using ncrypto::X509Name; using ncrypto::X509Pointer; using ncrypto::X509View; using v8::Array; using v8::ArrayBuffer; using v8::ArrayBufferView; using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::Context; using v8::Date; using v8::EscapableHandleScope; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Integer; using v8::Isolate; using v8::Local; using v8::LocalVector; using v8::MaybeLocal; using v8::NewStringType; using v8::Object; using v8::String; using v8::Uint32; using v8::Value; namespace crypto { ManagedX509::ManagedX509(X509Pointer&& cert) : cert_(std::move(cert)) {} ManagedX509::ManagedX509(const ManagedX509& that) { *this = that; } ManagedX509& ManagedX509::operator=(const ManagedX509& that) { cert_.reset(that.get()); if (cert_) [[likely]] X509_up_ref(cert_.get()); return *this; } void ManagedX509::MemoryInfo(MemoryTracker* tracker) const { if (!cert_) return; // This is an approximation based on the der encoding size. int size = i2d_X509(cert_.get(), nullptr); tracker->TrackFieldWithSize("cert", size); } namespace { MaybeLocal GetFingerprintDigest(Environment* env, const Digest& method, const X509View& cert) { auto fingerprint = cert.getFingerprint(method); // Returning an empty string indicates that the digest failed for // some reason. if (!fingerprint.has_value()) [[unlikely]] { return Undefined(env->isolate()); } auto& fp = fingerprint.value(); return OneByteString(env->isolate(), fp.data(), fp.length()); } template void Fingerprint(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (GetFingerprintDigest(env, algo, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } MaybeLocal ToV8Value(Environment* env, std::string_view val) { return String::NewFromUtf8( env->isolate(), val.data(), NewStringType::kNormal, val.size()); } MaybeLocal ToV8Value(Local context, BIOPointer&& bio) { if (!bio) [[unlikely]] return {}; BUF_MEM* mem = bio; Local ret; if (!String::NewFromUtf8(context->GetIsolate(), mem->data, NewStringType::kNormal, mem->length) .ToLocal(&ret)) return {}; return ret; } MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { if (!bio) [[unlikely]] return {}; BUF_MEM* mem = bio; Local ret; if (!String::NewFromUtf8(context->GetIsolate(), mem->data, NewStringType::kNormal, mem->length) .ToLocal(&ret)) return {}; return ret; } MaybeLocal ToBuffer(Environment* env, BIOPointer* bio) { if (bio == nullptr || !*bio) [[unlikely]] return {}; BUF_MEM* mem = *bio; auto backing = ArrayBuffer::NewBackingStore( mem->data, mem->length, [](void*, size_t, void* data) { BIOPointer free_me(static_cast(data)); }, bio->release()); auto ab = ArrayBuffer::New(env->isolate(), std::move(backing)); Local ret; if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&ret)) return {}; return ret; } MaybeLocal GetDer(Environment* env, const X509View& view) { Local ret; auto bio = view.toDER(); if (!bio) [[unlikely]] return Undefined(env->isolate()); if (!ToBuffer(env, &bio).ToLocal(&ret)) { return {}; } return ret; } MaybeLocal GetSubjectAltNameString(Environment* env, const X509View& view) { Local ret; auto bio = view.getSubjectAltName(); if (!bio) [[unlikely]] return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) return {}; return ret; } MaybeLocal GetInfoAccessString(Environment* env, const X509View& view) { Local ret; auto bio = view.getInfoAccess(); if (!bio) [[unlikely]] return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } return ret; } MaybeLocal GetValidFrom(Environment* env, const X509View& view) { Local ret; auto bio = view.getValidFrom(); if (!bio) [[unlikely]] return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } return ret; } MaybeLocal GetValidTo(Environment* env, const X509View& view) { Local ret; auto bio = view.getValidTo(); if (!bio) [[unlikely]] return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } return ret; } MaybeLocal GetValidFromDate(Environment* env, const X509View& view) { int64_t validFromTime = view.getValidFromTime(); return Date::New(env->context(), validFromTime * 1000.); } MaybeLocal GetValidToDate(Environment* env, const X509View& view) { int64_t validToTime = view.getValidToTime(); return Date::New(env->context(), validToTime * 1000.); } MaybeLocal GetSerialNumber(Environment* env, const X509View& view) { if (auto serial = view.getSerialNumber()) { return OneByteString(env->isolate(), static_cast(serial.get())); } return Undefined(env->isolate()); } MaybeLocal GetKeyUsage(Environment* env, const X509View& cert) { LocalVector vec(env->isolate()); bool res = cert.enumUsages([&](std::string_view view) { vec.push_back(OneByteString(env->isolate(), view)); }); if (!res) return Undefined(env->isolate()); return Array::New(env->isolate(), vec.data(), vec.size()); } void Pem(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (ToV8Value(env->context(), cert->view().toPEM()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void Der(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (GetDer(env, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void Subject(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (ToV8Value(env->context(), cert->view().getSubject()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void SubjectAltName(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (GetSubjectAltNameString(env, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void Issuer(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (ToV8Value(env->context(), cert->view().getIssuer()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void InfoAccess(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (GetInfoAccessString(env, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void ValidFrom(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (GetValidFrom(env, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void ValidTo(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (GetValidTo(env, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void ValidFromDate(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (GetValidFromDate(env, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void ValidToDate(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (GetValidToDate(env, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void SerialNumber(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (GetSerialNumber(env, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void PublicKey(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); // TODO(tniessen): consider checking X509_get_pubkey() when the // X509Certificate object is being created. auto result = cert->view().getPublicKey(); if (!result.value) [[unlikely]] { ThrowCryptoError(env, result.error.value_or(0)); return; } auto key_data = KeyObjectData::CreateAsymmetric(kKeyTypePublic, std::move(result.value)); Local ret; if (key_data && KeyObjectHandle::Create(env, key_data).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void KeyUsage(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; if (GetKeyUsage(env, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void CheckCA(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); args.GetReturnValue().Set(cert->view().isCA()); } void CheckIssued(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); CHECK(args[0]->IsObject()); CHECK(X509Certificate::HasInstance(env, args[0].As())); X509Certificate* issuer; ASSIGN_OR_RETURN_UNWRAP(&issuer, args[0]); args.GetReturnValue().Set(cert->view().isIssuedBy(issuer->view())); } void CheckPrivateKey(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); CHECK(args[0]->IsObject()); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args[0]); CHECK_EQ(key->Data().GetKeyType(), kKeyTypePrivate); args.GetReturnValue().Set( cert->view().checkPrivateKey(key->Data().GetAsymmetricKey())); } void CheckPublicKey(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); CHECK(args[0]->IsObject()); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args[0]); // A Public Key can be derived from a private key, so we allow both. CHECK_NE(key->Data().GetKeyType(), kKeyTypeSecret); args.GetReturnValue().Set( cert->view().checkPublicKey(key->Data().GetAsymmetricKey())); } void CheckHost(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); CHECK(args[0]->IsString()); // name CHECK(args[1]->IsUint32()); // flags Utf8Value name(env->isolate(), args[0]); uint32_t flags = args[1].As()->Value(); DataPointer peername; switch (cert->view().checkHost(name.ToStringView(), flags, &peername)) { case X509View::CheckMatch::MATCH: { // Match! Local ret = args[0]; if (peername) { ret = OneByteString(env->isolate(), static_cast(peername.get()), peername.size()); } return args.GetReturnValue().Set(ret); } case X509View::CheckMatch::NO_MATCH: // No Match! return; // No return value is set case X509View::CheckMatch::INVALID_NAME: // Error! return THROW_ERR_INVALID_ARG_VALUE(env, "Invalid name"); default: // Error! return THROW_ERR_CRYPTO_OPERATION_FAILED(env); } } void CheckEmail(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); CHECK(args[0]->IsString()); // name CHECK(args[1]->IsUint32()); // flags Utf8Value name(env->isolate(), args[0]); uint32_t flags = args[1].As()->Value(); switch (cert->view().checkEmail(name.ToStringView(), flags)) { case X509View::CheckMatch::MATCH: // Match! return args.GetReturnValue().Set(args[0]); case X509View::CheckMatch::NO_MATCH: // No Match! return; // No return value is set case X509View::CheckMatch::INVALID_NAME: // Error! return THROW_ERR_INVALID_ARG_VALUE(env, "Invalid name"); default: // Error! return THROW_ERR_CRYPTO_OPERATION_FAILED(env); } } void CheckIP(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); CHECK(args[0]->IsString()); // IP CHECK(args[1]->IsUint32()); // flags Utf8Value name(env->isolate(), args[0]); uint32_t flags = args[1].As()->Value(); switch (cert->view().checkIp(name.ToStringView(), flags)) { case X509View::CheckMatch::MATCH: // Match! return args.GetReturnValue().Set(args[0]); case X509View::CheckMatch::NO_MATCH: // No Match! return; // No return value is set case X509View::CheckMatch::INVALID_NAME: // Error! return THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP"); default: // Error! return THROW_ERR_CRYPTO_OPERATION_FAILED(env); } } void GetIssuerCert(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); auto issuer = cert->getIssuerCert(); if (issuer) args.GetReturnValue().Set(issuer->object()); } void Parse(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsArrayBufferView()); ArrayBufferViewContents buf(args[0].As()); Local cert; auto result = X509Pointer::Parse(ncrypto::Buffer{ .data = buf.data(), .len = buf.length(), }); if (!result.value) [[unlikely]] { return ThrowCryptoError(env, result.error.value_or(0)); } if (X509Certificate::New(env, std::move(result.value)).ToLocal(&cert)) { args.GetReturnValue().Set(cert); } } void ToLegacy(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); ClearErrorOnReturn clear_error_on_return; Local ret; if (cert->toObject(env).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } template bool Set(Environment* env, Local target, Local name, MaybeLocal maybe_value) { Local value; if (!maybe_value.ToLocal(&value)) [[unlikely]] return false; // Undefined is ignored, but still considered successful if (value->IsUndefined()) return true; return !target->Set(env->context(), name, value).IsNothing(); } template bool Set(Environment* env, Local target, uint32_t index, MaybeLocal maybe_value) { Local value; if (!maybe_value.ToLocal(&value)) [[unlikely]] return false; // Undefined is ignored, but still considered successful if (value->IsUndefined()) return true; return !target->Set(env->context(), index, value).IsNothing(); } // Convert an X509_NAME* into a JavaScript object. // Each entry of the name is converted into a property of the object. // The property value may be a single string or an array of strings. static MaybeLocal GetX509NameObject(Environment* env, const X509Name& name) { if (!name) return {}; Local v8_name; Local v8_value; // Note the the resulting object uses a null prototype. Local result = Object::New(env->isolate(), Null(env->isolate()), nullptr, nullptr, 0); if (result.IsEmpty()) return {}; for (auto i : name) { if (!ToV8Value(env, i.first).ToLocal(&v8_name) || !ToV8Value(env, i.second).ToLocal(&v8_value)) { return {}; } // For backward compatibility, we only create arrays if multiple values // exist for the same key. That is not great but there is not much we can // change here without breaking things. Note that this creates nested data // structures, yet still does not allow representing Distinguished Names // accurately. bool multiple; if (!result->Has(env->context(), v8_name).To(&multiple)) { return {}; } if (multiple) { Local accum; if (!result->Get(env->context(), v8_name).ToLocal(&accum)) { return {}; } if (!accum->IsArray()) { Local items[] = { accum, v8_value, }; accum = Array::New(env->isolate(), items, arraysize(items)); if (!Set(env, result, v8_name, accum)) { return {}; } } else { Local array = accum.As(); if (!Set(env, array, array->Length(), v8_value)) { return {}; } } continue; } if (!Set(env, result, v8_name, v8_value)) { return {}; } } return result; } MaybeLocal GetPubKey(Environment* env, const ncrypto::Rsa& rsa) { int size = i2d_RSA_PUBKEY(rsa, nullptr); CHECK_GE(size, 0); auto bs = ArrayBuffer::NewBackingStore( env->isolate(), size, BackingStoreInitializationMode::kUninitialized); auto serialized = reinterpret_cast(bs->Data()); CHECK_GE(i2d_RSA_PUBKEY(rsa, &serialized), 0); auto ab = ArrayBuffer::New(env->isolate(), std::move(bs)); return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local()); } MaybeLocal GetModulusString(Environment* env, const BIGNUM* n) { auto bio = BIOPointer::New(n); if (!bio) [[unlikely]] return {}; return ToV8Value(env->context(), bio); } MaybeLocal GetExponentString(Environment* env, const BIGNUM* e) { uint64_t exponent_word = static_cast(BignumPointer::GetWord(e)); auto bio = BIOPointer::NewMem(); if (!bio) [[unlikely]] return {}; BIO_printf(bio.get(), "0x%" PRIx64, exponent_word); return ToV8Value(env->context(), bio); } MaybeLocal GetECPubKey(Environment* env, const EC_GROUP* group, OSSL3_CONST EC_KEY* ec) { const auto pubkey = ECKeyPointer::GetPublicKey(ec); if (pubkey == nullptr) [[unlikely]] return Undefined(env->isolate()); return ECPointToBuffer(env, group, pubkey, EC_KEY_get_conv_form(ec), nullptr) .FromMaybe(Local()); } MaybeLocal GetECGroupBits(Environment* env, const EC_GROUP* group) { if (group == nullptr) [[unlikely]] return Undefined(env->isolate()); int bits = EC_GROUP_order_bits(group); if (bits <= 0) return Undefined(env->isolate()); return Integer::New(env->isolate(), bits); } template MaybeLocal GetCurveName(Environment* env, const int nid) { std::string_view name = nid2string(nid); return name.size() ? MaybeLocal(OneByteString(env->isolate(), name)) : MaybeLocal(Undefined(env->isolate())); } MaybeLocal X509ToObject(Environment* env, const X509View& cert) { EscapableHandleScope scope(env->isolate()); Local info = Object::New(env->isolate()); if (!Set(env, info, env->subject_string(), GetX509NameObject(env, cert.getSubjectName())) || !Set(env, info, env->issuer_string(), GetX509NameObject(env, cert.getIssuerName())) || !Set(env, info, env->subjectaltname_string(), GetSubjectAltNameString(env, cert)) || !Set(env, info, env->infoaccess_string(), GetInfoAccessString(env, cert)) || !Set(env, info, env->ca_string(), Boolean::New(env->isolate(), cert.isCA()))) [[unlikely]] { return {}; } if (!cert.ifRsa([&](const ncrypto::Rsa& rsa) { auto pub_key = rsa.getPublicKey(); if (!Set(env, info, env->modulus_string(), GetModulusString(env, pub_key.n)) || !Set(env, info, env->bits_string(), Integer::New(env->isolate(), BignumPointer::GetBitCount(pub_key.n))) || !Set(env, info, env->exponent_string(), GetExponentString(env, pub_key.e)) || !Set(env, info, env->pubkey_string(), GetPubKey(env, rsa))) [[unlikely]] { return false; } return true; })) [[unlikely]] { return {}; } if (!cert.ifEc([&](const ncrypto::Ec& ec) { const auto group = ec.getGroup(); if (!Set( env, info, env->bits_string(), GetECGroupBits(env, group)) || !Set( env, info, env->pubkey_string(), GetECPubKey(env, group, ec))) [[unlikely]] { return false; } const int nid = ec.getCurve(); if (nid != 0) [[likely]] { // Curve is well-known, get its OID and NIST nick-name (if it has // one). if (!Set(env, info, env->asn1curve_string(), GetCurveName(env, nid)) || !Set(env, info, env->nistcurve_string(), GetCurveName(env, nid))) [[unlikely]] { return false; } } // Unnamed curves can be described by their mathematical properties, // but aren't used much (at all?) with X.509/TLS. Support later if // needed. return true; })) [[unlikely]] { return {}; } if (!Set( env, info, env->valid_from_string(), GetValidFrom(env, cert)) || !Set(env, info, env->valid_to_string(), GetValidTo(env, cert)) || !Set(env, info, env->fingerprint_string(), GetFingerprintDigest(env, Digest::SHA1, cert)) || !Set(env, info, env->fingerprint256_string(), GetFingerprintDigest(env, Digest::SHA256, cert)) || !Set(env, info, env->fingerprint512_string(), GetFingerprintDigest(env, Digest::SHA512, cert)) || !Set( env, info, env->ext_key_usage_string(), GetKeyUsage(env, cert)) || !Set( env, info, env->serial_number_string(), GetSerialNumber(env, cert)) || !Set(env, info, env->raw_string(), GetDer(env, cert))) [[unlikely]] { return {}; } return scope.Escape(info); } } // namespace Local X509Certificate::GetConstructorTemplate( Environment* env) { Local tmpl = env->x509_constructor_template(); if (tmpl.IsEmpty()) { Isolate* isolate = env->isolate(); tmpl = NewFunctionTemplate(isolate, nullptr); tmpl->InstanceTemplate()->SetInternalFieldCount( BaseObject::kInternalFieldCount); tmpl->SetClassName( FIXED_ONE_BYTE_STRING(env->isolate(), "X509Certificate")); SetProtoMethodNoSideEffect(isolate, tmpl, "subject", Subject); SetProtoMethodNoSideEffect(isolate, tmpl, "subjectAltName", SubjectAltName); SetProtoMethodNoSideEffect(isolate, tmpl, "infoAccess", InfoAccess); SetProtoMethodNoSideEffect(isolate, tmpl, "issuer", Issuer); SetProtoMethodNoSideEffect(isolate, tmpl, "validTo", ValidTo); SetProtoMethodNoSideEffect(isolate, tmpl, "validFrom", ValidFrom); SetProtoMethodNoSideEffect(isolate, tmpl, "validToDate", ValidToDate); SetProtoMethodNoSideEffect(isolate, tmpl, "validFromDate", ValidFromDate); SetProtoMethodNoSideEffect( isolate, tmpl, "fingerprint", Fingerprint); SetProtoMethodNoSideEffect( isolate, tmpl, "fingerprint256", Fingerprint); SetProtoMethodNoSideEffect( isolate, tmpl, "fingerprint512", Fingerprint); SetProtoMethodNoSideEffect(isolate, tmpl, "keyUsage", KeyUsage); SetProtoMethodNoSideEffect(isolate, tmpl, "serialNumber", SerialNumber); SetProtoMethodNoSideEffect(isolate, tmpl, "pem", Pem); SetProtoMethodNoSideEffect(isolate, tmpl, "raw", Der); SetProtoMethodNoSideEffect(isolate, tmpl, "publicKey", PublicKey); SetProtoMethodNoSideEffect(isolate, tmpl, "checkCA", CheckCA); SetProtoMethodNoSideEffect(isolate, tmpl, "checkHost", CheckHost); SetProtoMethodNoSideEffect(isolate, tmpl, "checkEmail", CheckEmail); SetProtoMethodNoSideEffect(isolate, tmpl, "checkIP", CheckIP); SetProtoMethodNoSideEffect(isolate, tmpl, "checkIssued", CheckIssued); SetProtoMethodNoSideEffect( isolate, tmpl, "checkPrivateKey", CheckPrivateKey); SetProtoMethodNoSideEffect(isolate, tmpl, "verify", CheckPublicKey); SetProtoMethodNoSideEffect(isolate, tmpl, "toLegacy", ToLegacy); SetProtoMethodNoSideEffect(isolate, tmpl, "getIssuerCert", GetIssuerCert); env->set_x509_constructor_template(tmpl); } return tmpl; } bool X509Certificate::HasInstance(Environment* env, Local object) { return GetConstructorTemplate(env)->HasInstance(object); } MaybeLocal X509Certificate::New(Environment* env, X509Pointer cert, STACK_OF(X509) * issuer_chain) { std::shared_ptr mcert(new ManagedX509(std::move(cert))); return New(env, std::move(mcert), issuer_chain); } MaybeLocal X509Certificate::New(Environment* env, std::shared_ptr cert, STACK_OF(X509) * issuer_chain) { EscapableHandleScope scope(env->isolate()); Local ctor; if (!GetConstructorTemplate(env)->GetFunction(env->context()).ToLocal(&ctor)) return MaybeLocal(); Local obj; if (!ctor->NewInstance(env->context()).ToLocal(&obj)) return MaybeLocal(); new X509Certificate(env, obj, std::move(cert), issuer_chain); return scope.Escape(obj); } MaybeLocal X509Certificate::GetCert(Environment* env, const SSLPointer& ssl) { auto cert = X509View::From(ssl); if (!cert) [[unlikely]] return {}; return New(env, cert.clone()); } MaybeLocal X509Certificate::GetPeerCert(Environment* env, const SSLPointer& ssl, GetPeerCertificateFlag flag) { ClearErrorOnReturn clear_error_on_return; MaybeLocal maybe_cert; X509Pointer cert; if ((flag & GetPeerCertificateFlag::SERVER) == GetPeerCertificateFlag::SERVER) { cert = X509Pointer::PeerFrom(ssl); } STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(ssl.get()); if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) return MaybeLocal(); if (!cert) [[unlikely]] { cert.reset(sk_X509_value(ssl_certs, 0)); sk_X509_delete(ssl_certs, 0); } return sk_X509_num(ssl_certs) ? New(env, std::move(cert), ssl_certs) : New(env, std::move(cert)); } v8::MaybeLocal X509Certificate::toObject(Environment* env) { return toObject(env, view()); } v8::MaybeLocal X509Certificate::toObject(Environment* env, const X509View& cert) { if (!cert) [[unlikely]] return {}; return X509ToObject(env, cert).FromMaybe(Local()); } X509Certificate::X509Certificate( Environment* env, Local object, std::shared_ptr cert, STACK_OF(X509)* issuer_chain) : BaseObject(env, object), cert_(std::move(cert)) { MakeWeak(); if (issuer_chain != nullptr && sk_X509_num(issuer_chain)) { X509Pointer cert(X509_dup(sk_X509_value(issuer_chain, 0))); sk_X509_delete(issuer_chain, 0); Local obj = sk_X509_num(issuer_chain) ? X509Certificate::New(env, std::move(cert), issuer_chain) .ToLocalChecked() : X509Certificate::New(env, std::move(cert)) .ToLocalChecked(); issuer_cert_.reset(Unwrap(obj)); } } void X509Certificate::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("cert", cert_); } BaseObjectPtr X509Certificate::X509CertificateTransferData::Deserialize( Environment* env, Local context, std::unique_ptr self) { if (context != env->context()) [[unlikely]] { THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env); return {}; } Local handle; if (!X509Certificate::New(env, data_).ToLocal(&handle)) return {}; return BaseObjectPtr( Unwrap(handle.As())); } BaseObject::TransferMode X509Certificate::GetTransferMode() const { return BaseObject::TransferMode::kCloneable; } std::unique_ptr X509Certificate::CloneForMessaging() const { return std::make_unique(cert_); } void X509Certificate::Initialize(Environment* env, Local target) { SetMethod(env->context(), target, "parseX509", Parse); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_NEVER_CHECK_SUBJECT); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_NO_WILDCARDS); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS); } void X509Certificate::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(Parse); registry->Register(Subject); registry->Register(SubjectAltName); registry->Register(InfoAccess); registry->Register(Issuer); registry->Register(ValidTo); registry->Register(ValidFrom); registry->Register(ValidToDate); registry->Register(ValidFromDate); registry->Register(Fingerprint); registry->Register(Fingerprint); registry->Register(Fingerprint); registry->Register(KeyUsage); registry->Register(SerialNumber); registry->Register(Pem); registry->Register(Der); registry->Register(PublicKey); registry->Register(CheckCA); registry->Register(CheckHost); registry->Register(CheckEmail); registry->Register(CheckIP); registry->Register(CheckIssued); registry->Register(CheckPrivateKey); registry->Register(CheckPublicKey); registry->Register(ToLegacy); registry->Register(GetIssuerCert); } } // namespace crypto } // namespace node