crypto: allow setting add'l authenticated data
authorBrian White <mscdex@mscdex.net>
Tue, 4 Mar 2014 05:05:23 +0000 (00:05 -0500)
committerFedor Indutny <fedor.indutny@gmail.com>
Tue, 4 Mar 2014 08:42:03 +0000 (12:42 +0400)
doc/api/crypto.markdown
lib/crypto.js
src/node_crypto.cc
src/node_crypto.h
test/simple/test-crypto-authenticated.js

index a029d6b..2ebdc1f 100644 (file)
@@ -250,6 +250,12 @@ method returns a `Buffer` that represents the _authentication tag_ that
 has been computed from the given data. Should be called after
 encryption has been completed using the `final` method!
 
+### cipher.setAAD(buffer)
+
+For authenticated encryption modes (currently supported: GCM), this
+method sets the value used for the additional authenticated data (AAD) input
+parameter.
+
 
 ## crypto.createDecipher(algorithm, password)
 
@@ -308,6 +314,12 @@ If no tag is provided or if the ciphertext has been tampered with,
 `final` will throw, thus indicating that the ciphertext should
 be discarded due to failed authentication.
 
+### decipher.setAAD(buffer)
+
+For authenticated encryption modes (currently supported: GCM), this
+method sets the value used for the additional authenticated data (AAD) input
+parameter.
+
 
 ## crypto.createSign(algorithm)
 
index bd3aedc..2f8e2fa 100644 (file)
@@ -334,6 +334,9 @@ Cipheriv.prototype.setAuthTag = function(tagbuf) {
   this._binding.setAuthTag(tagbuf);
 };
 
+Cipheriv.prototype.setAAD = function(aadbuf) {
+  this._binding.setAAD(aadbuf);
+};
 
 
 exports.createDecipher = exports.Decipher = Decipher;
@@ -381,6 +384,7 @@ Decipheriv.prototype.finaltol = Cipher.prototype.final;
 Decipheriv.prototype.setAutoPadding = Cipher.prototype.setAutoPadding;
 Decipheriv.prototype.getAuthTag = Cipheriv.prototype.getAuthTag;
 Decipheriv.prototype.setAuthTag = Cipheriv.prototype.setAuthTag;
+Decipheriv.prototype.setAAD = Cipheriv.prototype.setAAD;
 
 
 
index 3f7a7d1..5ae45d8 100644 (file)
@@ -2194,6 +2194,7 @@ void CipherBase::Initialize(Environment* env, Handle<Object> target) {
   NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
   NODE_SET_PROTOTYPE_METHOD(t, "getAuthTag", GetAuthTag);
   NODE_SET_PROTOTYPE_METHOD(t, "setAuthTag", SetAuthTag);
+  NODE_SET_PROTOTYPE_METHOD(t, "setAAD", SetAAD);
 
   target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "CipherBase"),
               t->GetFunction());
@@ -2386,6 +2387,35 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
 }
 
 
+bool CipherBase::SetAAD(const char* data, unsigned int len) {
+  if (!initialised_ || !IsAuthenticatedMode())
+    return false;
+  int outlen;
+  if (!EVP_CipherUpdate(&ctx_,
+                        NULL,
+                        &outlen,
+                        reinterpret_cast<const unsigned char*>(data),
+                        len)) {
+    ThrowCryptoTypeError(env(), ERR_get_error());
+    return false;
+  }
+  return true;
+}
+
+
+void CipherBase::SetAAD(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args.GetIsolate());
+  HandleScope handle_scope(env->isolate());
+
+  ASSERT_IS_BUFFER(args[0]);
+
+  CipherBase* cipher = Unwrap<CipherBase>(args.This());
+
+  if (!cipher->SetAAD(Buffer::Data(args[0]), Buffer::Length(args[0])))
+    env->ThrowError("Attempting to set AAD in unsupported state");
+}
+
+
 bool CipherBase::Update(const char* data,
                         int len,
                         unsigned char** out,
index 3fed0d5..cc67061 100644 (file)
@@ -356,6 +356,7 @@ class CipherBase : public BaseObject {
   bool IsAuthenticatedMode() const;
   bool GetAuthTag(char** out, unsigned int* out_len) const;
   bool SetAuthTag(const char* data, unsigned int len);
+  bool SetAAD(const char* data, unsigned int len);
 
   static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void Init(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -366,6 +367,7 @@ class CipherBase : public BaseObject {
 
   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,
index ff9eeda..754732c 100644 (file)
@@ -41,11 +41,17 @@ crypto.DEFAULT_ENCODING = 'buffer';
 //
 
 var TEST_CASES = [
-  { algo: 'aes-128-gcm', key: 'ipxp9a6i1Mb4USb4', iv: 'X6sIq117H0vR',
-    plain: 'Hello World!', ct: '4BE13896F64DFA2C2D0F2C76',
+  { algo: 'aes-128-gcm', key: 'ipxp9a6i1Mb4USb4',
+    iv: 'X6sIq117H0vR', plain: 'Hello World!',
+    ct: '4BE13896F64DFA2C2D0F2C76',
     tag: '272B422F62EB545EAA15B5FF84092447', tampered: false },
-  { algo: 'aes-128-gcm', key: 'ipxp9a6i1Mb4USb4', iv: 'X6sIq117H0vR',
-    plain: 'Hello World!', ct: '4BE13596F64DFA2C2D0FAC76',
+  { algo: 'aes-128-gcm', key: 'ipxp9a6i1Mb4USb4',
+    iv: 'X6sIq117H0vR', plain: 'Hello World!',
+    ct: '4BE13896F64DFA2C2D0F2C76', aad: '000000FF',
+    tag: 'BA2479F66275665A88CB7B15F43EB005', tampered: false },
+  { algo: 'aes-128-gcm', key: 'ipxp9a6i1Mb4USb4',
+    iv: 'X6sIq117H0vR', plain: 'Hello World!',
+    ct: '4BE13596F64DFA2C2D0FAC76',
     tag: '272B422F62EB545EAA15B5FF84092447', tampered: true },
   { algo: 'aes-256-gcm', key: '3zTvzr3p67VC61jmV54rIYu1545x4TlY',
     iv: '60iP0h6vJoEa', plain: 'Hello node.js world!',
@@ -69,6 +75,8 @@ for (var i in TEST_CASES) {
 
   (function() {
     var encrypt = crypto.createCipheriv(test.algo, test.key, test.iv);
+    if (test.aad)
+      encrypt.setAAD(new Buffer(test.aad, 'hex'));
     var hex = encrypt.update(test.plain, 'ascii', 'hex');
     hex += encrypt.final('hex');
     var auth_tag = encrypt.getAuthTag();
@@ -82,6 +90,8 @@ for (var i in TEST_CASES) {
   (function() {
     var decrypt = crypto.createDecipheriv(test.algo, test.key, test.iv);
     decrypt.setAuthTag(new Buffer(test.tag, 'hex'));
+    if (test.aad)
+      decrypt.setAAD(new Buffer(test.aad, 'hex'));
     var msg = decrypt.update(test.ct, 'hex', 'ascii');
     if (!test.tampered) {
       msg += decrypt.final('ascii');