• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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