ruby / openssl

Provides SSL, TLS and general purpose cryptography.
Other
240 stars 167 forks source link

`OpenSSL::Cipher.new("chacha20").iv_len` should be `12`? #95

Closed zhenkyle closed 7 years ago

zhenkyle commented 7 years ago

My environment is

I got this error

enctest.rb:42:in `iv=': iv must be 16 bytes (ArgumentError)

running my little test script

# enctest.rb

gem "openssl"
require 'openssl'

def hex2bin hex
  [hex.to_s].pack("H*")
end

# ChaCha20 implementation test vectors
# Taken from https://tools.ietf.org/html/rfc7539#section-2.4.2
chacha20_key =            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
chacha20_nonce =          "000000090000004a00000000"
chacha20_message =        "4c616469657320616e642047656e746c" \
                          "656d656e206f662074686520636c6173" \
                          "73206f66202739393a20496620492063" \
                          "6f756c64206f6666657220796f75206f" \
                          "6e6c79206f6e652074697020666f7220" \
                          "746865206675747572652c2073756e73" \
                          "637265656e20776f756c642062652069" \
                          "742e"
chacha20_ciphertext =     "6e2e359a2568f98041ba0728dd0d6981" \
                          "e97e7aec1d4360c20a27afccfd9fae0b" \
                          "f91b65c5524733ab8f593dabcd62b357" \
                          "1639d624e65152ab8f530c359f0861d8" \
                          "07ca0dbf500d6a6156a38e088a22b65e" \
                          "52bc514d16ccf806818ce91ab7793736" \
                          "5af90bbf74a35be6b40b8eedf2785e42" \
                          "874d"

key = hex2bin(chacha20_key)
message = hex2bin(chacha20_message)
nonce = hex2bin(chacha20_nonce)
expect_ciphertext = hex2bin(chacha20_ciphertext)

cipher = OpenSSL::Cipher.new 'chacha20'
cipher.encrypt
cipher.iv = nonce
cipher.key = key

encrypted = cipher.update(message) + cipher.final

raise 'openssl fail!!!' unless encrypted  == expect_ciphertext

That is because OpenSSL::Cipher.new("chacha20").iv_len = 16, while I'm trying to pass in an iv with 12-bytes.

Accrodding to rfc7539 , iv_len of chacha20 should be 96-bit, that is 12-bytes.

o A 96-bit nonce. In some protocols, this is known as the Initialization Vector.

After all

OpenSSL::Cipher.new("chacha20-poly1305").iv_len equals to 12

So I think it's a bug.

rhenium commented 7 years ago

That OpenSSL::Cipher expects 16 bytes long IV is not a bug. For RFC 7539 compliant operation, prepend the initial counter to the nonce:

...
chacha20_nonce =          "000000000000004a00000000" # assuming 9 in your code is a typo
chacha20_initial_counter ="01000000"
...
initial_counter = hex2bin(chacha20_initial_counter)
...
cipher.iv = initial_counter + nonce
...

And I thought it would work, ... but it didn't because of a bug in the OpenSSL library. I will report to the OpenSSL project.

diff --git a/crypto/evp/e_chacha20_poly1305.c b/crypto/evp/e_chacha20_poly1305.c
index 952bd3fca781..befd805e35a5 100644
--- a/crypto/evp/e_chacha20_poly1305.c
+++ b/crypto/evp/e_chacha20_poly1305.c
@@ -127,7 +127,7 @@ static const EVP_CIPHER chacha20 = {
     1,                      /* block_size */
     CHACHA_KEY_SIZE,        /* key_len */
     CHACHA_CTR_SIZE,        /* iv_len, 128-bit counter in the context */
-    0,                      /* flags */
+    EVP_CIPH_CUSTOM_IV | EVP_CIPH_ALWAYS_CALL_INIT,
     chacha_init_key,
     chacha_cipher,
     NULL,
zhenkyle commented 7 years ago

The chacha20_nonce in my code is a typo, and after applying your patch to OpenSSL library my test did passed.

Thank you for point out the initial_counter's use case for me, you find and solve the real problem really quick.

Please do report the issuse to OpenSSL project.

rhenium commented 7 years ago

FYI, the patch has been merged (https://github.com/openssl/openssl/pull/2156) and the next 1.1.0 release will fix the issue.

zhenkyle commented 7 years ago

Got it, thanks.