aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2020-04-22 20:46:58 +0900
committerGitHub <noreply@github.com>2020-04-22 20:46:58 +0900
commitc14e2b20ebc559c3ab2676a2b57b41f42baa53ec (patch)
tree3220dd436a2f731ca8f9250a33b7e930f6b197d4
parentdafbb1b3e67a1a2fa7ff847fc762c0d9bedf5213 (diff)
parentbb3816953b0ed728af11c1b75e02ff10d70132ec (diff)
downloadruby-openssl-c14e2b20ebc559c3ab2676a2b57b41f42baa53ec.tar.gz
Merge pull request #359 from zeroSteiner/fix/aead/ccm-mode-in-len
Allow specifying the data length for CCM mode
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--ext/openssl/ossl_cipher.c26
-rw-r--r--test/openssl/test_cipher.rb42
3 files changed, 69 insertions, 1 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 89f7e1d2..9f246bbc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -80,7 +80,7 @@ $ docker-compose run debug
```
All possible values for `RUBY_VERSION` and `OPENSSL_VERSION` can be found in
-[`.travis.yml`](https://github.com/ruby/openssl/tree/master/.travis.yml).
+[`test.yml`](https://github.com/ruby/openssl/tree/master/.github/workflows/test.yml).
**NOTE**: these commands must be run from the openssl repository root, in order
to use the
diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c
index 0b78f40b..5b92fc39 100644
--- a/ext/openssl/ossl_cipher.c
+++ b/ext/openssl/ossl_cipher.c
@@ -814,6 +814,31 @@ ossl_cipher_block_size(VALUE self)
}
/*
+ * call-seq:
+ * cipher.ccm_data_len = integer -> integer
+ *
+ * Sets the length of the plaintext / ciphertext message that will be
+ * processed in CCM mode. Make sure to call this method after #key= and
+ * #iv= have been set, and before #auth_data=.
+ *
+ * Only call this method after calling Cipher#encrypt or Cipher#decrypt.
+ */
+static VALUE
+ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len)
+{
+ int in_len, out_len;
+ EVP_CIPHER_CTX *ctx;
+
+ in_len = NUM2INT(data_len);
+
+ GetCipher(self, ctx);
+ if (EVP_CipherUpdate(ctx, NULL, &out_len, NULL, in_len) != 1)
+ ossl_raise(eCipherError, NULL);
+
+ return data_len;
+}
+
+/*
* INIT
*/
void
@@ -1043,6 +1068,7 @@ Init_ossl_cipher(void)
rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0);
rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0);
rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1);
+ rb_define_method(cCipher, "ccm_data_len=", ossl_cipher_set_ccm_data_len, 1);
id_auth_tag_len = rb_intern_const("auth_tag_len");
id_key_set = rb_intern_const("key_set");
diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb
index 178f5aba..65b36dd1 100644
--- a/test/openssl/test_cipher.rb
+++ b/test/openssl/test_cipher.rb
@@ -174,6 +174,48 @@ class OpenSSL::TestCipher < OpenSSL::TestCase
assert_not_predicate(cipher, :authenticated?)
end
+ def test_aes_ccm
+ # RFC 3610 Section 8, Test Case 1
+ key = ["c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"].pack("H*")
+ iv = ["00000003020100a0a1a2a3a4a5"].pack("H*")
+ aad = ["0001020304050607"].pack("H*")
+ pt = ["08090a0b0c0d0e0f101112131415161718191a1b1c1d1e"].pack("H*")
+ ct = ["588c979a61c663d2f066d0c2c0f989806d5f6b61dac384"].pack("H*")
+ tag = ["17e8d12cfdf926e0"].pack("H*")
+
+ kwargs = {auth_tag_len: 8, iv_len: 13, key: key, iv: iv}
+ cipher = new_encryptor("aes-128-ccm", **kwargs, ccm_data_len: pt.length, auth_data: aad)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ assert_equal tag, cipher.auth_tag
+ cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag, auth_data: aad)
+ assert_equal pt, cipher.update(ct) << cipher.final
+
+ # truncated tag is accepted
+ cipher = new_encryptor("aes-128-ccm", **kwargs, ccm_data_len: pt.length, auth_data: aad)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ assert_equal tag[0, 8], cipher.auth_tag(8)
+ cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag[0, 8], auth_data: aad)
+ assert_equal pt, cipher.update(ct) << cipher.final
+
+ # wrong tag is rejected
+ tag2 = tag.dup
+ tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff)
+ cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag2, auth_data: aad)
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct) }
+
+ # wrong aad is rejected
+ aad2 = aad[0..-2] << aad[-1].succ
+ cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag, auth_data: aad2)
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct) }
+
+ # wrong ciphertext is rejected
+ ct2 = ct[0..-2] << ct[-1].succ
+ cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct2.length, auth_tag: tag, auth_data: aad)
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct2) }
+ end if has_cipher?("aes-128-ccm") &&
+ OpenSSL::Cipher.new("aes-128-ccm").authenticated? &&
+ OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10101000 # version >= v1.1.1
+
def test_aes_gcm
# GCM spec Appendix B Test Case 4
key = ["feffe9928665731c6d6a8f9467308308"].pack("H*")