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