Skip to content

Commit c14e2b2

Browse files
authored
Merge pull request #359 from zeroSteiner/fix/aead/ccm-mode-in-len
Allow specifying the data length for CCM mode
2 parents dafbb1b + bb38169 commit c14e2b2

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ $ docker-compose run debug
8080
```
8181

8282
All possible values for `RUBY_VERSION` and `OPENSSL_VERSION` can be found in
83-
[`.travis.yml`](https://github.com/ruby/openssl/tree/master/.travis.yml).
83+
[`test.yml`](https://github.com/ruby/openssl/tree/master/.github/workflows/test.yml).
8484

8585
**NOTE**: these commands must be run from the openssl repository root, in order
8686
to use the

ext/openssl/ossl_cipher.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,31 @@ ossl_cipher_block_size(VALUE self)
813813
return INT2NUM(EVP_CIPHER_CTX_block_size(ctx));
814814
}
815815

816+
/*
817+
* call-seq:
818+
* cipher.ccm_data_len = integer -> integer
819+
*
820+
* Sets the length of the plaintext / ciphertext message that will be
821+
* processed in CCM mode. Make sure to call this method after #key= and
822+
* #iv= have been set, and before #auth_data=.
823+
*
824+
* Only call this method after calling Cipher#encrypt or Cipher#decrypt.
825+
*/
826+
static VALUE
827+
ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len)
828+
{
829+
int in_len, out_len;
830+
EVP_CIPHER_CTX *ctx;
831+
832+
in_len = NUM2INT(data_len);
833+
834+
GetCipher(self, ctx);
835+
if (EVP_CipherUpdate(ctx, NULL, &out_len, NULL, in_len) != 1)
836+
ossl_raise(eCipherError, NULL);
837+
838+
return data_len;
839+
}
840+
816841
/*
817842
* INIT
818843
*/
@@ -1043,6 +1068,7 @@ Init_ossl_cipher(void)
10431068
rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0);
10441069
rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0);
10451070
rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1);
1071+
rb_define_method(cCipher, "ccm_data_len=", ossl_cipher_set_ccm_data_len, 1);
10461072

10471073
id_auth_tag_len = rb_intern_const("auth_tag_len");
10481074
id_key_set = rb_intern_const("key_set");

test/openssl/test_cipher.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,48 @@ def test_authenticated
174174
assert_not_predicate(cipher, :authenticated?)
175175
end
176176

177+
def test_aes_ccm
178+
# RFC 3610 Section 8, Test Case 1
179+
key = ["c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"].pack("H*")
180+
iv = ["00000003020100a0a1a2a3a4a5"].pack("H*")
181+
aad = ["0001020304050607"].pack("H*")
182+
pt = ["08090a0b0c0d0e0f101112131415161718191a1b1c1d1e"].pack("H*")
183+
ct = ["588c979a61c663d2f066d0c2c0f989806d5f6b61dac384"].pack("H*")
184+
tag = ["17e8d12cfdf926e0"].pack("H*")
185+
186+
kwargs = {auth_tag_len: 8, iv_len: 13, key: key, iv: iv}
187+
cipher = new_encryptor("aes-128-ccm", **kwargs, ccm_data_len: pt.length, auth_data: aad)
188+
assert_equal ct, cipher.update(pt) << cipher.final
189+
assert_equal tag, cipher.auth_tag
190+
cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag, auth_data: aad)
191+
assert_equal pt, cipher.update(ct) << cipher.final
192+
193+
# truncated tag is accepted
194+
cipher = new_encryptor("aes-128-ccm", **kwargs, ccm_data_len: pt.length, auth_data: aad)
195+
assert_equal ct, cipher.update(pt) << cipher.final
196+
assert_equal tag[0, 8], cipher.auth_tag(8)
197+
cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag[0, 8], auth_data: aad)
198+
assert_equal pt, cipher.update(ct) << cipher.final
199+
200+
# wrong tag is rejected
201+
tag2 = tag.dup
202+
tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff)
203+
cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag2, auth_data: aad)
204+
assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct) }
205+
206+
# wrong aad is rejected
207+
aad2 = aad[0..-2] << aad[-1].succ
208+
cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag, auth_data: aad2)
209+
assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct) }
210+
211+
# wrong ciphertext is rejected
212+
ct2 = ct[0..-2] << ct[-1].succ
213+
cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct2.length, auth_tag: tag, auth_data: aad)
214+
assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct2) }
215+
end if has_cipher?("aes-128-ccm") &&
216+
OpenSSL::Cipher.new("aes-128-ccm").authenticated? &&
217+
OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10101000 # version >= v1.1.1
218+
177219
def test_aes_gcm
178220
# GCM spec Appendix B Test Case 4
179221
key = ["feffe9928665731c6d6a8f9467308308"].pack("H*")

0 commit comments

Comments
 (0)