Coverage Report

Created: 2025-12-30 08:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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, &params)
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_