summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/crypto.md2
-rw-r--r--src/node_crypto.cc7
-rw-r--r--test/parallel/test-crypto-authenticated.js26
3 files changed, 29 insertions, 6 deletions
diff --git a/doc/api/crypto.md b/doc/api/crypto.md
index b26e9cae04..ecb0c8186c 100644
--- a/doc/api/crypto.md
+++ b/doc/api/crypto.md
@@ -445,7 +445,7 @@ is invalid according to [NIST SP 800-38D][] or does not match the value of the
`authTagLength` option, `decipher.setAuthTag()` will throw an error.
The `decipher.setAuthTag()` method must be called before
-[`decipher.final()`][].
+[`decipher.final()`][] and can only be called once.
### decipher.setAutoPadding([autoPadding])
<!-- YAML
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 47e0ba5349..4cf3ac5652 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -2894,14 +2894,11 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
if (!cipher->ctx_ ||
!cipher->IsAuthenticatedMode() ||
- cipher->kind_ != kDecipher) {
+ cipher->kind_ != kDecipher ||
+ cipher->auth_tag_state_ != kAuthTagUnknown) {
return args.GetReturnValue().Set(false);
}
- // TODO(tniessen): Throw if the authentication tag has already been set.
- if (cipher->auth_tag_state_ == kAuthTagPassedToOpenSSL)
- return args.GetReturnValue().Set(true);
-
unsigned int tag_len = Buffer::Length(args[0]);
const int mode = EVP_CIPHER_CTX_mode(cipher->ctx_.get());
bool is_valid;
diff --git a/test/parallel/test-crypto-authenticated.js b/test/parallel/test-crypto-authenticated.js
index 77587fadf7..ec5c05cb12 100644
--- a/test/parallel/test-crypto-authenticated.js
+++ b/test/parallel/test-crypto-authenticated.js
@@ -589,3 +589,29 @@ for (const test of TEST_CASES) {
}
}
}
+
+// Test that setAuthTag can only be called once.
+{
+ const plain = Buffer.from('Hello world', 'utf8');
+ const key = Buffer.from('0123456789abcdef', 'utf8');
+ const iv = Buffer.from('0123456789ab', 'utf8');
+ const opts = { authTagLength: 8 };
+
+ for (const mode of ['gcm', 'ccm', 'ocb']) {
+ const cipher = crypto.createCipheriv(`aes-128-${mode}`, key, iv, opts);
+ const ciphertext = Buffer.concat([cipher.update(plain), cipher.final()]);
+ const tag = cipher.getAuthTag();
+
+ const decipher = crypto.createDecipheriv(`aes-128-${mode}`, key, iv, opts);
+ decipher.setAuthTag(tag);
+ assert.throws(() => {
+ decipher.setAuthTag(tag);
+ }, errMessages.state);
+ // Decryption should still work.
+ const plaintext = Buffer.concat([
+ decipher.update(ciphertext),
+ decipher.final()
+ ]);
+ assert(plain.equals(plaintext));
+ }
+}