/src/node/src/crypto/crypto_common.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "crypto/crypto_common.h" |
2 | | #include "base_object-inl.h" |
3 | | #include "env-inl.h" |
4 | | #include "memory_tracker-inl.h" |
5 | | #include "node.h" |
6 | | #include "node_buffer.h" |
7 | | #include "node_crypto.h" |
8 | | #include "node_internals.h" |
9 | | #include "string_bytes.h" |
10 | | #include "v8.h" |
11 | | |
12 | | #include <openssl/ec.h> |
13 | | #include <openssl/ecdh.h> |
14 | | #include <openssl/evp.h> |
15 | | #include <openssl/pem.h> |
16 | | #include <openssl/x509v3.h> |
17 | | #include <openssl/hmac.h> |
18 | | #include <openssl/rand.h> |
19 | | #include <openssl/pkcs12.h> |
20 | | |
21 | | #include <string> |
22 | | #include <unordered_map> |
23 | | |
24 | | namespace node { |
25 | | |
26 | | using v8::Array; |
27 | | using v8::ArrayBuffer; |
28 | | using v8::BackingStore; |
29 | | using v8::Boolean; |
30 | | using v8::Context; |
31 | | using v8::EscapableHandleScope; |
32 | | using v8::Integer; |
33 | | using v8::Local; |
34 | | using v8::MaybeLocal; |
35 | | using v8::NewStringType; |
36 | | using v8::Object; |
37 | | using v8::String; |
38 | | using v8::Undefined; |
39 | | using v8::Value; |
40 | | |
41 | | namespace crypto { |
42 | | static constexpr int kX509NameFlagsMultiline = |
43 | | ASN1_STRFLGS_ESC_2253 | |
44 | | ASN1_STRFLGS_ESC_CTRL | |
45 | | ASN1_STRFLGS_UTF8_CONVERT | |
46 | | XN_FLAG_SEP_MULTILINE | |
47 | | XN_FLAG_FN_SN; |
48 | | |
49 | | static constexpr int kX509NameFlagsRFC2253WithinUtf8JSON = |
50 | | XN_FLAG_RFC2253 & |
51 | | ~ASN1_STRFLGS_ESC_MSB & |
52 | | ~ASN1_STRFLGS_ESC_CTRL; |
53 | | |
54 | 757 | X509Pointer SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert) { |
55 | 757 | X509_STORE* store = SSL_CTX_get_cert_store(ctx); |
56 | 757 | DeleteFnPtr<X509_STORE_CTX, X509_STORE_CTX_free> store_ctx( |
57 | 757 | X509_STORE_CTX_new()); |
58 | 757 | X509Pointer result; |
59 | 757 | X509* issuer; |
60 | 757 | if (store_ctx.get() != nullptr && |
61 | 757 | X509_STORE_CTX_init(store_ctx.get(), store, nullptr, nullptr) == 1 && |
62 | 757 | X509_STORE_CTX_get1_issuer(&issuer, store_ctx.get(), cert) == 1) { |
63 | 0 | result.reset(issuer); |
64 | 0 | } |
65 | 757 | return result; |
66 | 757 | } |
67 | | |
68 | | void LogSecret( |
69 | | const SSLPointer& ssl, |
70 | | const char* name, |
71 | | const unsigned char* secret, |
72 | 0 | size_t secretlen) { |
73 | 0 | auto keylog_cb = SSL_CTX_get_keylog_callback(SSL_get_SSL_CTX(ssl.get())); |
74 | | // All supported versions of TLS/SSL fix the client random to the same size. |
75 | 0 | constexpr size_t kTlsClientRandomSize = SSL3_RANDOM_SIZE; |
76 | 0 | unsigned char crandom[kTlsClientRandomSize]; |
77 | |
|
78 | 0 | if (keylog_cb == nullptr || |
79 | 0 | SSL_get_client_random(ssl.get(), crandom, kTlsClientRandomSize) != |
80 | 0 | kTlsClientRandomSize) { |
81 | 0 | return; |
82 | 0 | } |
83 | | |
84 | 0 | std::string line = name; |
85 | 0 | line += " " + StringBytes::hex_encode(reinterpret_cast<const char*>(crandom), |
86 | 0 | kTlsClientRandomSize); |
87 | 0 | line += " " + StringBytes::hex_encode( |
88 | 0 | reinterpret_cast<const char*>(secret), secretlen); |
89 | 0 | keylog_cb(ssl.get(), line.c_str()); |
90 | 0 | } |
91 | | |
92 | | MaybeLocal<Value> GetSSLOCSPResponse( |
93 | | Environment* env, |
94 | | SSL* ssl, |
95 | 0 | Local<Value> default_value) { |
96 | 0 | const unsigned char* resp; |
97 | 0 | int len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp); |
98 | 0 | if (resp == nullptr) |
99 | 0 | return default_value; |
100 | | |
101 | 0 | Local<Value> ret; |
102 | 0 | MaybeLocal<Object> maybe_buffer = |
103 | 0 | Buffer::Copy(env, reinterpret_cast<const char*>(resp), len); |
104 | |
|
105 | 0 | if (!maybe_buffer.ToLocal(&ret)) |
106 | 0 | return MaybeLocal<Value>(); |
107 | | |
108 | 0 | return ret; |
109 | 0 | } |
110 | | |
111 | | bool SetTLSSession( |
112 | | const SSLPointer& ssl, |
113 | 0 | const SSLSessionPointer& session) { |
114 | 0 | return session != nullptr && SSL_set_session(ssl.get(), session.get()) == 1; |
115 | 0 | } |
116 | | |
117 | 0 | SSLSessionPointer GetTLSSession(const unsigned char* buf, size_t length) { |
118 | 0 | return SSLSessionPointer(d2i_SSL_SESSION(nullptr, &buf, length)); |
119 | 0 | } |
120 | | |
121 | | long VerifyPeerCertificate( // NOLINT(runtime/int) |
122 | | const SSLPointer& ssl, |
123 | 0 | long def) { // NOLINT(runtime/int) |
124 | 0 | long err = def; // NOLINT(runtime/int) |
125 | 0 | if (X509* peer_cert = SSL_get_peer_certificate(ssl.get())) { |
126 | 0 | X509_free(peer_cert); |
127 | 0 | err = SSL_get_verify_result(ssl.get()); |
128 | 0 | } else { |
129 | 0 | const SSL_CIPHER* curr_cipher = SSL_get_current_cipher(ssl.get()); |
130 | 0 | const SSL_SESSION* sess = SSL_get_session(ssl.get()); |
131 | | // Allow no-cert for PSK authentication in TLS1.2 and lower. |
132 | | // In TLS1.3 check that session was reused because TLS1.3 PSK |
133 | | // looks like session resumption. |
134 | 0 | if (SSL_CIPHER_get_auth_nid(curr_cipher) == NID_auth_psk || |
135 | 0 | (SSL_SESSION_get_protocol_version(sess) == TLS1_3_VERSION && |
136 | 0 | SSL_session_reused(ssl.get()))) { |
137 | 0 | return X509_V_OK; |
138 | 0 | } |
139 | 0 | } |
140 | 0 | return err; |
141 | 0 | } |
142 | | |
143 | | bool UseSNIContext( |
144 | 0 | const SSLPointer& ssl, BaseObjectPtr<SecureContext> context) { |
145 | 0 | SSL_CTX* ctx = context->ctx().get(); |
146 | 0 | X509* x509 = SSL_CTX_get0_certificate(ctx); |
147 | 0 | EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx); |
148 | 0 | STACK_OF(X509)* chain; |
149 | |
|
150 | 0 | int err = SSL_CTX_get0_chain_certs(ctx, &chain); |
151 | 0 | if (err == 1) err = SSL_use_certificate(ssl.get(), x509); |
152 | 0 | if (err == 1) err = SSL_use_PrivateKey(ssl.get(), pkey); |
153 | 0 | if (err == 1 && chain != nullptr) err = SSL_set1_chain(ssl.get(), chain); |
154 | 0 | return err == 1; |
155 | 0 | } |
156 | | |
157 | 0 | const char* GetClientHelloALPN(const SSLPointer& ssl) { |
158 | 0 | const unsigned char* buf; |
159 | 0 | size_t len; |
160 | 0 | size_t rem; |
161 | |
|
162 | 0 | if (!SSL_client_hello_get0_ext( |
163 | 0 | ssl.get(), |
164 | 0 | TLSEXT_TYPE_application_layer_protocol_negotiation, |
165 | 0 | &buf, |
166 | 0 | &rem) || |
167 | 0 | rem < 2) { |
168 | 0 | return nullptr; |
169 | 0 | } |
170 | | |
171 | 0 | len = (buf[0] << 8) | buf[1]; |
172 | 0 | if (len + 2 != rem) return nullptr; |
173 | 0 | return reinterpret_cast<const char*>(buf + 3); |
174 | 0 | } |
175 | | |
176 | 0 | const char* GetClientHelloServerName(const SSLPointer& ssl) { |
177 | 0 | const unsigned char* buf; |
178 | 0 | size_t len; |
179 | 0 | size_t rem; |
180 | |
|
181 | 0 | if (!SSL_client_hello_get0_ext( |
182 | 0 | ssl.get(), |
183 | 0 | TLSEXT_TYPE_server_name, |
184 | 0 | &buf, |
185 | 0 | &rem) || rem <= 2) { |
186 | 0 | return nullptr; |
187 | 0 | } |
188 | | |
189 | 0 | len = (*buf << 8) | *(buf + 1); |
190 | 0 | if (len + 2 != rem) |
191 | 0 | return nullptr; |
192 | 0 | rem = len; |
193 | |
|
194 | 0 | if (rem == 0 || *(buf + 2) != TLSEXT_NAMETYPE_host_name) return nullptr; |
195 | 0 | rem--; |
196 | 0 | if (rem <= 2) |
197 | 0 | return nullptr; |
198 | 0 | len = (*(buf + 3) << 8) | *(buf + 4); |
199 | 0 | if (len + 2 > rem) |
200 | 0 | return nullptr; |
201 | 0 | return reinterpret_cast<const char*>(buf + 5); |
202 | 0 | } |
203 | | |
204 | 0 | const char* GetServerName(SSL* ssl) { |
205 | 0 | return SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); |
206 | 0 | } |
207 | | |
208 | 0 | bool SetGroups(SecureContext* sc, const char* groups) { |
209 | 0 | return SSL_CTX_set1_groups_list(sc->ctx().get(), groups) == 1; |
210 | 0 | } |
211 | | |
212 | 0 | const char* X509ErrorCode(long err) { // NOLINT(runtime/int) |
213 | 0 | const char* code = "UNSPECIFIED"; |
214 | 0 | #define CASE_X509_ERR(CODE) case X509_V_ERR_##CODE: code = #CODE; break; |
215 | 0 | switch (err) { |
216 | | // if you modify anything in here, *please* update the respective section in |
217 | | // doc/api/tls.md as well |
218 | 0 | CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT) |
219 | 0 | CASE_X509_ERR(UNABLE_TO_GET_CRL) |
220 | 0 | CASE_X509_ERR(UNABLE_TO_DECRYPT_CERT_SIGNATURE) |
221 | 0 | CASE_X509_ERR(UNABLE_TO_DECRYPT_CRL_SIGNATURE) |
222 | 0 | CASE_X509_ERR(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY) |
223 | 0 | CASE_X509_ERR(CERT_SIGNATURE_FAILURE) |
224 | 0 | CASE_X509_ERR(CRL_SIGNATURE_FAILURE) |
225 | 0 | CASE_X509_ERR(CERT_NOT_YET_VALID) |
226 | 0 | CASE_X509_ERR(CERT_HAS_EXPIRED) |
227 | 0 | CASE_X509_ERR(CRL_NOT_YET_VALID) |
228 | 0 | CASE_X509_ERR(CRL_HAS_EXPIRED) |
229 | 0 | CASE_X509_ERR(ERROR_IN_CERT_NOT_BEFORE_FIELD) |
230 | 0 | CASE_X509_ERR(ERROR_IN_CERT_NOT_AFTER_FIELD) |
231 | 0 | CASE_X509_ERR(ERROR_IN_CRL_LAST_UPDATE_FIELD) |
232 | 0 | CASE_X509_ERR(ERROR_IN_CRL_NEXT_UPDATE_FIELD) |
233 | 0 | CASE_X509_ERR(OUT_OF_MEM) |
234 | 0 | CASE_X509_ERR(DEPTH_ZERO_SELF_SIGNED_CERT) |
235 | 0 | CASE_X509_ERR(SELF_SIGNED_CERT_IN_CHAIN) |
236 | 0 | CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT_LOCALLY) |
237 | 0 | CASE_X509_ERR(UNABLE_TO_VERIFY_LEAF_SIGNATURE) |
238 | 0 | CASE_X509_ERR(CERT_CHAIN_TOO_LONG) |
239 | 0 | CASE_X509_ERR(CERT_REVOKED) |
240 | 0 | CASE_X509_ERR(INVALID_CA) |
241 | 0 | CASE_X509_ERR(PATH_LENGTH_EXCEEDED) |
242 | 0 | CASE_X509_ERR(INVALID_PURPOSE) |
243 | 0 | CASE_X509_ERR(CERT_UNTRUSTED) |
244 | 0 | CASE_X509_ERR(CERT_REJECTED) |
245 | 0 | CASE_X509_ERR(HOSTNAME_MISMATCH) |
246 | 0 | } |
247 | 0 | #undef CASE_X509_ERR |
248 | 0 | return code; |
249 | 0 | } |
250 | | |
251 | 0 | MaybeLocal<Value> GetValidationErrorReason(Environment* env, int err) { |
252 | 0 | if (err == 0) |
253 | 0 | return Undefined(env->isolate()); |
254 | 0 | const char* reason = X509_verify_cert_error_string(err); |
255 | 0 | return OneByteString(env->isolate(), reason); |
256 | 0 | } |
257 | | |
258 | 0 | MaybeLocal<Value> GetValidationErrorCode(Environment* env, int err) { |
259 | 0 | if (err == 0) |
260 | 0 | return Undefined(env->isolate()); |
261 | 0 | return OneByteString(env->isolate(), X509ErrorCode(err)); |
262 | 0 | } |
263 | | |
264 | 0 | MaybeLocal<Value> GetCert(Environment* env, const SSLPointer& ssl) { |
265 | 0 | ClearErrorOnReturn clear_error_on_return; |
266 | 0 | X509* cert = SSL_get_certificate(ssl.get()); |
267 | 0 | if (cert == nullptr) |
268 | 0 | return Undefined(env->isolate()); |
269 | | |
270 | 0 | MaybeLocal<Object> maybe_cert = X509ToObject(env, cert); |
271 | 0 | return maybe_cert.FromMaybe<Value>(Local<Value>()); |
272 | 0 | } |
273 | | |
274 | 0 | Local<Value> ToV8Value(Environment* env, const BIOPointer& bio) { |
275 | 0 | BUF_MEM* mem; |
276 | 0 | BIO_get_mem_ptr(bio.get(), &mem); |
277 | 0 | MaybeLocal<String> ret = |
278 | 0 | String::NewFromUtf8( |
279 | 0 | env->isolate(), |
280 | 0 | mem->data, |
281 | 0 | NewStringType::kNormal, |
282 | 0 | mem->length); |
283 | 0 | CHECK_EQ(BIO_reset(bio.get()), 1); |
284 | 0 | return ret.FromMaybe(Local<Value>()); |
285 | 0 | } |
286 | | |
287 | | namespace { |
288 | | template <typename T> |
289 | | bool Set( |
290 | | Local<Context> context, |
291 | | Local<Object> target, |
292 | | Local<Value> name, |
293 | 0 | MaybeLocal<T> maybe_value) { |
294 | 0 | Local<Value> value; |
295 | 0 | if (!maybe_value.ToLocal(&value)) |
296 | 0 | return false; |
297 | | |
298 | | // Undefined is ignored, but still considered successful |
299 | 0 | if (value->IsUndefined()) |
300 | 0 | return true; |
301 | | |
302 | 0 | return !target->Set(context, name, value).IsNothing(); |
303 | 0 | } Unexecuted instantiation: crypto_common.cc:bool node::crypto::(anonymous namespace)::Set<v8::Object>(v8::Local<v8::Context>, v8::Local<v8::Object>, v8::Local<v8::Value>, v8::MaybeLocal<v8::Object>) Unexecuted instantiation: crypto_common.cc:bool node::crypto::(anonymous namespace)::Set<v8::Value>(v8::Local<v8::Context>, v8::Local<v8::Object>, v8::Local<v8::Value>, v8::MaybeLocal<v8::Value>) Unexecuted instantiation: crypto_common.cc:bool node::crypto::(anonymous namespace)::Set<v8::String>(v8::Local<v8::Context>, v8::Local<v8::Object>, v8::Local<v8::Value>, v8::MaybeLocal<v8::String>) Unexecuted instantiation: crypto_common.cc:bool node::crypto::(anonymous namespace)::Set<v8::Integer>(v8::Local<v8::Context>, v8::Local<v8::Object>, v8::Local<v8::Value>, v8::MaybeLocal<v8::Integer>) Unexecuted instantiation: crypto_common.cc:bool node::crypto::(anonymous namespace)::Set<v8::Boolean>(v8::Local<v8::Context>, v8::Local<v8::Object>, v8::Local<v8::Value>, v8::MaybeLocal<v8::Boolean>) |
304 | | |
305 | | template <const char* (*getstr)(const SSL_CIPHER* cipher)> |
306 | 0 | MaybeLocal<Value> GetCipherValue(Environment* env, const SSL_CIPHER* cipher) { |
307 | 0 | if (cipher == nullptr) |
308 | 0 | return Undefined(env->isolate()); |
309 | | |
310 | 0 | return OneByteString(env->isolate(), getstr(cipher)); |
311 | 0 | } Unexecuted instantiation: crypto_common.cc:v8::MaybeLocal<v8::Value> node::crypto::(anonymous namespace)::GetCipherValue<&SSL_CIPHER_get_name>(node::Environment*, ssl_cipher_st const*) Unexecuted instantiation: crypto_common.cc:v8::MaybeLocal<v8::Value> node::crypto::(anonymous namespace)::GetCipherValue<&SSL_CIPHER_standard_name>(node::Environment*, ssl_cipher_st const*) Unexecuted instantiation: crypto_common.cc:v8::MaybeLocal<v8::Value> node::crypto::(anonymous namespace)::GetCipherValue<&SSL_CIPHER_get_version>(node::Environment*, ssl_cipher_st const*) |
312 | | |
313 | | constexpr auto GetCipherName = GetCipherValue<SSL_CIPHER_get_name>; |
314 | | constexpr auto GetCipherStandardName = GetCipherValue<SSL_CIPHER_standard_name>; |
315 | | constexpr auto GetCipherVersion = GetCipherValue<SSL_CIPHER_get_version>; |
316 | | |
317 | | StackOfX509 CloneSSLCerts(X509Pointer&& cert, |
318 | 0 | const STACK_OF(X509)* const ssl_certs) { |
319 | 0 | StackOfX509 peer_certs(sk_X509_new(nullptr)); |
320 | 0 | if (!peer_certs) return StackOfX509(); |
321 | 0 | if (cert && !sk_X509_push(peer_certs.get(), cert.release())) |
322 | 0 | return StackOfX509(); |
323 | 0 | for (int i = 0; i < sk_X509_num(ssl_certs); i++) { |
324 | 0 | X509Pointer cert(X509_dup(sk_X509_value(ssl_certs, i))); |
325 | 0 | if (!cert || !sk_X509_push(peer_certs.get(), cert.get())) |
326 | 0 | return StackOfX509(); |
327 | | // `cert` is now managed by the stack. |
328 | 0 | cert.release(); |
329 | 0 | } |
330 | 0 | return peer_certs; |
331 | 0 | } |
332 | | |
333 | | MaybeLocal<Object> AddIssuerChainToObject( |
334 | | X509Pointer* cert, |
335 | | Local<Object> object, |
336 | | StackOfX509&& peer_certs, |
337 | 0 | Environment* const env) { |
338 | 0 | Local<Context> context = env->isolate()->GetCurrentContext(); |
339 | 0 | cert->reset(sk_X509_delete(peer_certs.get(), 0)); |
340 | 0 | for (;;) { |
341 | 0 | int i; |
342 | 0 | for (i = 0; i < sk_X509_num(peer_certs.get()); i++) { |
343 | 0 | X509* ca = sk_X509_value(peer_certs.get(), i); |
344 | 0 | if (X509_check_issued(ca, cert->get()) != X509_V_OK) |
345 | 0 | continue; |
346 | | |
347 | 0 | Local<Object> ca_info; |
348 | 0 | MaybeLocal<Object> maybe_ca_info = X509ToObject(env, ca); |
349 | 0 | if (!maybe_ca_info.ToLocal(&ca_info)) |
350 | 0 | return MaybeLocal<Object>(); |
351 | | |
352 | 0 | if (!Set<Object>(context, object, env->issuercert_string(), ca_info)) |
353 | 0 | return MaybeLocal<Object>(); |
354 | 0 | object = ca_info; |
355 | | |
356 | | // NOTE: Intentionally freeing cert that is not used anymore. |
357 | | // Delete cert and continue aggregating issuers. |
358 | 0 | cert->reset(sk_X509_delete(peer_certs.get(), i)); |
359 | 0 | break; |
360 | 0 | } |
361 | | |
362 | | // Issuer not found, break out of the loop. |
363 | 0 | if (i == sk_X509_num(peer_certs.get())) |
364 | 0 | break; |
365 | 0 | } |
366 | 0 | return MaybeLocal<Object>(object); |
367 | 0 | } |
368 | | |
369 | | MaybeLocal<Object> GetLastIssuedCert( |
370 | | X509Pointer* cert, |
371 | | const SSLPointer& ssl, |
372 | | Local<Object> issuer_chain, |
373 | 0 | Environment* const env) { |
374 | 0 | Local<Context> context = env->isolate()->GetCurrentContext(); |
375 | 0 | while (X509_check_issued(cert->get(), cert->get()) != X509_V_OK) { |
376 | 0 | X509Pointer ca; |
377 | 0 | if (!(ca = SSL_CTX_get_issuer(SSL_get_SSL_CTX(ssl.get()), cert->get()))) |
378 | 0 | break; |
379 | | |
380 | 0 | Local<Object> ca_info; |
381 | 0 | MaybeLocal<Object> maybe_ca_info = X509ToObject(env, ca.get()); |
382 | 0 | if (!maybe_ca_info.ToLocal(&ca_info)) |
383 | 0 | return MaybeLocal<Object>(); |
384 | | |
385 | 0 | if (!Set<Object>(context, issuer_chain, env->issuercert_string(), ca_info)) |
386 | 0 | return MaybeLocal<Object>(); |
387 | 0 | issuer_chain = ca_info; |
388 | | |
389 | | // For self-signed certificates whose keyUsage field does not include |
390 | | // keyCertSign, X509_check_issued() will return false. Avoid going into an |
391 | | // infinite loop by checking if SSL_CTX_get_issuer() returned the same |
392 | | // certificate. |
393 | 0 | if (cert->get() == ca.get()) break; |
394 | | |
395 | | // Delete previous cert and continue aggregating issuers. |
396 | 0 | *cert = std::move(ca); |
397 | 0 | } |
398 | 0 | return MaybeLocal<Object>(issuer_chain); |
399 | 0 | } |
400 | | |
401 | | void AddFingerprintDigest( |
402 | | const unsigned char* md, |
403 | | unsigned int md_size, |
404 | 0 | char fingerprint[3 * EVP_MAX_MD_SIZE]) { |
405 | 0 | unsigned int i; |
406 | 0 | const char hex[] = "0123456789ABCDEF"; |
407 | |
|
408 | 0 | for (i = 0; i < md_size; i++) { |
409 | 0 | fingerprint[3*i] = hex[(md[i] & 0xf0) >> 4]; |
410 | 0 | fingerprint[(3*i)+1] = hex[(md[i] & 0x0f)]; |
411 | 0 | fingerprint[(3*i)+2] = ':'; |
412 | 0 | } |
413 | |
|
414 | 0 | DCHECK_GT(md_size, 0); |
415 | 0 | fingerprint[(3 * (md_size - 1)) + 2] = '\0'; |
416 | 0 | } |
417 | | |
418 | | template <const char* (*nid2string)(int nid)> |
419 | 0 | MaybeLocal<Value> GetCurveName(Environment* env, const int nid) { |
420 | 0 | const char* name = nid2string(nid); |
421 | 0 | return name != nullptr ? |
422 | 0 | MaybeLocal<Value>(OneByteString(env->isolate(), name)) : |
423 | 0 | MaybeLocal<Value>(Undefined(env->isolate())); |
424 | 0 | } Unexecuted instantiation: crypto_common.cc:v8::MaybeLocal<v8::Value> node::crypto::(anonymous namespace)::GetCurveName<&OBJ_nid2sn>(node::Environment*, int) Unexecuted instantiation: crypto_common.cc:v8::MaybeLocal<v8::Value> node::crypto::(anonymous namespace)::GetCurveName<&EC_curve_nid2nist>(node::Environment*, int) |
425 | | |
426 | | MaybeLocal<Value> GetECPubKey( |
427 | | Environment* env, |
428 | | const EC_GROUP* group, |
429 | 0 | const ECPointer& ec) { |
430 | 0 | const EC_POINT* pubkey = EC_KEY_get0_public_key(ec.get()); |
431 | 0 | if (pubkey == nullptr) |
432 | 0 | return Undefined(env->isolate()); |
433 | | |
434 | 0 | return ECPointToBuffer( |
435 | 0 | env, |
436 | 0 | group, |
437 | 0 | pubkey, |
438 | 0 | EC_KEY_get_conv_form(ec.get()), |
439 | 0 | nullptr).FromMaybe(Local<Object>()); |
440 | 0 | } |
441 | | |
442 | | MaybeLocal<Value> GetECGroup( |
443 | | Environment* env, |
444 | | const EC_GROUP* group, |
445 | 0 | const ECPointer& ec) { |
446 | 0 | if (group == nullptr) |
447 | 0 | return Undefined(env->isolate()); |
448 | | |
449 | 0 | int bits = EC_GROUP_order_bits(group); |
450 | 0 | if (bits <= 0) |
451 | 0 | return Undefined(env->isolate()); |
452 | | |
453 | 0 | return Integer::New(env->isolate(), bits); |
454 | 0 | } |
455 | | |
456 | 0 | MaybeLocal<Object> GetPubKey(Environment* env, const RSAPointer& rsa) { |
457 | 0 | int size = i2d_RSA_PUBKEY(rsa.get(), nullptr); |
458 | 0 | CHECK_GE(size, 0); |
459 | | |
460 | 0 | std::unique_ptr<BackingStore> bs; |
461 | 0 | { |
462 | 0 | NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
463 | 0 | bs = ArrayBuffer::NewBackingStore(env->isolate(), size); |
464 | 0 | } |
465 | |
|
466 | 0 | unsigned char* serialized = reinterpret_cast<unsigned char*>(bs->Data()); |
467 | 0 | CHECK_GE(i2d_RSA_PUBKEY(rsa.get(), &serialized), 0); |
468 | | |
469 | 0 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
470 | 0 | return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Object>()); |
471 | 0 | } |
472 | | |
473 | | MaybeLocal<Value> GetExponentString( |
474 | | Environment* env, |
475 | | const BIOPointer& bio, |
476 | 0 | const BIGNUM* e) { |
477 | 0 | uint64_t exponent_word = static_cast<uint64_t>(BN_get_word(e)); |
478 | 0 | BIO_printf(bio.get(), "0x%" PRIx64, exponent_word); |
479 | 0 | return ToV8Value(env, bio); |
480 | 0 | } |
481 | | |
482 | 0 | Local<Value> GetBits(Environment* env, const BIGNUM* n) { |
483 | 0 | return Integer::New(env->isolate(), BN_num_bits(n)); |
484 | 0 | } |
485 | | |
486 | | MaybeLocal<Value> GetModulusString( |
487 | | Environment* env, |
488 | | const BIOPointer& bio, |
489 | 0 | const BIGNUM* n) { |
490 | 0 | BN_print(bio.get(), n); |
491 | 0 | return ToV8Value(env, bio); |
492 | 0 | } |
493 | | } // namespace |
494 | | |
495 | 0 | MaybeLocal<Value> GetRawDERCertificate(Environment* env, X509* cert) { |
496 | 0 | int size = i2d_X509(cert, nullptr); |
497 | |
|
498 | 0 | std::unique_ptr<BackingStore> bs; |
499 | 0 | { |
500 | 0 | NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
501 | 0 | bs = ArrayBuffer::NewBackingStore(env->isolate(), size); |
502 | 0 | } |
503 | |
|
504 | 0 | unsigned char* serialized = reinterpret_cast<unsigned char*>(bs->Data()); |
505 | 0 | CHECK_GE(i2d_X509(cert, &serialized), 0); |
506 | | |
507 | 0 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
508 | 0 | return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Object>()); |
509 | 0 | } |
510 | | |
511 | 0 | MaybeLocal<Value> GetSerialNumber(Environment* env, X509* cert) { |
512 | 0 | if (ASN1_INTEGER* serial_number = X509_get_serialNumber(cert)) { |
513 | 0 | BignumPointer bn(ASN1_INTEGER_to_BN(serial_number, nullptr)); |
514 | 0 | if (bn) { |
515 | 0 | char* data = BN_bn2hex(bn.get()); |
516 | 0 | ByteSource buf = ByteSource::Allocated(data, strlen(data)); |
517 | 0 | if (buf) return OneByteString(env->isolate(), buf.data<unsigned char>()); |
518 | 0 | } |
519 | 0 | } |
520 | | |
521 | 0 | return Undefined(env->isolate()); |
522 | 0 | } |
523 | | |
524 | 0 | MaybeLocal<Value> GetKeyUsage(Environment* env, X509* cert) { |
525 | 0 | StackOfASN1 eku(static_cast<STACK_OF(ASN1_OBJECT)*>( |
526 | 0 | X509_get_ext_d2i(cert, NID_ext_key_usage, nullptr, nullptr))); |
527 | 0 | if (eku) { |
528 | 0 | const int count = sk_ASN1_OBJECT_num(eku.get()); |
529 | 0 | MaybeStackBuffer<Local<Value>, 16> ext_key_usage(count); |
530 | 0 | char buf[256]; |
531 | |
|
532 | 0 | int j = 0; |
533 | 0 | for (int i = 0; i < count; i++) { |
534 | 0 | if (OBJ_obj2txt(buf, |
535 | 0 | sizeof(buf), |
536 | 0 | sk_ASN1_OBJECT_value(eku.get(), i), 1) >= 0) { |
537 | 0 | ext_key_usage[j++] = OneByteString(env->isolate(), buf); |
538 | 0 | } |
539 | 0 | } |
540 | |
|
541 | 0 | return Array::New(env->isolate(), ext_key_usage.out(), count); |
542 | 0 | } |
543 | | |
544 | 0 | return Undefined(env->isolate()); |
545 | 0 | } |
546 | | |
547 | | MaybeLocal<Value> GetCurrentCipherName(Environment* env, |
548 | 0 | const SSLPointer& ssl) { |
549 | 0 | return GetCipherName(env, SSL_get_current_cipher(ssl.get())); |
550 | 0 | } |
551 | | |
552 | | MaybeLocal<Value> GetCurrentCipherVersion(Environment* env, |
553 | 0 | const SSLPointer& ssl) { |
554 | 0 | return GetCipherVersion(env, SSL_get_current_cipher(ssl.get())); |
555 | 0 | } |
556 | | |
557 | | MaybeLocal<Value> GetFingerprintDigest( |
558 | | Environment* env, |
559 | | const EVP_MD* method, |
560 | 0 | X509* cert) { |
561 | 0 | unsigned char md[EVP_MAX_MD_SIZE]; |
562 | 0 | unsigned int md_size; |
563 | 0 | char fingerprint[EVP_MAX_MD_SIZE * 3]; |
564 | |
|
565 | 0 | if (X509_digest(cert, method, md, &md_size)) { |
566 | 0 | AddFingerprintDigest(md, md_size, fingerprint); |
567 | 0 | return OneByteString(env->isolate(), fingerprint); |
568 | 0 | } |
569 | 0 | return Undefined(env->isolate()); |
570 | 0 | } |
571 | | |
572 | | MaybeLocal<Value> GetValidTo( |
573 | | Environment* env, |
574 | | X509* cert, |
575 | 0 | const BIOPointer& bio) { |
576 | 0 | ASN1_TIME_print(bio.get(), X509_get0_notAfter(cert)); |
577 | 0 | return ToV8Value(env, bio); |
578 | 0 | } |
579 | | |
580 | | MaybeLocal<Value> GetValidFrom( |
581 | | Environment* env, |
582 | | X509* cert, |
583 | 0 | const BIOPointer& bio) { |
584 | 0 | ASN1_TIME_print(bio.get(), X509_get0_notBefore(cert)); |
585 | 0 | return ToV8Value(env, bio); |
586 | 0 | } |
587 | | |
588 | 0 | static inline bool IsSafeAltName(const char* name, size_t length, bool utf8) { |
589 | 0 | for (size_t i = 0; i < length; i++) { |
590 | 0 | char c = name[i]; |
591 | 0 | switch (c) { |
592 | 0 | case '"': |
593 | 0 | case '\\': |
594 | | // These mess with encoding rules. |
595 | | // Fall through. |
596 | 0 | case ',': |
597 | | // Commas make it impossible to split the list of subject alternative |
598 | | // names unambiguously, which is why we have to escape. |
599 | | // Fall through. |
600 | 0 | case '\'': |
601 | | // Single quotes are unlikely to appear in any legitimate values, but they |
602 | | // could be used to make a value look like it was escaped (i.e., enclosed |
603 | | // in single/double quotes). |
604 | 0 | return false; |
605 | 0 | default: |
606 | 0 | if (utf8) { |
607 | | // In UTF8 strings, we require escaping for any ASCII control character, |
608 | | // but NOT for non-ASCII characters. Note that all bytes of any code |
609 | | // point that consists of more than a single byte have their MSB set. |
610 | 0 | if (static_cast<unsigned char>(c) < ' ' || c == '\x7f') { |
611 | 0 | return false; |
612 | 0 | } |
613 | 0 | } else { |
614 | | // Check if the char is a control character or non-ASCII character. Note |
615 | | // that char may or may not be a signed type. Regardless, non-ASCII |
616 | | // values will always be outside of this range. |
617 | 0 | if (c < ' ' || c > '~') { |
618 | 0 | return false; |
619 | 0 | } |
620 | 0 | } |
621 | 0 | } |
622 | 0 | } |
623 | 0 | return true; |
624 | 0 | } |
625 | | |
626 | | static inline void PrintAltName(const BIOPointer& out, const char* name, |
627 | | size_t length, bool utf8, |
628 | 0 | const char* safe_prefix) { |
629 | 0 | if (IsSafeAltName(name, length, utf8)) { |
630 | | // For backward-compatibility, append "safe" names without any |
631 | | // modifications. |
632 | 0 | if (safe_prefix != nullptr) { |
633 | 0 | BIO_printf(out.get(), "%s:", safe_prefix); |
634 | 0 | } |
635 | 0 | BIO_write(out.get(), name, length); |
636 | 0 | } else { |
637 | | // If a name is not "safe", we cannot embed it without special |
638 | | // encoding. This does not usually happen, but we don't want to hide |
639 | | // it from the user either. We use JSON compatible escaping here. |
640 | 0 | BIO_write(out.get(), "\"", 1); |
641 | 0 | if (safe_prefix != nullptr) { |
642 | 0 | BIO_printf(out.get(), "%s:", safe_prefix); |
643 | 0 | } |
644 | 0 | for (size_t j = 0; j < length; j++) { |
645 | 0 | char c = static_cast<char>(name[j]); |
646 | 0 | if (c == '\\') { |
647 | 0 | BIO_write(out.get(), "\\\\", 2); |
648 | 0 | } else if (c == '"') { |
649 | 0 | BIO_write(out.get(), "\\\"", 2); |
650 | 0 | } else if ((c >= ' ' && c != ',' && c <= '~') || (utf8 && (c & 0x80))) { |
651 | | // Note that the above condition explicitly excludes commas, which means |
652 | | // that those are encoded as Unicode escape sequences in the "else" |
653 | | // block. That is not strictly necessary, and Node.js itself would parse |
654 | | // it correctly either way. We only do this to account for third-party |
655 | | // code that might be splitting the string at commas (as Node.js itself |
656 | | // used to do). |
657 | 0 | BIO_write(out.get(), &c, 1); |
658 | 0 | } else { |
659 | | // Control character or non-ASCII character. We treat everything as |
660 | | // Latin-1, which corresponds to the first 255 Unicode code points. |
661 | 0 | const char hex[] = "0123456789abcdef"; |
662 | 0 | char u[] = { '\\', 'u', '0', '0', hex[(c & 0xf0) >> 4], hex[c & 0x0f] }; |
663 | 0 | BIO_write(out.get(), u, sizeof(u)); |
664 | 0 | } |
665 | 0 | } |
666 | 0 | BIO_write(out.get(), "\"", 1); |
667 | 0 | } |
668 | 0 | } |
669 | | |
670 | | static inline void PrintLatin1AltName(const BIOPointer& out, |
671 | | const ASN1_IA5STRING* name, |
672 | 0 | const char* safe_prefix = nullptr) { |
673 | 0 | PrintAltName(out, reinterpret_cast<const char*>(name->data), name->length, |
674 | 0 | false, safe_prefix); |
675 | 0 | } |
676 | | |
677 | | static inline void PrintUtf8AltName(const BIOPointer& out, |
678 | | const ASN1_UTF8STRING* name, |
679 | 0 | const char* safe_prefix = nullptr) { |
680 | 0 | PrintAltName(out, reinterpret_cast<const char*>(name->data), name->length, |
681 | 0 | true, safe_prefix); |
682 | 0 | } |
683 | | |
684 | | // This function emulates the behavior of i2v_GENERAL_NAME in a safer and less |
685 | | // ambiguous way. "othername:" entries use the GENERAL_NAME_print format. |
686 | 0 | static bool PrintGeneralName(const BIOPointer& out, const GENERAL_NAME* gen) { |
687 | 0 | if (gen->type == GEN_DNS) { |
688 | 0 | ASN1_IA5STRING* name = gen->d.dNSName; |
689 | 0 | BIO_write(out.get(), "DNS:", 4); |
690 | | // Note that the preferred name syntax (see RFCs 5280 and 1034) with |
691 | | // wildcards is a subset of what we consider "safe", so spec-compliant DNS |
692 | | // names will never need to be escaped. |
693 | 0 | PrintLatin1AltName(out, name); |
694 | 0 | } else if (gen->type == GEN_EMAIL) { |
695 | 0 | ASN1_IA5STRING* name = gen->d.rfc822Name; |
696 | 0 | BIO_write(out.get(), "email:", 6); |
697 | 0 | PrintLatin1AltName(out, name); |
698 | 0 | } else if (gen->type == GEN_URI) { |
699 | 0 | ASN1_IA5STRING* name = gen->d.uniformResourceIdentifier; |
700 | 0 | BIO_write(out.get(), "URI:", 4); |
701 | | // The set of "safe" names was designed to include just about any URI, |
702 | | // with a few exceptions, most notably URIs that contains commas (see |
703 | | // RFC 2396). In other words, most legitimate URIs will not require |
704 | | // escaping. |
705 | 0 | PrintLatin1AltName(out, name); |
706 | 0 | } else if (gen->type == GEN_DIRNAME) { |
707 | | // Earlier versions of Node.js used X509_NAME_oneline to print the X509_NAME |
708 | | // object. The format was non standard and should be avoided. The use of |
709 | | // X509_NAME_oneline is discouraged by OpenSSL but was required for backward |
710 | | // compatibility. Conveniently, X509_NAME_oneline produced ASCII and the |
711 | | // output was unlikely to contains commas or other characters that would |
712 | | // require escaping. However, it SHOULD NOT produce ASCII output since an |
713 | | // RFC5280 AttributeValue may be a UTF8String. |
714 | | // Newer versions of Node.js have since switched to X509_NAME_print_ex to |
715 | | // produce a better format at the cost of backward compatibility. The new |
716 | | // format may contain Unicode characters and it is likely to contain commas, |
717 | | // which require escaping. Fortunately, the recently safeguarded function |
718 | | // PrintAltName handles all of that safely. |
719 | 0 | BIO_printf(out.get(), "DirName:"); |
720 | 0 | BIOPointer tmp(BIO_new(BIO_s_mem())); |
721 | 0 | CHECK(tmp); |
722 | 0 | if (X509_NAME_print_ex(tmp.get(), |
723 | 0 | gen->d.dirn, |
724 | 0 | 0, |
725 | 0 | kX509NameFlagsRFC2253WithinUtf8JSON) < 0) { |
726 | 0 | return false; |
727 | 0 | } |
728 | 0 | char* oline = nullptr; |
729 | 0 | long n_bytes = BIO_get_mem_data(tmp.get(), &oline); // NOLINT(runtime/int) |
730 | 0 | CHECK_GE(n_bytes, 0); |
731 | 0 | CHECK_IMPLIES(n_bytes != 0, oline != nullptr); |
732 | 0 | PrintAltName(out, oline, static_cast<size_t>(n_bytes), true, nullptr); |
733 | 0 | } else if (gen->type == GEN_IPADD) { |
734 | 0 | BIO_printf(out.get(), "IP Address:"); |
735 | 0 | const ASN1_OCTET_STRING* ip = gen->d.ip; |
736 | 0 | const unsigned char* b = ip->data; |
737 | 0 | if (ip->length == 4) { |
738 | 0 | BIO_printf(out.get(), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]); |
739 | 0 | } else if (ip->length == 16) { |
740 | 0 | for (unsigned int j = 0; j < 8; j++) { |
741 | 0 | uint16_t pair = (b[2 * j] << 8) | b[2 * j + 1]; |
742 | 0 | BIO_printf(out.get(), (j == 0) ? "%X" : ":%X", pair); |
743 | 0 | } |
744 | 0 | } else { |
745 | 0 | #if OPENSSL_VERSION_MAJOR >= 3 |
746 | 0 | BIO_printf(out.get(), "<invalid length=%d>", ip->length); |
747 | | #else |
748 | | BIO_printf(out.get(), "<invalid>"); |
749 | | #endif |
750 | 0 | } |
751 | 0 | } else if (gen->type == GEN_RID) { |
752 | | // Unlike OpenSSL's default implementation, never print the OID as text and |
753 | | // instead always print its numeric representation. |
754 | 0 | char oline[256]; |
755 | 0 | OBJ_obj2txt(oline, sizeof(oline), gen->d.rid, true); |
756 | 0 | BIO_printf(out.get(), "Registered ID:%s", oline); |
757 | 0 | } else if (gen->type == GEN_OTHERNAME) { |
758 | | // The format that is used here is based on OpenSSL's implementation of |
759 | | // GENERAL_NAME_print (as of OpenSSL 3.0.1). Earlier versions of Node.js |
760 | | // instead produced the same format as i2v_GENERAL_NAME, which was somewhat |
761 | | // awkward, especially when passed to translatePeerCertificate. |
762 | 0 | bool unicode = true; |
763 | 0 | const char* prefix = nullptr; |
764 | | // OpenSSL 1.1.1 does not support othername in GENERAL_NAME_print and may |
765 | | // not define these NIDs. |
766 | 0 | #if OPENSSL_VERSION_MAJOR >= 3 |
767 | 0 | int nid = OBJ_obj2nid(gen->d.otherName->type_id); |
768 | 0 | switch (nid) { |
769 | 0 | case NID_id_on_SmtpUTF8Mailbox: |
770 | 0 | prefix = "SmtpUTF8Mailbox"; |
771 | 0 | break; |
772 | 0 | case NID_XmppAddr: |
773 | 0 | prefix = "XmppAddr"; |
774 | 0 | break; |
775 | 0 | case NID_SRVName: |
776 | 0 | prefix = "SRVName"; |
777 | 0 | unicode = false; |
778 | 0 | break; |
779 | 0 | case NID_ms_upn: |
780 | 0 | prefix = "UPN"; |
781 | 0 | break; |
782 | 0 | case NID_NAIRealm: |
783 | 0 | prefix = "NAIRealm"; |
784 | 0 | break; |
785 | 0 | } |
786 | 0 | #endif // OPENSSL_VERSION_MAJOR >= 3 |
787 | 0 | int val_type = gen->d.otherName->value->type; |
788 | 0 | if (prefix == nullptr || |
789 | 0 | (unicode && val_type != V_ASN1_UTF8STRING) || |
790 | 0 | (!unicode && val_type != V_ASN1_IA5STRING)) { |
791 | 0 | BIO_printf(out.get(), "othername:<unsupported>"); |
792 | 0 | } else { |
793 | 0 | BIO_printf(out.get(), "othername:"); |
794 | 0 | if (unicode) { |
795 | 0 | PrintUtf8AltName(out, gen->d.otherName->value->value.utf8string, |
796 | 0 | prefix); |
797 | 0 | } else { |
798 | 0 | PrintLatin1AltName(out, gen->d.otherName->value->value.ia5string, |
799 | 0 | prefix); |
800 | 0 | } |
801 | 0 | } |
802 | 0 | } else if (gen->type == GEN_X400) { |
803 | | // TODO(tniessen): this is what OpenSSL does, implement properly instead |
804 | 0 | BIO_printf(out.get(), "X400Name:<unsupported>"); |
805 | 0 | } else if (gen->type == GEN_EDIPARTY) { |
806 | | // TODO(tniessen): this is what OpenSSL does, implement properly instead |
807 | 0 | BIO_printf(out.get(), "EdiPartyName:<unsupported>"); |
808 | 0 | } else { |
809 | | // This is safe because X509V3_EXT_d2i would have returned nullptr in this |
810 | | // case already. |
811 | 0 | UNREACHABLE(); |
812 | 0 | } |
813 | | |
814 | 0 | return true; |
815 | 0 | } |
816 | | |
817 | 0 | bool SafeX509SubjectAltNamePrint(const BIOPointer& out, X509_EXTENSION* ext) { |
818 | 0 | const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext); |
819 | 0 | CHECK(method == X509V3_EXT_get_nid(NID_subject_alt_name)); |
820 | | |
821 | 0 | GENERAL_NAMES* names = static_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(ext)); |
822 | 0 | if (names == nullptr) |
823 | 0 | return false; |
824 | | |
825 | 0 | bool ok = true; |
826 | |
|
827 | 0 | for (int i = 0; i < sk_GENERAL_NAME_num(names); i++) { |
828 | 0 | GENERAL_NAME* gen = sk_GENERAL_NAME_value(names, i); |
829 | |
|
830 | 0 | if (i != 0) |
831 | 0 | BIO_write(out.get(), ", ", 2); |
832 | |
|
833 | 0 | if (!(ok = PrintGeneralName(out, gen))) { |
834 | 0 | break; |
835 | 0 | } |
836 | 0 | } |
837 | 0 | sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); |
838 | |
|
839 | 0 | return ok; |
840 | 0 | } |
841 | | |
842 | 0 | bool SafeX509InfoAccessPrint(const BIOPointer& out, X509_EXTENSION* ext) { |
843 | 0 | const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext); |
844 | 0 | CHECK(method == X509V3_EXT_get_nid(NID_info_access)); |
845 | | |
846 | 0 | AUTHORITY_INFO_ACCESS* descs = |
847 | 0 | static_cast<AUTHORITY_INFO_ACCESS*>(X509V3_EXT_d2i(ext)); |
848 | 0 | if (descs == nullptr) |
849 | 0 | return false; |
850 | | |
851 | 0 | bool ok = true; |
852 | |
|
853 | 0 | for (int i = 0; i < sk_ACCESS_DESCRIPTION_num(descs); i++) { |
854 | 0 | ACCESS_DESCRIPTION* desc = sk_ACCESS_DESCRIPTION_value(descs, i); |
855 | |
|
856 | 0 | if (i != 0) |
857 | 0 | BIO_write(out.get(), "\n", 1); |
858 | |
|
859 | 0 | char objtmp[80]; |
860 | 0 | i2t_ASN1_OBJECT(objtmp, sizeof(objtmp), desc->method); |
861 | 0 | BIO_printf(out.get(), "%s - ", objtmp); |
862 | 0 | if (!(ok = PrintGeneralName(out, desc->location))) { |
863 | 0 | break; |
864 | 0 | } |
865 | 0 | } |
866 | 0 | sk_ACCESS_DESCRIPTION_pop_free(descs, ACCESS_DESCRIPTION_free); |
867 | |
|
868 | | #if OPENSSL_VERSION_MAJOR < 3 |
869 | | BIO_write(out.get(), "\n", 1); |
870 | | #endif |
871 | |
|
872 | 0 | return ok; |
873 | 0 | } |
874 | | |
875 | | v8::MaybeLocal<v8::Value> GetSubjectAltNameString(Environment* env, |
876 | | X509* cert, |
877 | 0 | const BIOPointer& bio) { |
878 | 0 | int index = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); |
879 | 0 | if (index < 0) |
880 | 0 | return Undefined(env->isolate()); |
881 | | |
882 | 0 | X509_EXTENSION* ext = X509_get_ext(cert, index); |
883 | 0 | CHECK_NOT_NULL(ext); |
884 | | |
885 | 0 | if (!SafeX509SubjectAltNamePrint(bio, ext)) { |
886 | 0 | CHECK_EQ(BIO_reset(bio.get()), 1); |
887 | 0 | return v8::Null(env->isolate()); |
888 | 0 | } |
889 | | |
890 | 0 | return ToV8Value(env, bio); |
891 | 0 | } |
892 | | |
893 | | v8::MaybeLocal<v8::Value> GetInfoAccessString(Environment* env, |
894 | | X509* cert, |
895 | 0 | const BIOPointer& bio) { |
896 | 0 | int index = X509_get_ext_by_NID(cert, NID_info_access, -1); |
897 | 0 | if (index < 0) |
898 | 0 | return Undefined(env->isolate()); |
899 | | |
900 | 0 | X509_EXTENSION* ext = X509_get_ext(cert, index); |
901 | 0 | CHECK_NOT_NULL(ext); |
902 | | |
903 | 0 | if (!SafeX509InfoAccessPrint(bio, ext)) { |
904 | 0 | CHECK_EQ(BIO_reset(bio.get()), 1); |
905 | 0 | return v8::Null(env->isolate()); |
906 | 0 | } |
907 | | |
908 | 0 | return ToV8Value(env, bio); |
909 | 0 | } |
910 | | |
911 | | MaybeLocal<Value> GetIssuerString(Environment* env, |
912 | | X509* cert, |
913 | 0 | const BIOPointer& bio) { |
914 | 0 | X509_NAME* issuer_name = X509_get_issuer_name(cert); |
915 | 0 | if (X509_NAME_print_ex( |
916 | 0 | bio.get(), |
917 | 0 | issuer_name, |
918 | 0 | 0, |
919 | 0 | kX509NameFlagsMultiline) <= 0) { |
920 | 0 | CHECK_EQ(BIO_reset(bio.get()), 1); |
921 | 0 | return Undefined(env->isolate()); |
922 | 0 | } |
923 | | |
924 | 0 | return ToV8Value(env, bio); |
925 | 0 | } |
926 | | |
927 | | MaybeLocal<Value> GetSubject(Environment* env, |
928 | | X509* cert, |
929 | 0 | const BIOPointer& bio) { |
930 | 0 | if (X509_NAME_print_ex( |
931 | 0 | bio.get(), |
932 | 0 | X509_get_subject_name(cert), |
933 | 0 | 0, |
934 | 0 | kX509NameFlagsMultiline) <= 0) { |
935 | 0 | CHECK_EQ(BIO_reset(bio.get()), 1); |
936 | 0 | return Undefined(env->isolate()); |
937 | 0 | } |
938 | | |
939 | 0 | return ToV8Value(env, bio); |
940 | 0 | } |
941 | | |
942 | | template <X509_NAME* get_name(const X509*)> |
943 | 0 | static MaybeLocal<Value> GetX509NameObject(Environment* env, X509* cert) { |
944 | 0 | X509_NAME* name = get_name(cert); |
945 | 0 | CHECK_NOT_NULL(name); |
946 | | |
947 | 0 | int cnt = X509_NAME_entry_count(name); |
948 | 0 | CHECK_GE(cnt, 0); |
949 | | |
950 | 0 | Local<Object> result = |
951 | 0 | Object::New(env->isolate(), Null(env->isolate()), nullptr, nullptr, 0); |
952 | 0 | if (result.IsEmpty()) { |
953 | 0 | return MaybeLocal<Value>(); |
954 | 0 | } |
955 | | |
956 | 0 | for (int i = 0; i < cnt; i++) { |
957 | 0 | X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, i); |
958 | 0 | CHECK_NOT_NULL(entry); |
959 | | |
960 | | // We intentionally ignore the value of X509_NAME_ENTRY_set because the |
961 | | // representation as an object does not allow grouping entries into sets |
962 | | // anyway, and multi-value RDNs are rare, i.e., the vast majority of |
963 | | // Relative Distinguished Names contains a single type-value pair only. |
964 | 0 | const ASN1_OBJECT* type = X509_NAME_ENTRY_get_object(entry); |
965 | 0 | const ASN1_STRING* value = X509_NAME_ENTRY_get_data(entry); |
966 | | |
967 | | // If OpenSSL knows the type, use the short name of the type as the key, and |
968 | | // the numeric representation of the type's OID otherwise. |
969 | 0 | int type_nid = OBJ_obj2nid(type); |
970 | 0 | char type_buf[80]; |
971 | 0 | const char* type_str; |
972 | 0 | if (type_nid != NID_undef) { |
973 | 0 | type_str = OBJ_nid2sn(type_nid); |
974 | 0 | CHECK_NOT_NULL(type_str); |
975 | 0 | } else { |
976 | 0 | OBJ_obj2txt(type_buf, sizeof(type_buf), type, true); |
977 | 0 | type_str = type_buf; |
978 | 0 | } |
979 | | |
980 | 0 | Local<String> v8_name; |
981 | 0 | if (!String::NewFromUtf8(env->isolate(), type_str).ToLocal(&v8_name)) { |
982 | 0 | return MaybeLocal<Value>(); |
983 | 0 | } |
984 | | |
985 | | // The previous implementation used X509_NAME_print_ex, which escapes some |
986 | | // characters in the value. The old implementation did not decode/unescape |
987 | | // values correctly though, leading to ambiguous and incorrect |
988 | | // representations. The new implementation only converts to Unicode and does |
989 | | // not escape anything. |
990 | 0 | unsigned char* value_str; |
991 | 0 | int value_str_size = ASN1_STRING_to_UTF8(&value_str, value); |
992 | 0 | if (value_str_size < 0) { |
993 | 0 | return Undefined(env->isolate()); |
994 | 0 | } |
995 | 0 | auto free_value_str = OnScopeLeave([&]() { OPENSSL_free(value_str); }); Unexecuted instantiation: crypto_common.cc:node::crypto::GetX509NameObject<&X509_get_subject_name>(node::Environment*, x509_st*)::{lambda()#1}::operator()() const Unexecuted instantiation: crypto_common.cc:node::crypto::GetX509NameObject<&X509_get_issuer_name>(node::Environment*, x509_st*)::{lambda()#1}::operator()() const |
996 | |
|
997 | 0 | Local<String> v8_value; |
998 | 0 | if (!String::NewFromUtf8(env->isolate(), |
999 | 0 | reinterpret_cast<const char*>(value_str), |
1000 | 0 | NewStringType::kNormal, |
1001 | 0 | value_str_size) |
1002 | 0 | .ToLocal(&v8_value)) { |
1003 | 0 | return MaybeLocal<Value>(); |
1004 | 0 | } |
1005 | | |
1006 | | // For backward compatibility, we only create arrays if multiple values |
1007 | | // exist for the same key. That is not great but there is not much we can |
1008 | | // change here without breaking things. Note that this creates nested data |
1009 | | // structures, yet still does not allow representing Distinguished Names |
1010 | | // accurately. |
1011 | 0 | bool multiple; |
1012 | 0 | if (!result->HasOwnProperty(env->context(), v8_name).To(&multiple)) { |
1013 | 0 | return MaybeLocal<Value>(); |
1014 | 0 | } else if (multiple) { |
1015 | 0 | Local<Value> accum; |
1016 | 0 | if (!result->Get(env->context(), v8_name).ToLocal(&accum)) { |
1017 | 0 | return MaybeLocal<Value>(); |
1018 | 0 | } |
1019 | 0 | if (!accum->IsArray()) { |
1020 | 0 | accum = Array::New(env->isolate(), &accum, 1); |
1021 | 0 | if (result->Set(env->context(), v8_name, accum).IsNothing()) { |
1022 | 0 | return MaybeLocal<Value>(); |
1023 | 0 | } |
1024 | 0 | } |
1025 | 0 | Local<Array> array = accum.As<Array>(); |
1026 | 0 | if (array->Set(env->context(), array->Length(), v8_value).IsNothing()) { |
1027 | 0 | return MaybeLocal<Value>(); |
1028 | 0 | } |
1029 | 0 | } else if (result->Set(env->context(), v8_name, v8_value).IsNothing()) { |
1030 | 0 | return MaybeLocal<Value>(); |
1031 | 0 | } |
1032 | 0 | } |
1033 | | |
1034 | 0 | return result; |
1035 | 0 | } Unexecuted instantiation: crypto_common.cc:v8::MaybeLocal<v8::Value> node::crypto::GetX509NameObject<&X509_get_subject_name>(node::Environment*, x509_st*) Unexecuted instantiation: crypto_common.cc:v8::MaybeLocal<v8::Value> node::crypto::GetX509NameObject<&X509_get_issuer_name>(node::Environment*, x509_st*) |
1036 | | |
1037 | | template <MaybeLocal<Value> (*Get)(Environment* env, const SSL_CIPHER* cipher)> |
1038 | | MaybeLocal<Value> GetCurrentCipherValue(Environment* env, |
1039 | 0 | const SSLPointer& ssl) { |
1040 | 0 | return Get(env, SSL_get_current_cipher(ssl.get())); |
1041 | 0 | } Unexecuted instantiation: crypto_common.cc:v8::MaybeLocal<v8::Value> node::crypto::GetCurrentCipherValue<&(v8::MaybeLocal<v8::Value> node::crypto::(anonymous namespace)::GetCipherValue<&SSL_CIPHER_get_name>(node::Environment*, ssl_cipher_st const*))>(node::Environment*, std::__1::unique_ptr<ssl_st, node::FunctionDeleter<ssl_st, &SSL_free> > const&) Unexecuted instantiation: crypto_common.cc:v8::MaybeLocal<v8::Value> node::crypto::GetCurrentCipherValue<&(v8::MaybeLocal<v8::Value> node::crypto::(anonymous namespace)::GetCipherValue<&SSL_CIPHER_standard_name>(node::Environment*, ssl_cipher_st const*))>(node::Environment*, std::__1::unique_ptr<ssl_st, node::FunctionDeleter<ssl_st, &SSL_free> > const&) Unexecuted instantiation: crypto_common.cc:v8::MaybeLocal<v8::Value> node::crypto::GetCurrentCipherValue<&(v8::MaybeLocal<v8::Value> node::crypto::(anonymous namespace)::GetCipherValue<&SSL_CIPHER_get_version>(node::Environment*, ssl_cipher_st const*))>(node::Environment*, std::__1::unique_ptr<ssl_st, node::FunctionDeleter<ssl_st, &SSL_free> > const&) |
1042 | | |
1043 | | MaybeLocal<Array> GetClientHelloCiphers( |
1044 | | Environment* env, |
1045 | 0 | const SSLPointer& ssl) { |
1046 | 0 | EscapableHandleScope scope(env->isolate()); |
1047 | 0 | const unsigned char* buf; |
1048 | 0 | size_t len = SSL_client_hello_get0_ciphers(ssl.get(), &buf); |
1049 | 0 | size_t count = len / 2; |
1050 | 0 | MaybeStackBuffer<Local<Value>, 16> ciphers(count); |
1051 | 0 | int j = 0; |
1052 | 0 | for (size_t n = 0; n < len; n += 2) { |
1053 | 0 | const SSL_CIPHER* cipher = SSL_CIPHER_find(ssl.get(), buf); |
1054 | 0 | buf += 2; |
1055 | 0 | Local<Object> obj = Object::New(env->isolate()); |
1056 | 0 | if (!Set(env->context(), |
1057 | 0 | obj, |
1058 | 0 | env->name_string(), |
1059 | 0 | GetCipherName(env, cipher)) || |
1060 | 0 | !Set(env->context(), |
1061 | 0 | obj, |
1062 | 0 | env->standard_name_string(), |
1063 | 0 | GetCipherStandardName(env, cipher)) || |
1064 | 0 | !Set(env->context(), |
1065 | 0 | obj, |
1066 | 0 | env->version_string(), |
1067 | 0 | GetCipherVersion(env, cipher))) { |
1068 | 0 | return MaybeLocal<Array>(); |
1069 | 0 | } |
1070 | 0 | ciphers[j++] = obj; |
1071 | 0 | } |
1072 | 0 | Local<Array> ret = Array::New(env->isolate(), ciphers.out(), count); |
1073 | 0 | return scope.Escape(ret); |
1074 | 0 | } |
1075 | | |
1076 | | |
1077 | 0 | MaybeLocal<Object> GetCipherInfo(Environment* env, const SSLPointer& ssl) { |
1078 | 0 | if (SSL_get_current_cipher(ssl.get()) == nullptr) |
1079 | 0 | return MaybeLocal<Object>(); |
1080 | 0 | EscapableHandleScope scope(env->isolate()); |
1081 | 0 | Local<Object> info = Object::New(env->isolate()); |
1082 | |
|
1083 | 0 | if (!Set<Value>(env->context(), |
1084 | 0 | info, |
1085 | 0 | env->name_string(), |
1086 | 0 | GetCurrentCipherValue<GetCipherName>(env, ssl)) || |
1087 | 0 | !Set<Value>(env->context(), |
1088 | 0 | info, |
1089 | 0 | env->standard_name_string(), |
1090 | 0 | GetCurrentCipherValue<GetCipherStandardName>(env, ssl)) || |
1091 | 0 | !Set<Value>(env->context(), |
1092 | 0 | info, |
1093 | 0 | env->version_string(), |
1094 | 0 | GetCurrentCipherValue<GetCipherVersion>(env, ssl))) { |
1095 | 0 | return MaybeLocal<Object>(); |
1096 | 0 | } |
1097 | | |
1098 | 0 | return scope.Escape(info); |
1099 | 0 | } |
1100 | | |
1101 | 0 | MaybeLocal<Object> GetEphemeralKey(Environment* env, const SSLPointer& ssl) { |
1102 | 0 | CHECK_EQ(SSL_is_server(ssl.get()), 0); |
1103 | 0 | EVP_PKEY* raw_key; |
1104 | |
|
1105 | 0 | EscapableHandleScope scope(env->isolate()); |
1106 | 0 | Local<Object> info = Object::New(env->isolate()); |
1107 | 0 | if (!SSL_get_server_tmp_key(ssl.get(), &raw_key)) |
1108 | 0 | return scope.Escape(info); |
1109 | | |
1110 | 0 | Local<Context> context = env->context(); |
1111 | 0 | crypto::EVPKeyPointer key(raw_key); |
1112 | |
|
1113 | 0 | int kid = EVP_PKEY_id(key.get()); |
1114 | 0 | int bits = EVP_PKEY_bits(key.get()); |
1115 | 0 | switch (kid) { |
1116 | 0 | case EVP_PKEY_DH: |
1117 | 0 | if (!Set<String>(context, info, env->type_string(), env->dh_string()) || |
1118 | 0 | !Set<Integer>(context, |
1119 | 0 | info, |
1120 | 0 | env->size_string(), |
1121 | 0 | Integer::New(env->isolate(), bits))) { |
1122 | 0 | return MaybeLocal<Object>(); |
1123 | 0 | } |
1124 | 0 | break; |
1125 | 0 | case EVP_PKEY_EC: |
1126 | 0 | case EVP_PKEY_X25519: |
1127 | 0 | case EVP_PKEY_X448: |
1128 | 0 | { |
1129 | 0 | const char* curve_name; |
1130 | 0 | if (kid == EVP_PKEY_EC) { |
1131 | 0 | ECKeyPointer ec(EVP_PKEY_get1_EC_KEY(key.get())); |
1132 | 0 | int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec.get())); |
1133 | 0 | curve_name = OBJ_nid2sn(nid); |
1134 | 0 | } else { |
1135 | 0 | curve_name = OBJ_nid2sn(kid); |
1136 | 0 | } |
1137 | 0 | if (!Set<String>(context, |
1138 | 0 | info, |
1139 | 0 | env->type_string(), |
1140 | 0 | env->ecdh_string()) || |
1141 | 0 | !Set<String>(context, |
1142 | 0 | info, |
1143 | 0 | env->name_string(), |
1144 | 0 | OneByteString(env->isolate(), curve_name)) || |
1145 | 0 | !Set<Integer>(context, |
1146 | 0 | info, |
1147 | 0 | env->size_string(), |
1148 | 0 | Integer::New(env->isolate(), bits))) { |
1149 | 0 | return MaybeLocal<Object>(); |
1150 | 0 | } |
1151 | 0 | } |
1152 | 0 | break; |
1153 | 0 | } |
1154 | | |
1155 | 0 | return scope.Escape(info); |
1156 | 0 | } |
1157 | | |
1158 | | MaybeLocal<Object> ECPointToBuffer(Environment* env, |
1159 | | const EC_GROUP* group, |
1160 | | const EC_POINT* point, |
1161 | | point_conversion_form_t form, |
1162 | 9 | const char** error) { |
1163 | 9 | size_t len = EC_POINT_point2oct(group, point, form, nullptr, 0, nullptr); |
1164 | 9 | if (len == 0) { |
1165 | 0 | if (error != nullptr) *error = "Failed to get public key length"; |
1166 | 0 | return MaybeLocal<Object>(); |
1167 | 0 | } |
1168 | | |
1169 | 9 | std::unique_ptr<BackingStore> bs; |
1170 | 9 | { |
1171 | 9 | NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
1172 | 9 | bs = ArrayBuffer::NewBackingStore(env->isolate(), len); |
1173 | 9 | } |
1174 | | |
1175 | 9 | len = EC_POINT_point2oct(group, |
1176 | 9 | point, |
1177 | 9 | form, |
1178 | 9 | reinterpret_cast<unsigned char*>(bs->Data()), |
1179 | 9 | bs->ByteLength(), |
1180 | 9 | nullptr); |
1181 | 9 | if (len == 0) { |
1182 | 0 | if (error != nullptr) *error = "Failed to get public key"; |
1183 | 0 | return MaybeLocal<Object>(); |
1184 | 0 | } |
1185 | | |
1186 | 9 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
1187 | 9 | return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Object>()); |
1188 | 9 | } |
1189 | | |
1190 | | MaybeLocal<Value> GetPeerCert( |
1191 | | Environment* env, |
1192 | | const SSLPointer& ssl, |
1193 | | bool abbreviated, |
1194 | 0 | bool is_server) { |
1195 | 0 | ClearErrorOnReturn clear_error_on_return; |
1196 | 0 | Local<Object> result; |
1197 | 0 | MaybeLocal<Object> maybe_cert; |
1198 | | |
1199 | | // NOTE: This is because of the odd OpenSSL behavior. On client `cert_chain` |
1200 | | // contains the `peer_certificate`, but on server it doesn't. |
1201 | 0 | X509Pointer cert(is_server ? SSL_get_peer_certificate(ssl.get()) : nullptr); |
1202 | 0 | STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(ssl.get()); |
1203 | 0 | if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) |
1204 | 0 | return Undefined(env->isolate()); |
1205 | | |
1206 | | // Short result requested. |
1207 | 0 | if (abbreviated) { |
1208 | 0 | maybe_cert = |
1209 | 0 | X509ToObject(env, cert ? cert.get() : sk_X509_value(ssl_certs, 0)); |
1210 | 0 | return maybe_cert.ToLocal(&result) ? result : MaybeLocal<Value>(); |
1211 | 0 | } |
1212 | | |
1213 | 0 | StackOfX509 peer_certs = CloneSSLCerts(std::move(cert), ssl_certs); |
1214 | 0 | if (peer_certs == nullptr) |
1215 | 0 | return Undefined(env->isolate()); |
1216 | | |
1217 | | // First and main certificate. |
1218 | 0 | X509Pointer first_cert(sk_X509_value(peer_certs.get(), 0)); |
1219 | 0 | CHECK(first_cert); |
1220 | 0 | maybe_cert = X509ToObject(env, first_cert.release()); |
1221 | 0 | if (!maybe_cert.ToLocal(&result)) |
1222 | 0 | return MaybeLocal<Value>(); |
1223 | | |
1224 | 0 | Local<Object> issuer_chain; |
1225 | 0 | MaybeLocal<Object> maybe_issuer_chain; |
1226 | |
|
1227 | 0 | maybe_issuer_chain = |
1228 | 0 | AddIssuerChainToObject( |
1229 | 0 | &cert, |
1230 | 0 | result, |
1231 | 0 | std::move(peer_certs), |
1232 | 0 | env); |
1233 | 0 | if (!maybe_issuer_chain.ToLocal(&issuer_chain)) |
1234 | 0 | return MaybeLocal<Value>(); |
1235 | | |
1236 | 0 | maybe_issuer_chain = |
1237 | 0 | GetLastIssuedCert( |
1238 | 0 | &cert, |
1239 | 0 | ssl, |
1240 | 0 | issuer_chain, |
1241 | 0 | env); |
1242 | |
|
1243 | 0 | issuer_chain.Clear(); |
1244 | 0 | if (!maybe_issuer_chain.ToLocal(&issuer_chain)) |
1245 | 0 | return MaybeLocal<Value>(); |
1246 | | |
1247 | | // Last certificate should be self-signed. |
1248 | 0 | if (X509_check_issued(cert.get(), cert.get()) == X509_V_OK && |
1249 | 0 | !Set<Object>(env->context(), |
1250 | 0 | issuer_chain, |
1251 | 0 | env->issuercert_string(), |
1252 | 0 | issuer_chain)) { |
1253 | 0 | return MaybeLocal<Value>(); |
1254 | 0 | } |
1255 | | |
1256 | 0 | return result; |
1257 | 0 | } |
1258 | | |
1259 | | MaybeLocal<Object> X509ToObject( |
1260 | | Environment* env, |
1261 | 0 | X509* cert) { |
1262 | 0 | EscapableHandleScope scope(env->isolate()); |
1263 | 0 | Local<Context> context = env->context(); |
1264 | 0 | Local<Object> info = Object::New(env->isolate()); |
1265 | |
|
1266 | 0 | BIOPointer bio(BIO_new(BIO_s_mem())); |
1267 | 0 | CHECK(bio); |
1268 | | |
1269 | | // X509_check_ca() returns a range of values. Only 1 means "is a CA" |
1270 | 0 | auto is_ca = Boolean::New(env->isolate(), 1 == X509_check_ca(cert)); |
1271 | 0 | if (!Set<Value>(context, |
1272 | 0 | info, |
1273 | 0 | env->subject_string(), |
1274 | 0 | GetX509NameObject<X509_get_subject_name>(env, cert)) || |
1275 | 0 | !Set<Value>(context, |
1276 | 0 | info, |
1277 | 0 | env->issuer_string(), |
1278 | 0 | GetX509NameObject<X509_get_issuer_name>(env, cert)) || |
1279 | 0 | !Set<Value>(context, |
1280 | 0 | info, |
1281 | 0 | env->subjectaltname_string(), |
1282 | 0 | GetSubjectAltNameString(env, cert, bio)) || |
1283 | 0 | !Set<Value>(context, |
1284 | 0 | info, |
1285 | 0 | env->infoaccess_string(), |
1286 | 0 | GetInfoAccessString(env, cert, bio)) || |
1287 | 0 | !Set<Boolean>(context, info, env->ca_string(), is_ca)) { |
1288 | 0 | return MaybeLocal<Object>(); |
1289 | 0 | } |
1290 | | |
1291 | 0 | EVPKeyPointer pkey(X509_get_pubkey(cert)); |
1292 | 0 | RSAPointer rsa; |
1293 | 0 | ECPointer ec; |
1294 | 0 | if (pkey) { |
1295 | 0 | switch (EVP_PKEY_id(pkey.get())) { |
1296 | 0 | case EVP_PKEY_RSA: |
1297 | 0 | rsa.reset(EVP_PKEY_get1_RSA(pkey.get())); |
1298 | 0 | break; |
1299 | 0 | case EVP_PKEY_EC: |
1300 | 0 | ec.reset(EVP_PKEY_get1_EC_KEY(pkey.get())); |
1301 | 0 | break; |
1302 | 0 | } |
1303 | 0 | } |
1304 | | |
1305 | 0 | if (rsa) { |
1306 | 0 | const BIGNUM* n; |
1307 | 0 | const BIGNUM* e; |
1308 | 0 | RSA_get0_key(rsa.get(), &n, &e, nullptr); |
1309 | 0 | if (!Set<Value>(context, |
1310 | 0 | info, |
1311 | 0 | env->modulus_string(), |
1312 | 0 | GetModulusString(env, bio, n)) || |
1313 | 0 | !Set<Value>(context, info, env->bits_string(), GetBits(env, n)) || |
1314 | 0 | !Set<Value>(context, |
1315 | 0 | info, |
1316 | 0 | env->exponent_string(), |
1317 | 0 | GetExponentString(env, bio, e)) || |
1318 | 0 | !Set<Object>(context, |
1319 | 0 | info, |
1320 | 0 | env->pubkey_string(), |
1321 | 0 | GetPubKey(env, rsa))) { |
1322 | 0 | return MaybeLocal<Object>(); |
1323 | 0 | } |
1324 | 0 | } else if (ec) { |
1325 | 0 | const EC_GROUP* group = EC_KEY_get0_group(ec.get()); |
1326 | |
|
1327 | 0 | if (!Set<Value>(context, |
1328 | 0 | info, |
1329 | 0 | env->bits_string(), |
1330 | 0 | GetECGroup(env, group, ec)) || |
1331 | 0 | !Set<Value>(context, |
1332 | 0 | info, |
1333 | 0 | env->pubkey_string(), |
1334 | 0 | GetECPubKey(env, group, ec))) { |
1335 | 0 | return MaybeLocal<Object>(); |
1336 | 0 | } |
1337 | | |
1338 | 0 | const int nid = EC_GROUP_get_curve_name(group); |
1339 | 0 | if (nid != 0) { |
1340 | | // Curve is well-known, get its OID and NIST nick-name (if it has one). |
1341 | |
|
1342 | 0 | if (!Set<Value>(context, |
1343 | 0 | info, |
1344 | 0 | env->asn1curve_string(), |
1345 | 0 | GetCurveName<OBJ_nid2sn>(env, nid)) || |
1346 | 0 | !Set<Value>(context, |
1347 | 0 | info, |
1348 | 0 | env->nistcurve_string(), |
1349 | 0 | GetCurveName<EC_curve_nid2nist>(env, nid))) { |
1350 | 0 | return MaybeLocal<Object>(); |
1351 | 0 | } |
1352 | 0 | } else { |
1353 | | // Unnamed curves can be described by their mathematical properties, |
1354 | | // but aren't used much (at all?) with X.509/TLS. Support later if needed. |
1355 | 0 | } |
1356 | 0 | } |
1357 | | |
1358 | | // pkey, rsa, and ec pointers are no longer needed. |
1359 | 0 | pkey.reset(); |
1360 | 0 | rsa.reset(); |
1361 | 0 | ec.reset(); |
1362 | |
|
1363 | 0 | if (!Set<Value>(context, |
1364 | 0 | info, |
1365 | 0 | env->valid_from_string(), |
1366 | 0 | GetValidFrom(env, cert, bio)) || |
1367 | 0 | !Set<Value>(context, |
1368 | 0 | info, |
1369 | 0 | env->valid_to_string(), |
1370 | 0 | GetValidTo(env, cert, bio))) { |
1371 | 0 | return MaybeLocal<Object>(); |
1372 | 0 | } |
1373 | | |
1374 | | // bio is no longer needed |
1375 | 0 | bio.reset(); |
1376 | |
|
1377 | 0 | if (!Set<Value>(context, |
1378 | 0 | info, |
1379 | 0 | env->fingerprint_string(), |
1380 | 0 | GetFingerprintDigest(env, EVP_sha1(), cert)) || |
1381 | 0 | !Set<Value>(context, |
1382 | 0 | info, |
1383 | 0 | env->fingerprint256_string(), |
1384 | 0 | GetFingerprintDigest(env, EVP_sha256(), cert)) || |
1385 | 0 | !Set<Value>(context, |
1386 | 0 | info, |
1387 | 0 | env->fingerprint512_string(), |
1388 | 0 | GetFingerprintDigest(env, EVP_sha512(), cert)) || |
1389 | 0 | !Set<Value>( |
1390 | 0 | context, info, env->ext_key_usage_string(), GetKeyUsage(env, cert)) || |
1391 | 0 | !Set<Value>(context, |
1392 | 0 | info, |
1393 | 0 | env->serial_number_string(), |
1394 | 0 | GetSerialNumber(env, cert)) || |
1395 | 0 | !Set<Value>( |
1396 | 0 | context, info, env->raw_string(), GetRawDERCertificate(env, cert))) { |
1397 | 0 | return MaybeLocal<Object>(); |
1398 | 0 | } |
1399 | | |
1400 | 0 | return scope.Escape(info); |
1401 | 0 | } |
1402 | | |
1403 | | } // namespace crypto |
1404 | | } // namespace node |