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