1 #include "crypto/crypto_spkac.h"
2 #include "crypto/crypto_common.h"
3 #include "crypto/crypto_util.h"
4 #include "env-inl.h"
5 #include "memory_tracker-inl.h"
6 #include "node.h"
7 #include "v8.h"
8
9 namespace node {
10
11 using v8::Context;
12 using v8::FunctionCallbackInfo;
13 using v8::Local;
14 using v8::Object;
15 using v8::Value;
16
17 namespace crypto {
18 namespace SPKAC {
VerifySpkac(const ArrayBufferOrViewContents<char> & input)19 bool VerifySpkac(const ArrayBufferOrViewContents<char>& input) {
20 size_t length = input.size();
21 #ifdef OPENSSL_IS_BORINGSSL
22 // OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters,
23 // while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not.
24 // As such, we trim those characters here for compatibility.
25 length = std::string(input.data()).find_last_not_of(" \n\r\t") + 1;
26 #endif
27 NetscapeSPKIPointer spki(
28 NETSCAPE_SPKI_b64_decode(input.data(), length));
29 if (!spki)
30 return false;
31
32 EVPKeyPointer pkey(X509_PUBKEY_get(spki->spkac->pubkey));
33 if (!pkey)
34 return false;
35
36 return NETSCAPE_SPKI_verify(spki.get(), pkey.get()) > 0;
37 }
38
VerifySpkac(const FunctionCallbackInfo<Value> & args)39 void VerifySpkac(const FunctionCallbackInfo<Value>& args) {
40 Environment* env = Environment::GetCurrent(args);
41 ArrayBufferOrViewContents<char> input(args[0]);
42 if (input.size() == 0)
43 return args.GetReturnValue().SetEmptyString();
44
45 if (UNLIKELY(!input.CheckSizeInt32()))
46 return THROW_ERR_OUT_OF_RANGE(env, "spkac is too large");
47
48 args.GetReturnValue().Set(VerifySpkac(input));
49 }
50
ExportPublicKey(Environment * env,const ArrayBufferOrViewContents<char> & input)51 ByteSource ExportPublicKey(Environment* env,
52 const ArrayBufferOrViewContents<char>& input) {
53 BIOPointer bio(BIO_new(BIO_s_mem()));
54 if (!bio) return ByteSource();
55
56 size_t length = input.size();
57 #ifdef OPENSSL_IS_BORINGSSL
58 // OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters,
59 // while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not.
60 // As such, we trim those characters here for compatibility.
61 length = std::string(input.data()).find_last_not_of(" \n\r\t") + 1;
62 #endif
63 NetscapeSPKIPointer spki(
64 NETSCAPE_SPKI_b64_decode(input.data(), length));
65 if (!spki) return ByteSource();
66
67 EVPKeyPointer pkey(NETSCAPE_SPKI_get_pubkey(spki.get()));
68 if (!pkey) return ByteSource();
69
70 if (PEM_write_bio_PUBKEY(bio.get(), pkey.get()) <= 0) return ByteSource();
71
72 return ByteSource::FromBIO(bio);
73 }
74
ExportPublicKey(const FunctionCallbackInfo<Value> & args)75 void ExportPublicKey(const FunctionCallbackInfo<Value>& args) {
76 Environment* env = Environment::GetCurrent(args);
77
78 ArrayBufferOrViewContents<char> input(args[0]);
79 if (input.size() == 0) return args.GetReturnValue().SetEmptyString();
80
81 if (UNLIKELY(!input.CheckSizeInt32()))
82 return THROW_ERR_OUT_OF_RANGE(env, "spkac is too large");
83
84 ByteSource pkey = ExportPublicKey(env, input);
85 if (!pkey) return args.GetReturnValue().SetEmptyString();
86
87 args.GetReturnValue().Set(pkey.ToBuffer(env).FromMaybe(Local<Value>()));
88 }
89
ExportChallenge(const ArrayBufferOrViewContents<char> & input)90 ByteSource ExportChallenge(const ArrayBufferOrViewContents<char>& input) {
91 size_t length = input.size();
92 #ifdef OPENSSL_IS_BORINGSSL
93 // OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters,
94 // while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not.
95 // As such, we trim those characters here for compatibility.
96 length = std::string(input.data()).find_last_not_of(" \n\r\t") + 1;
97 #endif
98 NetscapeSPKIPointer sp(
99 NETSCAPE_SPKI_b64_decode(input.data(), length));
100 if (!sp)
101 return ByteSource();
102
103 unsigned char* buf = nullptr;
104 int buf_size = ASN1_STRING_to_UTF8(&buf, sp->spkac->challenge);
105 return (buf_size >= 0) ? ByteSource::Allocated(buf, buf_size) : ByteSource();
106 }
107
ExportChallenge(const FunctionCallbackInfo<Value> & args)108 void ExportChallenge(const FunctionCallbackInfo<Value>& args) {
109 Environment* env = Environment::GetCurrent(args);
110
111 ArrayBufferOrViewContents<char> input(args[0]);
112 if (input.size() == 0)
113 return args.GetReturnValue().SetEmptyString();
114
115 if (UNLIKELY(!input.CheckSizeInt32()))
116 return THROW_ERR_OUT_OF_RANGE(env, "spkac is too large");
117
118 ByteSource cert = ExportChallenge(input);
119 if (!cert)
120 return args.GetReturnValue().SetEmptyString();
121
122 Local<Value> outString =
123 Encode(env->isolate(), cert.data<char>(), cert.size(), BUFFER);
124
125 args.GetReturnValue().Set(outString);
126 }
127
Initialize(Environment * env,Local<Object> target)128 void Initialize(Environment* env, Local<Object> target) {
129 Local<Context> context = env->context();
130 SetMethodNoSideEffect(context, target, "certVerifySpkac", VerifySpkac);
131 SetMethodNoSideEffect(
132 context, target, "certExportPublicKey", ExportPublicKey);
133 SetMethodNoSideEffect(
134 context, target, "certExportChallenge", ExportChallenge);
135 }
136
RegisterExternalReferences(ExternalReferenceRegistry * registry)137 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
138 registry->Register(VerifySpkac);
139 registry->Register(ExportPublicKey);
140 registry->Register(ExportChallenge);
141 }
142 } // namespace SPKAC
143 } // namespace crypto
144 } // namespace node
145