/src/node/src/crypto/crypto_cipher.h
Line | Count | Source |
1 | | #ifndef SRC_CRYPTO_CRYPTO_CIPHER_H_ |
2 | | #define SRC_CRYPTO_CRYPTO_CIPHER_H_ |
3 | | |
4 | | #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
5 | | |
6 | | #include "crypto/crypto_keys.h" |
7 | | #include "crypto/crypto_util.h" |
8 | | #include "base_object.h" |
9 | | #include "env.h" |
10 | | #include "memory_tracker.h" |
11 | | #include "v8.h" |
12 | | |
13 | | #include <string> |
14 | | |
15 | | namespace node { |
16 | | namespace crypto { |
17 | | class CipherBase : public BaseObject { |
18 | | public: |
19 | | static void GetSSLCiphers(const v8::FunctionCallbackInfo<v8::Value>& args); |
20 | | static void GetCiphers(const v8::FunctionCallbackInfo<v8::Value>& args); |
21 | | |
22 | | static void Initialize(Environment* env, v8::Local<v8::Object> target); |
23 | | static void RegisterExternalReferences(ExternalReferenceRegistry* registry); |
24 | | |
25 | | void MemoryInfo(MemoryTracker* tracker) const override; |
26 | | SET_MEMORY_INFO_NAME(CipherBase) |
27 | | SET_SELF_SIZE(CipherBase) |
28 | | |
29 | | protected: |
30 | | enum CipherKind { |
31 | | kCipher, |
32 | | kDecipher |
33 | | }; |
34 | | enum UpdateResult { |
35 | | kSuccess, |
36 | | kErrorMessageSize, |
37 | | kErrorState |
38 | | }; |
39 | | enum AuthTagState { |
40 | | kAuthTagUnknown, |
41 | | kAuthTagSetByUser, |
42 | | kAuthTagComputed, |
43 | | }; |
44 | | static const unsigned kNoAuthTagLength = static_cast<unsigned>(-1); |
45 | | |
46 | | void CommonInit(const char* cipher_type, |
47 | | const ncrypto::Cipher& cipher, |
48 | | const unsigned char* key, |
49 | | int key_len, |
50 | | const unsigned char* iv, |
51 | | int iv_len, |
52 | | unsigned int auth_tag_len); |
53 | | void InitIv(const char* cipher_type, |
54 | | const ByteSource& key_buf, |
55 | | const ArrayBufferOrViewContents<unsigned char>& iv_buf, |
56 | | unsigned int auth_tag_len); |
57 | | bool InitAuthenticated(const char* cipher_type, |
58 | | int iv_len, |
59 | | unsigned int auth_tag_len); |
60 | | bool CheckCCMMessageLength(int message_len); |
61 | | UpdateResult Update(const char* data, size_t len, |
62 | | std::unique_ptr<v8::BackingStore>* out); |
63 | | bool Final(std::unique_ptr<v8::BackingStore>* out); |
64 | | bool SetAutoPadding(bool auto_padding); |
65 | | |
66 | | bool IsAuthenticatedMode() const; |
67 | | bool SetAAD(const ArrayBufferOrViewContents<unsigned char>& data, |
68 | | int plaintext_len); |
69 | | |
70 | | static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
71 | | static void Update(const v8::FunctionCallbackInfo<v8::Value>& args); |
72 | | static void Final(const v8::FunctionCallbackInfo<v8::Value>& args); |
73 | | static void SetAutoPadding(const v8::FunctionCallbackInfo<v8::Value>& args); |
74 | | |
75 | | static void GetAuthTag(const v8::FunctionCallbackInfo<v8::Value>& args); |
76 | | static void SetAuthTag(const v8::FunctionCallbackInfo<v8::Value>& args); |
77 | | static void SetAAD(const v8::FunctionCallbackInfo<v8::Value>& args); |
78 | | |
79 | | CipherBase(Environment* env, v8::Local<v8::Object> wrap, CipherKind kind); |
80 | | |
81 | | private: |
82 | | ncrypto::CipherCtxPointer ctx_; |
83 | | const CipherKind kind_; |
84 | | AuthTagState auth_tag_state_; |
85 | | unsigned int auth_tag_len_; |
86 | | char auth_tag_[ncrypto::Cipher::MAX_AUTH_TAG_LENGTH]; |
87 | | bool pending_auth_failed_; |
88 | | int max_message_size_; |
89 | | }; |
90 | | |
91 | | class PublicKeyCipher { |
92 | | public: |
93 | | using Cipher_t = |
94 | | ncrypto::DataPointer(const ncrypto::EVPKeyPointer&, |
95 | | const ncrypto::Cipher::CipherParams& params, |
96 | | const ncrypto::Buffer<const void>); |
97 | | |
98 | | enum Operation { |
99 | | kPublic, |
100 | | kPrivate |
101 | | }; |
102 | | |
103 | | template <Cipher_t cipher> |
104 | | static bool Cipher(Environment* env, |
105 | | const ncrypto::EVPKeyPointer& pkey, |
106 | | int padding, |
107 | | const ncrypto::Digest& digest, |
108 | | const ArrayBufferOrViewContents<unsigned char>& oaep_label, |
109 | | const ArrayBufferOrViewContents<unsigned char>& data, |
110 | | std::unique_ptr<v8::BackingStore>* out); |
111 | | |
112 | | template <Operation operation, Cipher_t cipher> |
113 | | static void Cipher(const v8::FunctionCallbackInfo<v8::Value>& args); |
114 | | }; |
115 | | |
116 | | enum WebCryptoCipherMode { |
117 | | kWebCryptoCipherEncrypt, |
118 | | kWebCryptoCipherDecrypt |
119 | | }; |
120 | | |
121 | | enum class WebCryptoCipherStatus { |
122 | | OK, |
123 | | INVALID_KEY_TYPE, |
124 | | FAILED |
125 | | }; |
126 | | |
127 | | // CipherJob is a base implementation class for implementations of |
128 | | // one-shot sync and async ciphers. It has been added primarily to |
129 | | // support the AES and RSA ciphers underlying the WebCrypt API. |
130 | | // |
131 | | // See the crypto_aes and crypto_rsa headers for examples of how to |
132 | | // use CipherJob. |
133 | | template <typename CipherTraits> |
134 | | class CipherJob final : public CryptoJob<CipherTraits> { |
135 | | public: |
136 | | using AdditionalParams = typename CipherTraits::AdditionalParameters; |
137 | | |
138 | 0 | static void New(const v8::FunctionCallbackInfo<v8::Value>& args) { |
139 | 0 | Environment* env = Environment::GetCurrent(args); |
140 | 0 | CHECK(args.IsConstructCall()); |
141 | | |
142 | 0 | CryptoJobMode mode = GetCryptoJobMode(args[0]); |
143 | |
|
144 | 0 | CHECK(args[1]->IsUint32()); // Cipher Mode |
145 | 0 | auto cipher_mode = |
146 | 0 | static_cast<WebCryptoCipherMode>(args[1].As<v8::Uint32>()->Value()); |
147 | |
|
148 | 0 | CHECK(args[2]->IsObject()); // KeyObject |
149 | 0 | KeyObjectHandle* key; |
150 | 0 | ASSIGN_OR_RETURN_UNWRAP(&key, args[2]); |
151 | 0 | CHECK_NOT_NULL(key); |
152 | | |
153 | 0 | ArrayBufferOrViewContents<char> data(args[3]); // data to operate on |
154 | 0 | if (!data.CheckSizeInt32()) |
155 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "data is too large"); |
156 | | |
157 | 0 | AdditionalParams params; |
158 | 0 | if (CipherTraits::AdditionalConfig(mode, args, 4, cipher_mode, ¶ms) |
159 | 0 | .IsNothing()) { |
160 | | // The CipherTraits::AdditionalConfig is responsible for |
161 | | // calling an appropriate THROW_CRYPTO_* variant reporting |
162 | | // whatever error caused initialization to fail. |
163 | 0 | return; |
164 | 0 | } |
165 | | |
166 | 0 | new CipherJob<CipherTraits>( |
167 | 0 | env, |
168 | 0 | args.This(), |
169 | 0 | mode, |
170 | 0 | key, |
171 | 0 | cipher_mode, |
172 | 0 | data, |
173 | 0 | std::move(params)); |
174 | 0 | } Unexecuted instantiation: node::crypto::CipherJob<node::crypto::AESCipherTraits>::New(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node::crypto::CipherJob<node::crypto::ChaCha20Poly1305CipherTraits>::New(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node::crypto::CipherJob<node::crypto::RSACipherTraits>::New(v8::FunctionCallbackInfo<v8::Value> const&) |
175 | | |
176 | | static void Initialize( |
177 | | Environment* env, |
178 | 0 | v8::Local<v8::Object> target) { |
179 | 0 | CryptoJob<CipherTraits>::Initialize(New, env, target); |
180 | 0 | } Unexecuted instantiation: node::crypto::CipherJob<node::crypto::AESCipherTraits>::Initialize(node::Environment*, v8::Local<v8::Object>) Unexecuted instantiation: node::crypto::CipherJob<node::crypto::ChaCha20Poly1305CipherTraits>::Initialize(node::Environment*, v8::Local<v8::Object>) Unexecuted instantiation: node::crypto::CipherJob<node::crypto::RSACipherTraits>::Initialize(node::Environment*, v8::Local<v8::Object>) |
181 | | |
182 | 0 | static void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
183 | 0 | CryptoJob<CipherTraits>::RegisterExternalReferences(New, registry); |
184 | 0 | } Unexecuted instantiation: node::crypto::CipherJob<node::crypto::AESCipherTraits>::RegisterExternalReferences(node::ExternalReferenceRegistry*) Unexecuted instantiation: node::crypto::CipherJob<node::crypto::ChaCha20Poly1305CipherTraits>::RegisterExternalReferences(node::ExternalReferenceRegistry*) Unexecuted instantiation: node::crypto::CipherJob<node::crypto::RSACipherTraits>::RegisterExternalReferences(node::ExternalReferenceRegistry*) |
185 | | |
186 | | CipherJob(Environment* env, |
187 | | v8::Local<v8::Object> object, |
188 | | CryptoJobMode mode, |
189 | | KeyObjectHandle* key, |
190 | | WebCryptoCipherMode cipher_mode, |
191 | | const ArrayBufferOrViewContents<char>& data, |
192 | | AdditionalParams&& params) |
193 | 0 | : CryptoJob<CipherTraits>(env, |
194 | 0 | object, |
195 | 0 | AsyncWrap::PROVIDER_CIPHERREQUEST, |
196 | 0 | mode, |
197 | 0 | std::move(params)), |
198 | 0 | key_(key->Data().addRef()), |
199 | 0 | cipher_mode_(cipher_mode), |
200 | 0 | in_(mode == kCryptoJobAsync ? data.ToCopy() : data.ToByteSource()) {}Unexecuted instantiation: node::crypto::CipherJob<node::crypto::AESCipherTraits>::CipherJob(node::Environment*, v8::Local<v8::Object>, node::crypto::CryptoJobMode, node::crypto::KeyObjectHandle*, node::crypto::WebCryptoCipherMode, node::crypto::ArrayBufferOrViewContents<char> const&, node::crypto::AESCipherConfig&&) Unexecuted instantiation: node::crypto::CipherJob<node::crypto::ChaCha20Poly1305CipherTraits>::CipherJob(node::Environment*, v8::Local<v8::Object>, node::crypto::CryptoJobMode, node::crypto::KeyObjectHandle*, node::crypto::WebCryptoCipherMode, node::crypto::ArrayBufferOrViewContents<char> const&, node::crypto::ChaCha20Poly1305CipherConfig&&) Unexecuted instantiation: node::crypto::CipherJob<node::crypto::RSACipherTraits>::CipherJob(node::Environment*, v8::Local<v8::Object>, node::crypto::CryptoJobMode, node::crypto::KeyObjectHandle*, node::crypto::WebCryptoCipherMode, node::crypto::ArrayBufferOrViewContents<char> const&, node::crypto::RSACipherConfig&&) |
201 | | |
202 | 0 | const KeyObjectData& key() const { return key_; }Unexecuted instantiation: node::crypto::CipherJob<node::crypto::AESCipherTraits>::key() const Unexecuted instantiation: node::crypto::CipherJob<node::crypto::ChaCha20Poly1305CipherTraits>::key() const Unexecuted instantiation: node::crypto::CipherJob<node::crypto::RSACipherTraits>::key() const |
203 | | |
204 | | WebCryptoCipherMode cipher_mode() const { return cipher_mode_; } |
205 | | |
206 | 0 | void DoThreadPoolWork() override { |
207 | 0 | const WebCryptoCipherStatus status = |
208 | 0 | CipherTraits::DoCipher( |
209 | 0 | AsyncWrap::env(), |
210 | 0 | key(), |
211 | 0 | cipher_mode_, |
212 | 0 | *CryptoJob<CipherTraits>::params(), |
213 | 0 | in_, |
214 | 0 | &out_); |
215 | 0 | if (status == WebCryptoCipherStatus::OK) { |
216 | | // Success! |
217 | 0 | return; |
218 | 0 | } |
219 | 0 | CryptoErrorStore* errors = CryptoJob<CipherTraits>::errors(); |
220 | 0 | errors->Capture(); |
221 | 0 | if (errors->Empty()) { |
222 | 0 | switch (status) { |
223 | 0 | case WebCryptoCipherStatus::OK: |
224 | 0 | UNREACHABLE(); |
225 | 0 | break; |
226 | 0 | case WebCryptoCipherStatus::INVALID_KEY_TYPE: |
227 | 0 | errors->Insert(NodeCryptoError::INVALID_KEY_TYPE); |
228 | 0 | break; |
229 | 0 | case WebCryptoCipherStatus::FAILED: |
230 | 0 | errors->Insert(NodeCryptoError::CIPHER_JOB_FAILED); |
231 | 0 | break; |
232 | 0 | } |
233 | 0 | } |
234 | 0 | } Unexecuted instantiation: node::crypto::CipherJob<node::crypto::AESCipherTraits>::DoThreadPoolWork() Unexecuted instantiation: node::crypto::CipherJob<node::crypto::ChaCha20Poly1305CipherTraits>::DoThreadPoolWork() Unexecuted instantiation: node::crypto::CipherJob<node::crypto::RSACipherTraits>::DoThreadPoolWork() |
235 | | |
236 | | v8::Maybe<void> ToResult(v8::Local<v8::Value>* err, |
237 | 0 | v8::Local<v8::Value>* result) override { |
238 | 0 | Environment* env = AsyncWrap::env(); |
239 | 0 | CryptoErrorStore* errors = CryptoJob<CipherTraits>::errors(); |
240 | |
|
241 | 0 | if (errors->Empty()) |
242 | 0 | errors->Capture(); |
243 | |
|
244 | 0 | if (out_.size() > 0 || errors->Empty()) { |
245 | 0 | CHECK(errors->Empty()); |
246 | 0 | *err = v8::Undefined(env->isolate()); |
247 | 0 | *result = out_.ToArrayBuffer(env); |
248 | 0 | if (result->IsEmpty()) { |
249 | 0 | return v8::Nothing<void>(); |
250 | 0 | } |
251 | 0 | } else { |
252 | 0 | *result = v8::Undefined(env->isolate()); |
253 | 0 | if (!errors->ToException(env).ToLocal(err)) { |
254 | 0 | return v8::Nothing<void>(); |
255 | 0 | } |
256 | 0 | } |
257 | 0 | CHECK(!result->IsEmpty()); |
258 | 0 | CHECK(!err->IsEmpty()); |
259 | 0 | return v8::JustVoid(); |
260 | 0 | } Unexecuted instantiation: node::crypto::CipherJob<node::crypto::AESCipherTraits>::ToResult(v8::Local<v8::Value>*, v8::Local<v8::Value>*) Unexecuted instantiation: node::crypto::CipherJob<node::crypto::ChaCha20Poly1305CipherTraits>::ToResult(v8::Local<v8::Value>*, v8::Local<v8::Value>*) Unexecuted instantiation: node::crypto::CipherJob<node::crypto::RSACipherTraits>::ToResult(v8::Local<v8::Value>*, v8::Local<v8::Value>*) |
261 | | |
262 | | SET_SELF_SIZE(CipherJob) |
263 | 0 | void MemoryInfo(MemoryTracker* tracker) const override { |
264 | 0 | if (CryptoJob<CipherTraits>::mode() == kCryptoJobAsync) |
265 | 0 | tracker->TrackFieldWithSize("in", in_.size()); |
266 | 0 | tracker->TrackFieldWithSize("out", out_.size()); |
267 | 0 | CryptoJob<CipherTraits>::MemoryInfo(tracker); |
268 | 0 | } Unexecuted instantiation: node::crypto::CipherJob<node::crypto::AESCipherTraits>::MemoryInfo(node::MemoryTracker*) const Unexecuted instantiation: node::crypto::CipherJob<node::crypto::ChaCha20Poly1305CipherTraits>::MemoryInfo(node::MemoryTracker*) const Unexecuted instantiation: node::crypto::CipherJob<node::crypto::RSACipherTraits>::MemoryInfo(node::MemoryTracker*) const |
269 | | |
270 | | private: |
271 | | KeyObjectData key_; |
272 | | WebCryptoCipherMode cipher_mode_; |
273 | | ByteSource in_; |
274 | | ByteSource out_; |
275 | | }; |
276 | | |
277 | | } // namespace crypto |
278 | | } // namespace node |
279 | | |
280 | | #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
281 | | #endif // SRC_CRYPTO_CRYPTO_CIPHER_H_ |