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