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