GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
#include "allocated_buffer-inl.h" |
||
2 |
#include "base_object-inl.h" |
||
3 |
#include "env-inl.h" |
||
4 |
#include "node_buffer.h" |
||
5 |
#include "node_crypto.h" |
||
6 |
#include "crypto/crypto_common.h" |
||
7 |
#include "node.h" |
||
8 |
#include "node_internals.h" |
||
9 |
#include "node_url.h" |
||
10 |
#include "string_bytes.h" |
||
11 |
#include "memory_tracker-inl.h" |
||
12 |
#include "v8.h" |
||
13 |
|||
14 |
#include <openssl/ec.h> |
||
15 |
#include <openssl/ecdh.h> |
||
16 |
#include <openssl/evp.h> |
||
17 |
#include <openssl/pem.h> |
||
18 |
#include <openssl/x509v3.h> |
||
19 |
#include <openssl/hmac.h> |
||
20 |
#include <openssl/rand.h> |
||
21 |
#include <openssl/pkcs12.h> |
||
22 |
|||
23 |
#include <string> |
||
24 |
#include <unordered_map> |
||
25 |
|||
26 |
namespace node { |
||
27 |
|||
28 |
using v8::Array; |
||
29 |
using v8::ArrayBuffer; |
||
30 |
using v8::ArrayBufferView; |
||
31 |
using v8::BackingStore; |
||
32 |
using v8::Context; |
||
33 |
using v8::EscapableHandleScope; |
||
34 |
using v8::Integer; |
||
35 |
using v8::Local; |
||
36 |
using v8::MaybeLocal; |
||
37 |
using v8::NewStringType; |
||
38 |
using v8::Object; |
||
39 |
using v8::String; |
||
40 |
using v8::Undefined; |
||
41 |
using v8::Value; |
||
42 |
|||
43 |
namespace crypto { |
||
44 |
static constexpr int X509_NAME_FLAGS = |
||
45 |
ASN1_STRFLGS_ESC_CTRL | |
||
46 |
ASN1_STRFLGS_UTF8_CONVERT | |
||
47 |
XN_FLAG_SEP_MULTILINE | |
||
48 |
XN_FLAG_FN_SN; |
||
49 |
|||
50 |
838 |
int SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert, X509** issuer) { |
|
51 |
838 |
X509_STORE* store = SSL_CTX_get_cert_store(ctx); |
|
52 |
DeleteFnPtr<X509_STORE_CTX, X509_STORE_CTX_free> store_ctx( |
||
53 |
838 |
X509_STORE_CTX_new()); |
|
54 |
✓✗ | 1676 |
return store_ctx.get() != nullptr && |
55 |
✓✗✓✓ |
1676 |
X509_STORE_CTX_init(store_ctx.get(), store, nullptr, nullptr) == 1 && |
56 |
1676 |
X509_STORE_CTX_get1_issuer(issuer, store_ctx.get(), cert) == 1; |
|
57 |
} |
||
58 |
|||
59 |
void LogSecret( |
||
60 |
const SSLPointer& ssl, |
||
61 |
const char* name, |
||
62 |
const unsigned char* secret, |
||
63 |
size_t secretlen) { |
||
64 |
auto keylog_cb = SSL_CTX_get_keylog_callback(SSL_get_SSL_CTX(ssl.get())); |
||
65 |
unsigned char crandom[32]; |
||
66 |
|||
67 |
if (keylog_cb == nullptr || |
||
68 |
SSL_get_client_random(ssl.get(), crandom, 32) != 32) { |
||
69 |
return; |
||
70 |
} |
||
71 |
|||
72 |
std::string line = name; |
||
73 |
line += " " + StringBytes::hex_encode( |
||
74 |
reinterpret_cast<const char*>(crandom), 32); |
||
75 |
line += " " + StringBytes::hex_encode( |
||
76 |
reinterpret_cast<const char*>(secret), secretlen); |
||
77 |
keylog_cb(ssl.get(), line.c_str()); |
||
78 |
} |
||
79 |
|||
80 |
bool SetALPN(const SSLPointer& ssl, const std::string& alpn) { |
||
81 |
return SSL_set_alpn_protos( |
||
82 |
ssl.get(), |
||
83 |
reinterpret_cast<const uint8_t*>(alpn.c_str()), |
||
84 |
alpn.length()) == 0; |
||
85 |
} |
||
86 |
|||
87 |
39 |
bool SetALPN(const SSLPointer& ssl, Local<Value> alpn) { |
|
88 |
✗✓ | 39 |
if (!alpn->IsArrayBufferView()) |
89 |
return false; |
||
90 |
39 |
ArrayBufferViewContents<unsigned char> protos(alpn.As<ArrayBufferView>()); |
|
91 |
39 |
return SSL_set_alpn_protos(ssl.get(), protos.data(), protos.length()) == 0; |
|
92 |
} |
||
93 |
|||
94 |
4 |
MaybeLocal<Value> GetSSLOCSPResponse( |
|
95 |
Environment* env, |
||
96 |
SSL* ssl, |
||
97 |
Local<Value> default_value) { |
||
98 |
const unsigned char* resp; |
||
99 |
4 |
int len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp); |
|
100 |
✓✓ | 4 |
if (resp == nullptr) |
101 |
2 |
return default_value; |
|
102 |
|||
103 |
Local<Value> ret; |
||
104 |
MaybeLocal<Object> maybe_buffer = |
||
105 |
2 |
Buffer::Copy(env, reinterpret_cast<const char*>(resp), len); |
|
106 |
|||
107 |
✗✓ | 2 |
if (!maybe_buffer.ToLocal(&ret)) |
108 |
return MaybeLocal<Value>(); |
||
109 |
|||
110 |
2 |
return ret; |
|
111 |
} |
||
112 |
|||
113 |
bool SetTLSSession( |
||
114 |
const SSLPointer& ssl, |
||
115 |
const unsigned char* buf, |
||
116 |
size_t length) { |
||
117 |
SSLSessionPointer s(d2i_SSL_SESSION(nullptr, &buf, length)); |
||
118 |
return s == nullptr ? false : SetTLSSession(ssl, s); |
||
119 |
} |
||
120 |
|||
121 |
96 |
bool SetTLSSession( |
|
122 |
const SSLPointer& ssl, |
||
123 |
const SSLSessionPointer& session) { |
||
124 |
✓✗✓✗ |
96 |
return session != nullptr && SSL_set_session(ssl.get(), session.get()) == 1; |
125 |
} |
||
126 |
|||
127 |
96 |
SSLSessionPointer GetTLSSession(Local<Value> val) { |
|
128 |
✗✓ | 96 |
if (!val->IsArrayBufferView()) |
129 |
return SSLSessionPointer(); |
||
130 |
96 |
ArrayBufferViewContents<unsigned char> sbuf(val.As<ArrayBufferView>()); |
|
131 |
96 |
return GetTLSSession(sbuf.data(), sbuf.length()); |
|
132 |
} |
||
133 |
|||
134 |
96 |
SSLSessionPointer GetTLSSession(const unsigned char* buf, size_t length) { |
|
135 |
96 |
return SSLSessionPointer(d2i_SSL_SESSION(nullptr, &buf, length)); |
|
136 |
} |
||
137 |
|||
138 |
std::unordered_multimap<std::string, std::string> |
||
139 |
GetCertificateAltNames(X509* cert) { |
||
140 |
std::unordered_multimap<std::string, std::string> map; |
||
141 |
BIOPointer bio(BIO_new(BIO_s_mem())); |
||
142 |
BUF_MEM* mem; |
||
143 |
int idx = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); |
||
144 |
if (idx < 0) // There is no subject alt name |
||
145 |
return map; |
||
146 |
|||
147 |
X509_EXTENSION* ext = X509_get_ext(cert, idx); |
||
148 |
CHECK_NOT_NULL(ext); |
||
149 |
const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext); |
||
150 |
CHECK_EQ(method, X509V3_EXT_get_nid(NID_subject_alt_name)); |
||
151 |
|||
152 |
GENERAL_NAMES* names = static_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(ext)); |
||
153 |
if (names == nullptr) // There are no names |
||
154 |
return map; |
||
155 |
|||
156 |
for (int i = 0; i < sk_GENERAL_NAME_num(names); i++) { |
||
157 |
USE(BIO_reset(bio.get())); |
||
158 |
GENERAL_NAME* gen = sk_GENERAL_NAME_value(names, i); |
||
159 |
if (gen->type == GEN_DNS) { |
||
160 |
ASN1_IA5STRING* name = gen->d.dNSName; |
||
161 |
BIO_write(bio.get(), name->data, name->length); |
||
162 |
BIO_get_mem_ptr(bio.get(), &mem); |
||
163 |
map.emplace("dns", std::string(mem->data, mem->length)); |
||
164 |
} else { |
||
165 |
STACK_OF(CONF_VALUE)* nval = i2v_GENERAL_NAME( |
||
166 |
const_cast<X509V3_EXT_METHOD*>(method), gen, nullptr); |
||
167 |
if (nval == nullptr) |
||
168 |
continue; |
||
169 |
X509V3_EXT_val_prn(bio.get(), nval, 0, 0); |
||
170 |
sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); |
||
171 |
BIO_get_mem_ptr(bio.get(), &mem); |
||
172 |
std::string value(mem->data, mem->length); |
||
173 |
if (value.compare(0, 11, "IP Address:") == 0) { |
||
174 |
map.emplace("ip", value.substr(11)); |
||
175 |
} else if (value.compare(0, 4, "URI:") == 0) { |
||
176 |
url::URL url(value.substr(4)); |
||
177 |
if (url.flags() & url::URL_FLAGS_CANNOT_BE_BASE || |
||
178 |
url.flags() & url::URL_FLAGS_FAILED) { |
||
179 |
continue; // Skip this one |
||
180 |
} |
||
181 |
map.emplace("uri", url.host()); |
||
182 |
} |
||
183 |
} |
||
184 |
} |
||
185 |
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); |
||
186 |
return map; |
||
187 |
} |
||
188 |
|||
189 |
std::string GetCertificateCN(X509* cert) { |
||
190 |
X509_NAME* subject = X509_get_subject_name(cert); |
||
191 |
if (subject != nullptr) { |
||
192 |
int nid = OBJ_txt2nid("CN"); |
||
193 |
int idx = X509_NAME_get_index_by_NID(subject, nid, -1); |
||
194 |
if (idx != -1) { |
||
195 |
X509_NAME_ENTRY* cn = X509_NAME_get_entry(subject, idx); |
||
196 |
if (cn != nullptr) { |
||
197 |
ASN1_STRING* cn_str = X509_NAME_ENTRY_get_data(cn); |
||
198 |
if (cn_str != nullptr) { |
||
199 |
return std::string(reinterpret_cast<const char*>( |
||
200 |
ASN1_STRING_get0_data(cn_str))); |
||
201 |
} |
||
202 |
} |
||
203 |
} |
||
204 |
} |
||
205 |
return std::string(); |
||
206 |
} |
||
207 |
|||
208 |
894 |
long VerifyPeerCertificate( // NOLINT(runtime/int) |
|
209 |
const SSLPointer& ssl, |
||
210 |
long def) { // NOLINT(runtime/int) |
||
211 |
894 |
long err = def; // NOLINT(runtime/int) |
|
212 |
✓✓ | 894 |
if (X509* peer_cert = SSL_get_peer_certificate(ssl.get())) { |
213 |
886 |
X509_free(peer_cert); |
|
214 |
886 |
err = SSL_get_verify_result(ssl.get()); |
|
215 |
} else { |
||
216 |
8 |
const SSL_CIPHER* curr_cipher = SSL_get_current_cipher(ssl.get()); |
|
217 |
8 |
const SSL_SESSION* sess = SSL_get_session(ssl.get()); |
|
218 |
// Allow no-cert for PSK authentication in TLS1.2 and lower. |
||
219 |
// In TLS1.3 check that session was reused because TLS1.3 PSK |
||
220 |
// looks like session resumption. |
||
221 |
✓✓✓✗ ✓✓ |
20 |
if (SSL_CIPHER_get_auth_nid(curr_cipher) == NID_auth_psk || |
222 |
✓✓ | 12 |
(SSL_SESSION_get_protocol_version(sess) == TLS1_3_VERSION && |
223 |
6 |
SSL_session_reused(ssl.get()))) { |
|
224 |
7 |
return X509_V_OK; |
|
225 |
} |
||
226 |
} |
||
227 |
887 |
return err; |
|
228 |
} |
||
229 |
|||
230 |
12 |
int UseSNIContext(const SSLPointer& ssl, BaseObjectPtr<SecureContext> context) { |
|
231 |
12 |
SSL_CTX* ctx = context->ctx_.get(); |
|
232 |
12 |
X509* x509 = SSL_CTX_get0_certificate(ctx); |
|
233 |
12 |
EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx); |
|
234 |
STACK_OF(X509)* chain; |
||
235 |
|||
236 |
12 |
int err = SSL_CTX_get0_chain_certs(ctx, &chain); |
|
237 |
✓✗ | 12 |
if (err == 1) err = SSL_use_certificate(ssl.get(), x509); |
238 |
✓✓ | 12 |
if (err == 1) err = SSL_use_PrivateKey(ssl.get(), pkey); |
239 |
✓✓✓✓ |
12 |
if (err == 1 && chain != nullptr) err = SSL_set1_chain(ssl.get(), chain); |
240 |
12 |
return err; |
|
241 |
} |
||
242 |
|||
243 |
const char* GetClientHelloALPN(const SSLPointer& ssl) { |
||
244 |
const unsigned char* buf; |
||
245 |
size_t len; |
||
246 |
size_t rem; |
||
247 |
|||
248 |
if (!SSL_client_hello_get0_ext( |
||
249 |
ssl.get(), |
||
250 |
TLSEXT_TYPE_application_layer_protocol_negotiation, |
||
251 |
&buf, |
||
252 |
&rem) || |
||
253 |
rem < 2) { |
||
254 |
return nullptr; |
||
255 |
} |
||
256 |
|||
257 |
len = (buf[0] << 8) | buf[1]; |
||
258 |
if (len + 2 != rem) return nullptr; |
||
259 |
return reinterpret_cast<const char*>(buf + 3); |
||
260 |
} |
||
261 |
|||
262 |
const char* GetClientHelloServerName(const SSLPointer& ssl) { |
||
263 |
const unsigned char* buf; |
||
264 |
size_t len; |
||
265 |
size_t rem; |
||
266 |
|||
267 |
if (!SSL_client_hello_get0_ext( |
||
268 |
ssl.get(), |
||
269 |
TLSEXT_TYPE_server_name, |
||
270 |
&buf, |
||
271 |
&rem) || rem <= 2) { |
||
272 |
return nullptr; |
||
273 |
} |
||
274 |
|||
275 |
len = (*buf << 8) | *(buf + 1); |
||
276 |
if (len + 2 != rem) |
||
277 |
return nullptr; |
||
278 |
rem = len; |
||
279 |
|||
280 |
if (rem == 0 || *(buf + 2) != TLSEXT_NAMETYPE_host_name) return nullptr; |
||
281 |
rem--; |
||
282 |
if (rem <= 2) |
||
283 |
return nullptr; |
||
284 |
len = (*(buf + 3) << 8) | *(buf + 4); |
||
285 |
if (len + 2 > rem) |
||
286 |
return nullptr; |
||
287 |
return reinterpret_cast<const char*>(buf + 5); |
||
288 |
} |
||
289 |
|||
290 |
25 |
const char* GetServerName(SSL* ssl) { |
|
291 |
25 |
return SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); |
|
292 |
} |
||
293 |
|||
294 |
bool SetGroups(SecureContext* sc, const char* groups) { |
||
295 |
return SSL_CTX_set1_groups_list(**sc, groups) == 1; |
||
296 |
} |
||
297 |
|||
298 |
434 |
const char* X509ErrorCode(long err) { // NOLINT(runtime/int) |
|
299 |
434 |
const char* code = "UNSPECIFIED"; |
|
300 |
#define CASE_X509_ERR(CODE) case X509_V_ERR_##CODE: code = #CODE; break; |
||
301 |
✓✓✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✓✓✓ ✗✓✗✗ ✓✗✗✗ ✗ |
434 |
switch (err) { |
302 |
// if you modify anything in here, *please* update the respective section in |
||
303 |
// doc/api/tls.md as well |
||
304 |
1 |
CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT) |
|
305 |
1 |
CASE_X509_ERR(UNABLE_TO_GET_CRL) |
|
306 |
CASE_X509_ERR(UNABLE_TO_DECRYPT_CERT_SIGNATURE) |
||
307 |
CASE_X509_ERR(UNABLE_TO_DECRYPT_CRL_SIGNATURE) |
||
308 |
CASE_X509_ERR(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY) |
||
309 |
CASE_X509_ERR(CERT_SIGNATURE_FAILURE) |
||
310 |
CASE_X509_ERR(CRL_SIGNATURE_FAILURE) |
||
311 |
CASE_X509_ERR(CERT_NOT_YET_VALID) |
||
312 |
CASE_X509_ERR(CERT_HAS_EXPIRED) |
||
313 |
CASE_X509_ERR(CRL_NOT_YET_VALID) |
||
314 |
CASE_X509_ERR(CRL_HAS_EXPIRED) |
||
315 |
CASE_X509_ERR(ERROR_IN_CERT_NOT_BEFORE_FIELD) |
||
316 |
CASE_X509_ERR(ERROR_IN_CERT_NOT_AFTER_FIELD) |
||
317 |
CASE_X509_ERR(ERROR_IN_CRL_LAST_UPDATE_FIELD) |
||
318 |
CASE_X509_ERR(ERROR_IN_CRL_NEXT_UPDATE_FIELD) |
||
319 |
CASE_X509_ERR(OUT_OF_MEM) |
||
320 |
240 |
CASE_X509_ERR(DEPTH_ZERO_SELF_SIGNED_CERT) |
|
321 |
18 |
CASE_X509_ERR(SELF_SIGNED_CERT_IN_CHAIN) |
|
322 |
4 |
CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT_LOCALLY) |
|
323 |
168 |
CASE_X509_ERR(UNABLE_TO_VERIFY_LEAF_SIGNATURE) |
|
324 |
CASE_X509_ERR(CERT_CHAIN_TOO_LONG) |
||
325 |
1 |
CASE_X509_ERR(CERT_REVOKED) |
|
326 |
CASE_X509_ERR(INVALID_CA) |
||
327 |
CASE_X509_ERR(PATH_LENGTH_EXCEEDED) |
||
328 |
1 |
CASE_X509_ERR(INVALID_PURPOSE) |
|
329 |
CASE_X509_ERR(CERT_UNTRUSTED) |
||
330 |
CASE_X509_ERR(CERT_REJECTED) |
||
331 |
CASE_X509_ERR(HOSTNAME_MISMATCH) |
||
332 |
} |
||
333 |
#undef CASE_X509_ERR |
||
334 |
434 |
return code; |
|
335 |
} |
||
336 |
|||
337 |
MaybeLocal<Value> GetValidationErrorReason(Environment* env, int err) { |
||
338 |
if (err == 0) |
||
339 |
return Undefined(env->isolate()); |
||
340 |
const char* reason = X509_verify_cert_error_string(err); |
||
341 |
return OneByteString(env->isolate(), reason); |
||
342 |
} |
||
343 |
|||
344 |
MaybeLocal<Value> GetValidationErrorCode(Environment* env, int err) { |
||
345 |
if (err == 0) |
||
346 |
return Undefined(env->isolate()); |
||
347 |
return OneByteString(env->isolate(), X509ErrorCode(err)); |
||
348 |
} |
||
349 |
|||
350 |
12 |
MaybeLocal<Value> GetCert(Environment* env, const SSLPointer& ssl) { |
|
351 |
12 |
ClearErrorOnReturn clear_error_on_return; |
|
352 |
12 |
X509* cert = SSL_get_certificate(ssl.get()); |
|
353 |
✓✓ | 12 |
if (cert == nullptr) |
354 |
2 |
return Undefined(env->isolate()); |
|
355 |
|||
356 |
11 |
MaybeLocal<Object> maybe_cert = X509ToObject(env, cert); |
|
357 |
11 |
return maybe_cert.FromMaybe<Value>(Local<Value>()); |
|
358 |
} |
||
359 |
|||
360 |
6939 |
Local<Value> ToV8Value(Environment* env, const BIOPointer& bio) { |
|
361 |
BUF_MEM* mem; |
||
362 |
6939 |
BIO_get_mem_ptr(bio.get(), &mem); |
|
363 |
MaybeLocal<String> ret = |
||
364 |
String::NewFromUtf8( |
||
365 |
env->isolate(), |
||
366 |
6939 |
mem->data, |
|
367 |
NewStringType::kNormal, |
||
368 |
6939 |
mem->length); |
|
369 |
6939 |
USE(BIO_reset(bio.get())); |
|
370 |
6939 |
return ret.FromMaybe(Local<Value>()); |
|
371 |
} |
||
372 |
|||
373 |
namespace { |
||
374 |
template <typename T> |
||
375 |
41864 |
bool Set( |
|
376 |
Local<Context> context, |
||
377 |
Local<Object> target, |
||
378 |
Local<Value> name, |
||
379 |
MaybeLocal<T> maybe_value) { |
||
380 |
Local<Value> value; |
||
381 |
✗✓ | 41864 |
if (!maybe_value.ToLocal(&value)) |
382 |
return false; |
||
383 |
|||
384 |
// Undefined is ignored, but still considered successful |
||
385 |
✓✓ | 83728 |
if (value->IsUndefined()) |
386 |
6754 |
return true; |
|
387 |
|||
388 |
70220 |
return !target->Set(context, name, value).IsNothing(); |
|
389 |
} |
||
390 |
|||
391 |
159 |
MaybeLocal<Value> GetCipherValue(Environment* env, |
|
392 |
const SSL_CIPHER* cipher, |
||
393 |
const char* (*getstr)(const SSL_CIPHER* cipher)) { |
||
394 |
✗✓ | 159 |
if (cipher == nullptr) |
395 |
return Undefined(env->isolate()); |
||
396 |
|||
397 |
318 |
return OneByteString(env->isolate(), getstr(cipher)); |
|
398 |
} |
||
399 |
|||
400 |
53 |
MaybeLocal<Value> GetCipherName(Environment* env, const SSL_CIPHER* cipher) { |
|
401 |
53 |
return GetCipherValue(env, cipher, SSL_CIPHER_get_name); |
|
402 |
} |
||
403 |
|||
404 |
53 |
MaybeLocal<Value> GetCipherStandardName( |
|
405 |
Environment* env, |
||
406 |
const SSL_CIPHER* cipher) { |
||
407 |
53 |
return GetCipherValue(env, cipher, SSL_CIPHER_standard_name); |
|
408 |
} |
||
409 |
|||
410 |
53 |
MaybeLocal<Value> GetCipherVersion(Environment* env, const SSL_CIPHER* cipher) { |
|
411 |
53 |
return GetCipherValue(env, cipher, SSL_CIPHER_get_version); |
|
412 |
} |
||
413 |
|||
414 |
395 |
StackOfX509 CloneSSLCerts(X509Pointer&& cert, |
|
415 |
const STACK_OF(X509)* const ssl_certs) { |
||
416 |
790 |
StackOfX509 peer_certs(sk_X509_new(nullptr)); |
|
417 |
✗✓ | 395 |
if (cert) |
418 |
sk_X509_push(peer_certs.get(), cert.release()); |
||
419 |
✓✓ | 1128 |
for (int i = 0; i < sk_X509_num(ssl_certs); i++) { |
420 |
733 |
X509Pointer cert(X509_dup(sk_X509_value(ssl_certs, i))); |
|
421 |
✓✗✗✓ ✗✓ |
733 |
if (!cert || !sk_X509_push(peer_certs.get(), cert.get())) |
422 |
return StackOfX509(); |
||
423 |
// `cert` is now managed by the stack. |
||
424 |
733 |
cert.release(); |
|
425 |
} |
||
426 |
395 |
return peer_certs; |
|
427 |
} |
||
428 |
|||
429 |
395 |
MaybeLocal<Object> AddIssuerChainToObject( |
|
430 |
X509Pointer* cert, |
||
431 |
Local<Object> object, |
||
432 |
StackOfX509&& peer_certs, |
||
433 |
Environment* const env) { |
||
434 |
395 |
Local<Context> context = env->isolate()->GetCurrentContext(); |
|
435 |
395 |
cert->reset(sk_X509_delete(peer_certs.get(), 0)); |
|
436 |
for (;;) { |
||
437 |
int i; |
||
438 |
✓✓ | 399 |
for (i = 0; i < sk_X509_num(peer_certs.get()); i++) { |
439 |
338 |
X509* ca = sk_X509_value(peer_certs.get(), i); |
|
440 |
✓✓ | 338 |
if (X509_check_issued(ca, cert->get()) != X509_V_OK) |
441 |
2 |
continue; |
|
442 |
|||
443 |
Local<Object> ca_info; |
||
444 |
336 |
MaybeLocal<Object> maybe_ca_info = X509ToObject(env, ca); |
|
445 |
✗✓ | 336 |
if (!maybe_ca_info.ToLocal(&ca_info)) |
446 |
return MaybeLocal<Object>(); |
||
447 |
|||
448 |
✗✓ | 672 |
if (!Set<Object>(context, object, env->issuercert_string(), ca_info)) |
449 |
return MaybeLocal<Object>(); |
||
450 |
336 |
object = ca_info; |
|
451 |
|||
452 |
// NOTE: Intentionally freeing cert that is not used anymore. |
||
453 |
// Delete cert and continue aggregating issuers. |
||
454 |
336 |
cert->reset(sk_X509_delete(peer_certs.get(), i)); |
|
455 |
336 |
break; |
|
456 |
} |
||
457 |
|||
458 |
// Issuer not found, break out of the loop. |
||
459 |
✓✓ | 397 |
if (i == sk_X509_num(peer_certs.get())) |
460 |
395 |
break; |
|
461 |
2 |
} |
|
462 |
395 |
return MaybeLocal<Object>(object); |
|
463 |
} |
||
464 |
|||
465 |
395 |
MaybeLocal<Object> GetLastIssuedCert( |
|
466 |
X509Pointer* cert, |
||
467 |
const SSLPointer& ssl, |
||
468 |
Local<Object> issuer_chain, |
||
469 |
Environment* const env) { |
||
470 |
395 |
Local<Context> context = env->isolate()->GetCurrentContext(); |
|
471 |
✓✓ | 761 |
while (X509_check_issued(cert->get(), cert->get()) != X509_V_OK) { |
472 |
X509* ca; |
||
473 |
✗✓ | 367 |
if (SSL_CTX_get_issuer(SSL_get_SSL_CTX(ssl.get()), cert->get(), &ca) <= 0) |
474 |
1 |
break; |
|
475 |
|||
476 |
Local<Object> ca_info; |
||
477 |
367 |
MaybeLocal<Object> maybe_ca_info = X509ToObject(env, ca); |
|
478 |
✗✓ | 367 |
if (!maybe_ca_info.ToLocal(&ca_info)) |
479 |
return MaybeLocal<Object>(); |
||
480 |
|||
481 |
✗✓ | 734 |
if (!Set<Object>(context, issuer_chain, env->issuercert_string(), ca_info)) |
482 |
return MaybeLocal<Object>(); |
||
483 |
367 |
issuer_chain = ca_info; |
|
484 |
|||
485 |
// Take the value of cert->get() before the call to cert->reset() |
||
486 |
// in order to compare it to ca after and provide a way to exit this loop |
||
487 |
// in case it gets stuck. |
||
488 |
367 |
X509* value_before_reset = cert->get(); |
|
489 |
|||
490 |
// Delete previous cert and continue aggregating issuers. |
||
491 |
367 |
cert->reset(ca); |
|
492 |
|||
493 |
✓✓ | 367 |
if (value_before_reset == ca) |
494 |
1 |
break; |
|
495 |
} |
||
496 |
395 |
return MaybeLocal<Object>(issuer_chain); |
|
497 |
} |
||
498 |
|||
499 |
2310 |
void AddFingerprintDigest( |
|
500 |
const unsigned char* md, |
||
501 |
unsigned int md_size, |
||
502 |
char (*fingerprint)[3 * EVP_MAX_MD_SIZE + 1]) { |
||
503 |
unsigned int i; |
||
504 |
2310 |
const char hex[] = "0123456789ABCDEF"; |
|
505 |
|||
506 |
✓✓ | 62370 |
for (i = 0; i < md_size; i++) { |
507 |
60060 |
(*fingerprint)[3*i] = hex[(md[i] & 0xf0) >> 4]; |
|
508 |
60060 |
(*fingerprint)[(3*i)+1] = hex[(md[i] & 0x0f)]; |
|
509 |
60060 |
(*fingerprint)[(3*i)+2] = ':'; |
|
510 |
} |
||
511 |
|||
512 |
✓✗ | 2310 |
if (md_size > 0) { |
513 |
2310 |
(*fingerprint)[(3*(md_size-1))+2] = '\0'; |
|
514 |
} else { |
||
515 |
(*fingerprint)[0] = '\0'; |
||
516 |
} |
||
517 |
2310 |
} |
|
518 |
|||
519 |
33 |
MaybeLocal<Value> GetCurveASN1Name(Environment* env, const int nid) { |
|
520 |
33 |
const char* nist = OBJ_nid2sn(nid); |
|
521 |
return nist != nullptr ? |
||
522 |
MaybeLocal<Value>(OneByteString(env->isolate(), nist)) : |
||
523 |
✓✗ | 66 |
MaybeLocal<Value>(Undefined(env->isolate())); |
524 |
} |
||
525 |
|||
526 |
33 |
MaybeLocal<Value> GetCurveNistName(Environment* env, const int nid) { |
|
527 |
33 |
const char* nist = EC_curve_nid2nist(nid); |
|
528 |
return nist != nullptr ? |
||
529 |
MaybeLocal<Value>(OneByteString(env->isolate(), nist)) : |
||
530 |
✓✗ | 66 |
MaybeLocal<Value>(Undefined(env->isolate())); |
531 |
} |
||
532 |
|||
533 |
33 |
MaybeLocal<Value> GetECPubKey( |
|
534 |
Environment* env, |
||
535 |
const EC_GROUP* group, |
||
536 |
const ECPointer& ec) { |
||
537 |
33 |
const EC_POINT* pubkey = EC_KEY_get0_public_key(ec.get()); |
|
538 |
✗✓ | 33 |
if (pubkey == nullptr) |
539 |
return Undefined(env->isolate()); |
||
540 |
|||
541 |
33 |
return ECPointToBuffer( |
|
542 |
env, |
||
543 |
group, |
||
544 |
pubkey, |
||
545 |
33 |
EC_KEY_get_conv_form(ec.get()), |
|
546 |
66 |
nullptr).FromMaybe(Local<Object>()); |
|
547 |
} |
||
548 |
|||
549 |
33 |
MaybeLocal<Value> GetECGroup( |
|
550 |
Environment* env, |
||
551 |
const EC_GROUP* group, |
||
552 |
const ECPointer& ec) { |
||
553 |
✗✓ | 33 |
if (group == nullptr) |
554 |
return Undefined(env->isolate()); |
||
555 |
|||
556 |
33 |
int bits = EC_GROUP_order_bits(group); |
|
557 |
✗✓ | 33 |
if (bits <= 0) |
558 |
return Undefined(env->isolate()); |
||
559 |
|||
560 |
66 |
return Integer::New(env->isolate(), bits); |
|
561 |
} |
||
562 |
|||
563 |
1121 |
MaybeLocal<Object> GetPubKey(Environment* env, const RSAPointer& rsa) { |
|
564 |
1121 |
int size = i2d_RSA_PUBKEY(rsa.get(), nullptr); |
|
565 |
✗✓ | 1121 |
CHECK_GE(size, 0); |
566 |
|||
567 |
2242 |
std::unique_ptr<BackingStore> bs; |
|
568 |
{ |
||
569 |
1121 |
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
|
570 |
1121 |
bs = ArrayBuffer::NewBackingStore(env->isolate(), size); |
|
571 |
} |
||
572 |
|||
573 |
1121 |
unsigned char* serialized = reinterpret_cast<unsigned char*>(bs->Data()); |
|
574 |
✗✓ | 1121 |
CHECK_GE(i2d_RSA_PUBKEY(rsa.get(), &serialized), 0); |
575 |
|||
576 |
1121 |
Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
|
577 |
2242 |
return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Object>()); |
|
578 |
} |
||
579 |
|||
580 |
1121 |
MaybeLocal<Value> GetExponentString( |
|
581 |
Environment* env, |
||
582 |
const BIOPointer& bio, |
||
583 |
const BIGNUM* e) { |
||
584 |
1121 |
uint64_t exponent_word = static_cast<uint64_t>(BN_get_word(e)); |
|
585 |
1121 |
uint32_t lo = static_cast<uint32_t>(exponent_word); |
|
586 |
1121 |
uint32_t hi = static_cast<uint32_t>(exponent_word >> 32); |
|
587 |
✓✗ | 1121 |
if (hi == 0) |
588 |
1121 |
BIO_printf(bio.get(), "0x%x", lo); |
|
589 |
else |
||
590 |
BIO_printf(bio.get(), "0x%x%08x", hi, lo); |
||
591 |
|||
592 |
2242 |
return ToV8Value(env, bio); |
|
593 |
} |
||
594 |
|||
595 |
1121 |
Local<Value> GetBits(Environment* env, const BIGNUM* n) { |
|
596 |
2242 |
return Integer::New(env->isolate(), BN_num_bits(n)); |
|
597 |
} |
||
598 |
|||
599 |
1121 |
MaybeLocal<Value> GetModulusString( |
|
600 |
Environment* env, |
||
601 |
const BIOPointer& bio, |
||
602 |
const BIGNUM* n) { |
||
603 |
1121 |
BN_print(bio.get(), n); |
|
604 |
2242 |
return ToV8Value(env, bio); |
|
605 |
} |
||
606 |
} // namespace |
||
607 |
|||
608 |
1156 |
MaybeLocal<Object> GetRawDERCertificate(Environment* env, X509* cert) { |
|
609 |
1156 |
int size = i2d_X509(cert, nullptr); |
|
610 |
|||
611 |
2312 |
std::unique_ptr<BackingStore> bs; |
|
612 |
{ |
||
613 |
1156 |
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
|
614 |
1156 |
bs = ArrayBuffer::NewBackingStore(env->isolate(), size); |
|
615 |
} |
||
616 |
|||
617 |
1156 |
unsigned char* serialized = reinterpret_cast<unsigned char*>(bs->Data()); |
|
618 |
✗✓ | 1156 |
CHECK_GE(i2d_X509(cert, &serialized), 0); |
619 |
|||
620 |
1156 |
Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
|
621 |
2312 |
return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Object>()); |
|
622 |
} |
||
623 |
|||
624 |
1157 |
MaybeLocal<Value> GetSerialNumber(Environment* env, X509* cert) { |
|
625 |
✓✗ | 1157 |
if (ASN1_INTEGER* serial_number = X509_get_serialNumber(cert)) { |
626 |
1157 |
BignumPointer bn(ASN1_INTEGER_to_BN(serial_number, nullptr)); |
|
627 |
✓✗ | 1157 |
if (bn) { |
628 |
1157 |
char* data = BN_bn2hex(bn.get()); |
|
629 |
1157 |
ByteSource buf = ByteSource::Allocated(data, strlen(data)); |
|
630 |
✓✗ | 1157 |
if (buf) |
631 |
2314 |
return OneByteString(env->isolate(), buf.get()); |
|
632 |
} |
||
633 |
} |
||
634 |
|||
635 |
return Undefined(env->isolate()); |
||
636 |
} |
||
637 |
|||
638 |
1155 |
MaybeLocal<Value> GetKeyUsage(Environment* env, X509* cert) { |
|
639 |
StackOfASN1 eku(static_cast<STACK_OF(ASN1_OBJECT)*>( |
||
640 |
2310 |
X509_get_ext_d2i(cert, NID_ext_key_usage, nullptr, nullptr))); |
|
641 |
✓✓ | 1155 |
if (eku) { |
642 |
9 |
const int count = sk_ASN1_OBJECT_num(eku.get()); |
|
643 |
9 |
MaybeStackBuffer<Local<Value>, 16> ext_key_usage(count); |
|
644 |
char buf[256]; |
||
645 |
|||
646 |
9 |
int j = 0; |
|
647 |
✓✓ | 20 |
for (int i = 0; i < count; i++) { |
648 |
11 |
if (OBJ_obj2txt(buf, |
|
649 |
sizeof(buf), |
||
650 |
✓✗ | 22 |
sk_ASN1_OBJECT_value(eku.get(), i), 1) >= 0) { |
651 |
22 |
ext_key_usage[j++] = OneByteString(env->isolate(), buf); |
|
652 |
} |
||
653 |
} |
||
654 |
|||
655 |
18 |
return Array::New(env->isolate(), ext_key_usage.out(), count); |
|
656 |
} |
||
657 |
|||
658 |
2292 |
return Undefined(env->isolate()); |
|
659 |
} |
||
660 |
|||
661 |
2310 |
MaybeLocal<Value> GetFingerprintDigest( |
|
662 |
Environment* env, |
||
663 |
const EVP_MD* method, |
||
664 |
X509* cert) { |
||
665 |
unsigned char md[EVP_MAX_MD_SIZE]; |
||
666 |
unsigned int md_size; |
||
667 |
char fingerprint[EVP_MAX_MD_SIZE * 3 + 1]; |
||
668 |
|||
669 |
✓✗ | 2310 |
if (X509_digest(cert, method, md, &md_size)) { |
670 |
2310 |
AddFingerprintDigest(md, md_size, &fingerprint); |
|
671 |
4620 |
return OneByteString(env->isolate(), fingerprint); |
|
672 |
} |
||
673 |
return Undefined(env->isolate()); |
||
674 |
} |
||
675 |
|||
676 |
1155 |
MaybeLocal<Value> GetValidTo( |
|
677 |
Environment* env, |
||
678 |
X509* cert, |
||
679 |
const BIOPointer& bio) { |
||
680 |
1155 |
ASN1_TIME_print(bio.get(), X509_get0_notAfter(cert)); |
|
681 |
2310 |
return ToV8Value(env, bio); |
|
682 |
} |
||
683 |
|||
684 |
1155 |
MaybeLocal<Value> GetValidFrom( |
|
685 |
Environment* env, |
||
686 |
X509* cert, |
||
687 |
const BIOPointer& bio) { |
||
688 |
1155 |
ASN1_TIME_print(bio.get(), X509_get0_notBefore(cert)); |
|
689 |
2310 |
return ToV8Value(env, bio); |
|
690 |
} |
||
691 |
|||
692 |
77 |
bool SafeX509ExtPrint(const BIOPointer& out, X509_EXTENSION* ext) { |
|
693 |
77 |
const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext); |
|
694 |
|||
695 |
✓✓ | 77 |
if (method != X509V3_EXT_get_nid(NID_subject_alt_name)) |
696 |
70 |
return false; |
|
697 |
|||
698 |
7 |
GENERAL_NAMES* names = static_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(ext)); |
|
699 |
✓✓ | 7 |
if (names == nullptr) |
700 |
1 |
return false; |
|
701 |
|||
702 |
✓✓ | 24 |
for (int i = 0; i < sk_GENERAL_NAME_num(names); i++) { |
703 |
18 |
GENERAL_NAME* gen = sk_GENERAL_NAME_value(names, i); |
|
704 |
|||
705 |
✓✓ | 18 |
if (i != 0) |
706 |
12 |
BIO_write(out.get(), ", ", 2); |
|
707 |
|||
708 |
✓✓ | 18 |
if (gen->type == GEN_DNS) { |
709 |
12 |
ASN1_IA5STRING* name = gen->d.dNSName; |
|
710 |
|||
711 |
12 |
BIO_write(out.get(), "DNS:", 4); |
|
712 |
12 |
BIO_write(out.get(), name->data, name->length); |
|
713 |
} else { |
||
714 |
6 |
STACK_OF(CONF_VALUE)* nval = i2v_GENERAL_NAME( |
|
715 |
const_cast<X509V3_EXT_METHOD*>(method), gen, nullptr); |
||
716 |
✗✓ | 6 |
if (nval == nullptr) |
717 |
return false; |
||
718 |
6 |
X509V3_EXT_val_prn(out.get(), nval, 0, 0); |
|
719 |
6 |
sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); |
|
720 |
} |
||
721 |
} |
||
722 |
6 |
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); |
|
723 |
|||
724 |
6 |
return true; |
|
725 |
} |
||
726 |
|||
727 |
1155 |
MaybeLocal<Value> GetIssuerString( |
|
728 |
Environment* env, |
||
729 |
const BIOPointer& bio, |
||
730 |
X509* cert) { |
||
731 |
1155 |
X509_NAME* issuer_name = X509_get_issuer_name(cert); |
|
732 |
✗✓ | 1155 |
if (X509_NAME_print_ex(bio.get(), issuer_name, 0, X509_NAME_FLAGS) <= 0) { |
733 |
USE(BIO_reset(bio.get())); |
||
734 |
return Undefined(env->isolate()); |
||
735 |
} |
||
736 |
|||
737 |
2310 |
return ToV8Value(env, bio); |
|
738 |
} |
||
739 |
|||
740 |
1155 |
MaybeLocal<Value> GetSubject( |
|
741 |
Environment* env, |
||
742 |
const BIOPointer& bio, |
||
743 |
X509* cert) { |
||
744 |
1155 |
if (X509_NAME_print_ex( |
|
745 |
bio.get(), |
||
746 |
1155 |
X509_get_subject_name(cert), |
|
747 |
0, |
||
748 |
✗✓ | 1155 |
X509_NAME_FLAGS) <= 0) { |
749 |
USE(BIO_reset(bio.get())); |
||
750 |
return Undefined(env->isolate()); |
||
751 |
} |
||
752 |
|||
753 |
2310 |
return ToV8Value(env, bio); |
|
754 |
} |
||
755 |
|||
756 |
53 |
MaybeLocal<Value> GetCipherName(Environment* env, const SSLPointer& ssl) { |
|
757 |
53 |
return GetCipherName(env, SSL_get_current_cipher(ssl.get())); |
|
758 |
} |
||
759 |
|||
760 |
53 |
MaybeLocal<Value> GetCipherStandardName( |
|
761 |
Environment* env, |
||
762 |
const SSLPointer& ssl) { |
||
763 |
53 |
return GetCipherStandardName(env, SSL_get_current_cipher(ssl.get())); |
|
764 |
} |
||
765 |
|||
766 |
53 |
MaybeLocal<Value> GetCipherVersion(Environment* env, const SSLPointer& ssl) { |
|
767 |
53 |
return GetCipherVersion(env, SSL_get_current_cipher(ssl.get())); |
|
768 |
} |
||
769 |
|||
770 |
MaybeLocal<Array> GetClientHelloCiphers( |
||
771 |
Environment* env, |
||
772 |
const SSLPointer& ssl) { |
||
773 |
EscapableHandleScope scope(env->isolate()); |
||
774 |
const unsigned char* buf; |
||
775 |
size_t len = SSL_client_hello_get0_ciphers(ssl.get(), &buf); |
||
776 |
size_t count = len / 2; |
||
777 |
MaybeStackBuffer<Local<Value>, 16> ciphers(count); |
||
778 |
int j = 0; |
||
779 |
for (size_t n = 0; n < len; n += 2) { |
||
780 |
const SSL_CIPHER* cipher = SSL_CIPHER_find(ssl.get(), buf); |
||
781 |
buf += 2; |
||
782 |
Local<Object> obj = Object::New(env->isolate()); |
||
783 |
if (!Set(env->context(), |
||
784 |
obj, |
||
785 |
env->name_string(), |
||
786 |
GetCipherName(env, cipher)) || |
||
787 |
!Set(env->context(), |
||
788 |
obj, |
||
789 |
env->standard_name_string(), |
||
790 |
GetCipherStandardName(env, cipher)) || |
||
791 |
!Set(env->context(), |
||
792 |
obj, |
||
793 |
env->version_string(), |
||
794 |
GetCipherVersion(env, cipher))) { |
||
795 |
return MaybeLocal<Array>(); |
||
796 |
} |
||
797 |
ciphers[j++] = obj; |
||
798 |
} |
||
799 |
Local<Array> ret = Array::New(env->isolate(), ciphers.out(), count); |
||
800 |
return scope.Escape(ret); |
||
801 |
} |
||
802 |
|||
803 |
|||
804 |
53 |
MaybeLocal<Object> GetCipherInfo(Environment* env, const SSLPointer& ssl) { |
|
805 |
✗✓ | 53 |
if (SSL_get_current_cipher(ssl.get()) == nullptr) |
806 |
return MaybeLocal<Object>(); |
||
807 |
53 |
EscapableHandleScope scope(env->isolate()); |
|
808 |
53 |
Local<Object> info = Object::New(env->isolate()); |
|
809 |
|||
810 |
106 |
if (!Set<Value>(env->context(), |
|
811 |
info, |
||
812 |
env->name_string(), |
||
813 |
53 |
GetCipherName(env, ssl)) || |
|
814 |
✓✗ | 106 |
!Set<Value>(env->context(), |
815 |
info, |
||
816 |
env->standard_name_string(), |
||
817 |
✓✗ | 106 |
GetCipherStandardName(env, ssl)) || |
818 |
✗✓✗✓ |
159 |
!Set<Value>(env->context(), |
819 |
info, |
||
820 |
env->version_string(), |
||
821 |
GetCipherVersion(env, ssl))) { |
||
822 |
return MaybeLocal<Object>(); |
||
823 |
} |
||
824 |
|||
825 |
53 |
return scope.Escape(info); |
|
826 |
} |
||
827 |
|||
828 |
821 |
MaybeLocal<Object> GetEphemeralKey(Environment* env, const SSLPointer& ssl) { |
|
829 |
✗✓ | 821 |
CHECK_EQ(SSL_is_server(ssl.get()), 0); |
830 |
EVP_PKEY* raw_key; |
||
831 |
|||
832 |
821 |
EscapableHandleScope scope(env->isolate()); |
|
833 |
821 |
Local<Object> info = Object::New(env->isolate()); |
|
834 |
✓✓ | 821 |
if (!SSL_get_server_tmp_key(ssl.get(), &raw_key)) |
835 |
30 |
return scope.Escape(info); |
|
836 |
|||
837 |
791 |
Local<Context> context = env->context(); |
|
838 |
1582 |
crypto::EVPKeyPointer key(raw_key); |
|
839 |
|||
840 |
791 |
int kid = EVP_PKEY_id(key.get()); |
|
841 |
791 |
int bits = EVP_PKEY_bits(key.get()); |
|
842 |
✓✓✗ | 791 |
switch (kid) { |
843 |
7 |
case EVP_PKEY_DH: |
|
844 |
✓✗ | 28 |
if (!Set<String>(context, info, env->type_string(), env->dh_string()) || |
845 |
✗✓✗✓ |
28 |
!Set<Integer>(context, |
846 |
info, |
||
847 |
env->size_string(), |
||
848 |
Integer::New(env->isolate(), bits))) { |
||
849 |
return MaybeLocal<Object>(); |
||
850 |
} |
||
851 |
7 |
break; |
|
852 |
784 |
case EVP_PKEY_EC: |
|
853 |
case EVP_PKEY_X25519: |
||
854 |
case EVP_PKEY_X448: |
||
855 |
{ |
||
856 |
const char* curve_name; |
||
857 |
✓✓ | 784 |
if (kid == EVP_PKEY_EC) { |
858 |
10 |
ECKeyPointer ec(EVP_PKEY_get1_EC_KEY(key.get())); |
|
859 |
5 |
int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec.get())); |
|
860 |
5 |
curve_name = OBJ_nid2sn(nid); |
|
861 |
} else { |
||
862 |
779 |
curve_name = OBJ_nid2sn(kid); |
|
863 |
} |
||
864 |
2352 |
if (!Set<String>(context, |
|
865 |
info, |
||
866 |
env->type_string(), |
||
867 |
784 |
env->ecdh_string()) || |
|
868 |
✓✗ | 2352 |
!Set<String>(context, |
869 |
info, |
||
870 |
env->name_string(), |
||
871 |
✓✗ | 1568 |
OneByteString(env->isolate(), curve_name)) || |
872 |
✗✓✗✓ |
3136 |
!Set<Integer>(context, |
873 |
info, |
||
874 |
env->size_string(), |
||
875 |
Integer::New(env->isolate(), bits))) { |
||
876 |
return MaybeLocal<Object>(); |
||
877 |
784 |
} |
|
878 |
} |
||
879 |
784 |
break; |
|
880 |
} |
||
881 |
|||
882 |
791 |
return scope.Escape(info); |
|
883 |
} |
||
884 |
|||
885 |
59 |
MaybeLocal<Object> ECPointToBuffer(Environment* env, |
|
886 |
const EC_GROUP* group, |
||
887 |
const EC_POINT* point, |
||
888 |
point_conversion_form_t form, |
||
889 |
const char** error) { |
||
890 |
59 |
size_t len = EC_POINT_point2oct(group, point, form, nullptr, 0, nullptr); |
|
891 |
✗✓ | 59 |
if (len == 0) { |
892 |
if (error != nullptr) *error = "Failed to get public key length"; |
||
893 |
return MaybeLocal<Object>(); |
||
894 |
} |
||
895 |
|||
896 |
59 |
std::unique_ptr<BackingStore> bs; |
|
897 |
{ |
||
898 |
59 |
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
|
899 |
59 |
bs = ArrayBuffer::NewBackingStore(env->isolate(), len); |
|
900 |
} |
||
901 |
|||
902 |
118 |
len = EC_POINT_point2oct(group, |
|
903 |
point, |
||
904 |
form, |
||
905 |
59 |
reinterpret_cast<unsigned char*>(bs->Data()), |
|
906 |
bs->ByteLength(), |
||
907 |
nullptr); |
||
908 |
✗✓ | 59 |
if (len == 0) { |
909 |
if (error != nullptr) *error = "Failed to get public key"; |
||
910 |
return MaybeLocal<Object>(); |
||
911 |
} |
||
912 |
|||
913 |
59 |
Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
|
914 |
118 |
return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Object>()); |
|
915 |
} |
||
916 |
|||
917 |
✓✓ | 444 |
MaybeLocal<Value> GetPeerCert( |
918 |
Environment* env, |
||
919 |
const SSLPointer& ssl, |
||
920 |
bool abbreviated, |
||
921 |
bool is_server) { |
||
922 |
444 |
ClearErrorOnReturn clear_error_on_return; |
|
923 |
Local<Object> result; |
||
924 |
MaybeLocal<Object> maybe_cert; |
||
925 |
|||
926 |
// NOTE: This is because of the odd OpenSSL behavior. On client `cert_chain` |
||
927 |
// contains the `peer_certificate`, but on server it doesn't. |
||
928 |
✓✓ | 888 |
X509Pointer cert(is_server ? SSL_get_peer_certificate(ssl.get()) : nullptr); |
929 |
444 |
STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(ssl.get()); |
|
930 |
✓✓✓✓ ✗✓✓✓ |
444 |
if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) |
931 |
10 |
return Undefined(env->isolate()); |
|
932 |
|||
933 |
// Short result requested. |
||
934 |
✓✓ | 439 |
if (abbreviated) { |
935 |
maybe_cert = |
||
936 |
✓✓ | 44 |
X509ToObject(env, cert ? cert.get() : sk_X509_value(ssl_certs, 0)); |
937 |
✓✗ | 88 |
return maybe_cert.ToLocal(&result) ? result : MaybeLocal<Value>(); |
938 |
} |
||
939 |
|||
940 |
790 |
StackOfX509 peer_certs = CloneSSLCerts(std::move(cert), ssl_certs); |
|
941 |
✗✓ | 395 |
if (peer_certs == nullptr) |
942 |
return Undefined(env->isolate()); |
||
943 |
|||
944 |
// First and main certificate. |
||
945 |
790 |
X509Pointer first_cert(sk_X509_value(peer_certs.get(), 0)); |
|
946 |
✗✓ | 395 |
CHECK(first_cert); |
947 |
395 |
maybe_cert = X509ToObject(env, first_cert.release()); |
|
948 |
✗✓ | 395 |
if (!maybe_cert.ToLocal(&result)) |
949 |
return MaybeLocal<Value>(); |
||
950 |
|||
951 |
Local<Object> issuer_chain; |
||
952 |
MaybeLocal<Object> maybe_issuer_chain; |
||
953 |
|||
954 |
maybe_issuer_chain = |
||
955 |
AddIssuerChainToObject( |
||
956 |
&cert, |
||
957 |
result, |
||
958 |
395 |
std::move(peer_certs), |
|
959 |
395 |
env); |
|
960 |
✗✓ | 395 |
if (!maybe_issuer_chain.ToLocal(&issuer_chain)) |
961 |
return MaybeLocal<Value>(); |
||
962 |
|||
963 |
maybe_issuer_chain = |
||
964 |
GetLastIssuedCert( |
||
965 |
&cert, |
||
966 |
ssl, |
||
967 |
issuer_chain, |
||
968 |
395 |
env); |
|
969 |
|||
970 |
issuer_chain.Clear(); |
||
971 |
✗✓ | 395 |
if (!maybe_issuer_chain.ToLocal(&issuer_chain)) |
972 |
return MaybeLocal<Value>(); |
||
973 |
|||
974 |
// Last certificate should be self-signed. |
||
975 |
✓✓ | 789 |
if (X509_check_issued(cert.get(), cert.get()) == X509_V_OK && |
976 |
✗✓✗✓ |
1183 |
!Set<Object>(env->context(), |
977 |
issuer_chain, |
||
978 |
env->issuercert_string(), |
||
979 |
issuer_chain)) { |
||
980 |
return MaybeLocal<Value>(); |
||
981 |
} |
||
982 |
|||
983 |
395 |
return result; |
|
984 |
} |
||
985 |
|||
986 |
1154 |
MaybeLocal<Object> X509ToObject(Environment* env, X509* cert) { |
|
987 |
1154 |
EscapableHandleScope scope(env->isolate()); |
|
988 |
1154 |
Local<Context> context = env->context(); |
|
989 |
1154 |
Local<Object> info = Object::New(env->isolate()); |
|
990 |
|||
991 |
2308 |
BIOPointer bio(BIO_new(BIO_s_mem())); |
|
992 |
|||
993 |
2308 |
if (!Set<Value>(context, |
|
994 |
info, |
||
995 |
env->subject_string(), |
||
996 |
1154 |
GetSubject(env, bio, cert)) || |
|
997 |
✓✗ | 2308 |
!Set<Value>(context, |
998 |
info, |
||
999 |
env->issuer_string(), |
||
1000 |
1154 |
GetIssuerString(env, bio, cert)) || |
|
1001 |
✓✗ | 2308 |
!Set<Value>(context, |
1002 |
info, |
||
1003 |
env->subjectaltname_string(), |
||
1004 |
✓✗ | 2308 |
GetInfoString<NID_subject_alt_name>(env, bio, cert)) || |
1005 |
✗✓✗✓ |
3462 |
!Set<Value>(context, |
1006 |
info, |
||
1007 |
env->infoaccess_string(), |
||
1008 |
GetInfoString<NID_info_access>(env, bio, cert))) { |
||
1009 |
return MaybeLocal<Object>(); |
||
1010 |
} |
||
1011 |
|||
1012 |
2308 |
EVPKeyPointer pkey(X509_get_pubkey(cert)); |
|
1013 |
1154 |
RSAPointer rsa; |
|
1014 |
1154 |
ECPointer ec; |
|
1015 |
✓✗ | 1154 |
if (pkey) { |
1016 |
✓✓✗ | 1154 |
switch (EVP_PKEY_id(pkey.get())) { |
1017 |
1121 |
case EVP_PKEY_RSA: |
|
1018 |
1121 |
rsa.reset(EVP_PKEY_get1_RSA(pkey.get())); |
|
1019 |
1121 |
break; |
|
1020 |
33 |
case EVP_PKEY_EC: |
|
1021 |
33 |
ec.reset(EVP_PKEY_get1_EC_KEY(pkey.get())); |
|
1022 |
33 |
break; |
|
1023 |
} |
||
1024 |
} |
||
1025 |
|||
1026 |
✓✓ | 1154 |
if (rsa) { |
1027 |
const BIGNUM* n; |
||
1028 |
const BIGNUM* e; |
||
1029 |
1121 |
RSA_get0_key(rsa.get(), &n, &e, nullptr); |
|
1030 |
2242 |
if (!Set<Value>(context, |
|
1031 |
info, |
||
1032 |
env->modulus_string(), |
||
1033 |
1121 |
GetModulusString(env, bio, n)) || |
|
1034 |
✓✗ | 3363 |
!Set<Value>(context, info, env->bits_string(), GetBits(env, n)) || |
1035 |
✓✗ | 2242 |
!Set<Value>(context, |
1036 |
info, |
||
1037 |
env->exponent_string(), |
||
1038 |
✓✗ | 2242 |
GetExponentString(env, bio, e)) || |
1039 |
✗✓✗✓ |
3363 |
!Set<Object>(context, |
1040 |
info, |
||
1041 |
env->pubkey_string(), |
||
1042 |
GetPubKey(env, rsa))) { |
||
1043 |
return MaybeLocal<Object>(); |
||
1044 |
} |
||
1045 |
✓✗ | 33 |
} else if (ec) { |
1046 |
33 |
const EC_GROUP* group = EC_KEY_get0_group(ec.get()); |
|
1047 |
|||
1048 |
66 |
if (!Set<Value>(context, |
|
1049 |
info, |
||
1050 |
env->bits_string(), |
||
1051 |
✓✗ | 66 |
GetECGroup(env, group, ec)) || |
1052 |
✗✓✗✓ |
99 |
!Set<Value>(context, |
1053 |
info, |
||
1054 |
env->pubkey_string(), |
||
1055 |
GetECPubKey(env, group, ec))) { |
||
1056 |
return MaybeLocal<Object>(); |
||
1057 |
} |
||
1058 |
|||
1059 |
33 |
const int nid = EC_GROUP_get_curve_name(group); |
|
1060 |
✓✗ | 33 |
if (nid != 0) { |
1061 |
// Curve is well-known, get its OID and NIST nick-name (if it has one). |
||
1062 |
|||
1063 |
66 |
if (!Set<Value>(context, |
|
1064 |
info, |
||
1065 |
env->asn1curve_string(), |
||
1066 |
✓✗ | 66 |
GetCurveASN1Name(env, nid)) || |
1067 |
✗✓✗✓ |
99 |
!Set<Value>(context, |
1068 |
info, |
||
1069 |
env->nistcurve_string(), |
||
1070 |
GetCurveNistName(env, nid))) { |
||
1071 |
return MaybeLocal<Object>(); |
||
1072 |
} |
||
1073 |
} else { |
||
1074 |
// Unnamed curves can be described by their mathematical properties, |
||
1075 |
// but aren't used much (at all?) with X.509/TLS. Support later if needed. |
||
1076 |
} |
||
1077 |
} |
||
1078 |
|||
1079 |
// pkey, rsa, and ec pointers are no longer needed. |
||
1080 |
1154 |
pkey.reset(); |
|
1081 |
1154 |
rsa.reset(); |
|
1082 |
1154 |
ec.reset(); |
|
1083 |
|||
1084 |
2308 |
if (!Set<Value>(context, |
|
1085 |
info, |
||
1086 |
env->valid_from_string(), |
||
1087 |
✓✗ | 2308 |
GetValidFrom(env, cert, bio)) || |
1088 |
✗✓✗✓ |
3462 |
!Set<Value>(context, |
1089 |
info, |
||
1090 |
env->valid_to_string(), |
||
1091 |
GetValidTo(env, cert, bio))) { |
||
1092 |
return MaybeLocal<Object>(); |
||
1093 |
} |
||
1094 |
|||
1095 |
// bio is no longer needed |
||
1096 |
1154 |
bio.reset(); |
|
1097 |
|||
1098 |
2308 |
if (!Set<Value>(context, |
|
1099 |
info, |
||
1100 |
env->fingerprint_string(), |
||
1101 |
1154 |
GetFingerprintDigest(env, EVP_sha1(), cert)) || |
|
1102 |
✓✗ | 2308 |
!Set<Value>(context, |
1103 |
info, |
||
1104 |
env->fingerprint256_string(), |
||
1105 |
1154 |
GetFingerprintDigest(env, EVP_sha256(), cert)) || |
|
1106 |
✓✗ | 2308 |
!Set<Value>(context, |
1107 |
info, |
||
1108 |
env->ext_key_usage_string(), |
||
1109 |
1154 |
GetKeyUsage(env, cert)) || |
|
1110 |
✓✗ | 2308 |
!Set<Value>(context, |
1111 |
info, |
||
1112 |
env->serial_number_string(), |
||
1113 |
✓✗ | 2308 |
GetSerialNumber(env, cert)) || |
1114 |
✗✓✗✓ |
3462 |
!Set<Object>(context, |
1115 |
info, |
||
1116 |
env->raw_string(), |
||
1117 |
GetRawDERCertificate(env, cert))) { |
||
1118 |
return MaybeLocal<Object>(); |
||
1119 |
} |
||
1120 |
|||
1121 |
1154 |
return scope.Escape(info); |
|
1122 |
} |
||
1123 |
|||
1124 |
} // namespace crypto |
||
1125 |
} // namespace node |
Generated by: GCOVR (Version 4.2) |