Project

General

Profile

Feature #6980 » openssl_aead_ciphers.patch

OpenSSL AEAD mode support, sans whitespace changes - stouset (Stephen Touset), 09/05/2012 04:14 AM

View differences:

ext/openssl/ossl_cipher.c
/*
* call-seq:
* cipher.verify -> string
*
* Verifies the decrypted ciphertext against the tag set prior to
* decryption using the ciphertext and optional associated
* authentication data. Returns an empty string if the ciphertext was
* authenticated successfully, otherwise raises an
* OpenSSL::Cipher::CipherError.
*
* Only call this method after setting the authentication tag
* appropriate for your cipher mode and passing the entire contents
* of the ciphertext into the cipher, and before calling
* Cipher#final.
*/
static VALUE
ossl_cipher_verify(VALUE self)
{
EVP_CIPHER_CTX *ctx;
int out_len = 0;
GetCipher(self, ctx);
if (!EVP_CipherUpdate(ctx, NULL, &out_len, NULL, 0))
ossl_raise(eCipherError, "ciphertext failed to authenticate");
return rb_str_new(0, 0);
}
/*
* call-seq:
* cipher.name -> string
*
* Returns the name of the cipher which may differ slightly from the original
......
return iv;
}
/*
* call-seq:
* cipher.aad = string -> string
*
* Sets the cipher's additional authenticated data. This field may be
* set optionally before using AEAD cipher modes such as GCM or
* CCM. The contents of this field should be non-sensitive
* data which will be added to the ciphertext to generate the
* authentication tag which validates the contents of the ciphertext.
*
* The AAD must be set prior to encryption or decryption. Only call
* this method after calling Cipher#encrypt when encrypting, and
* after Cipher#decrypt and Cipher#gcm_tag= when decrypting.
*/
static VALUE
ossl_cipher_set_aad(VALUE self, VALUE data)
{
EVP_CIPHER_CTX *ctx;
unsigned char *in = NULL;
int in_len = 0;
int out_len = 0;
StringValue(data);
in = (unsigned char *) RSTRING_PTR(data);
in_len = RSTRING_LENINT(data);
GetCipher(self, ctx);
if (!EVP_CipherUpdate(ctx, NULL, &out_len, in, in_len))
ossl_raise(eCipherError, "couldn't set additional authenticated data");
return self;
}
/*
* call-seq:
* cipher.gcm_tag -> string
*
* Gets the authentication tag generated by GCM cipher modes. This
* tag may be stored along with the ciphertext, then set on the
* decryption cipher to authenticate the contents of the ciphertext
* against changes.
*
* The tag may only be retrieved after calling Cipher#final.
*/
static VALUE
ossl_cipher_get_gcm_tag(VALUE self)
{
EVP_CIPHER_CTX *ctx;
VALUE tag;
// GCM tags are 16 bytes in OpenSSL
tag = rb_str_new(NULL, 16);
GetCipher(self, ctx);
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, (unsigned char *)RSTRING_PTR(tag)))
ossl_raise(eCipherError, "Cipher#finish must be called before getting the tag");
return tag;
}
/*
* call-seq:
* cipher.gcm_tag = string -> string
*
* Sets the authentication tag to verify the contents of the
* ciphertext. The GCM tag must be set after calling Cipher#decrypt
* but before decrypting any of the ciphertext. After all decryption
* is performed, the tag can be verified by calling Cipher#verify.
*/
static VALUE
ossl_cipher_set_gcm_tag(VALUE self, VALUE data)
{
EVP_CIPHER_CTX *ctx;
unsigned char *in = NULL;
int in_len = 0;
StringValue(data);
in = (unsigned char *) RSTRING_PTR(data);
in_len = RSTRING_LENINT(data);
GetCipher(self, ctx);
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, in_len, in))
ossl_raise(eCipherError, "unable to set GCM tag");
return data;
}
/*
* call-seq:
......
rb_define_method(cCipher, "pkcs5_keyivgen", ossl_cipher_pkcs5_keyivgen, -1);
rb_define_method(cCipher, "update", ossl_cipher_update, -1);
rb_define_method(cCipher, "final", ossl_cipher_final, 0);
rb_define_method(cCipher, "verify", ossl_cipher_verify, 0);
rb_define_method(cCipher, "name", ossl_cipher_name, 0);
rb_define_method(cCipher, "key=", ossl_cipher_set_key, 1);
rb_define_method(cCipher, "aad=", ossl_cipher_set_aad, 1);
rb_define_method(cCipher, "gcm_tag=", ossl_cipher_set_gcm_tag, 1);
rb_define_method(cCipher, "gcm_tag", ossl_cipher_get_gcm_tag, 0);
rb_define_method(cCipher, "key_len=", ossl_cipher_set_key_length, 1);
rb_define_method(cCipher, "key_len", ossl_cipher_key_length, 0);
rb_define_method(cCipher, "iv=", ossl_cipher_set_iv, 1);
test/openssl/test_cipher.rb
end
end
end
if OpenSSL::OPENSSL_VERSION_NUMBER > 0x1000103f
def test_aes_gcm
pt = File.read(__FILE__)
c1 = OpenSSL::Cipher.new('aes-256-gcm')
c2 = OpenSSL::Cipher.new('aes-256-gcm')
c3 = OpenSSL::Cipher.new('aes-256-gcm')
c1.encrypt
c1.pkcs5_keyivgen('passwd')
c1.aad = 'aad'
ct = c1.update(pt) + c1.final
tag = c1.gcm_tag
c2.decrypt
c2.pkcs5_keyivgen('passwd')
c2.gcm_tag = tag
c2.aad = 'aad'
assert_equal(pt, c2.update(ct) + c2.verify + c2.final)
c3.decrypt
c3.pkcs5_keyivgen('passwd')
c3.gcm_tag = tag[0..-2] << tag[-1].succ
c3.aad = 'aad'
assert_raise OpenSSL::Cipher::CipherError do
c3.update(ct) + c3.verify + c3.final
end
end
end
end
end
(2-2/2)