openssl_aead_ciphers.patch

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

Download (5.83 KB)

View differences:

ext/openssl/ossl_cipher.c
403 403

  
404 404
/*
405 405
 *  call-seq:
406
 *     cipher.verify -> string
407
 *
408
 *  Verifies the decrypted ciphertext against the tag set prior to
409
 *  decryption using the ciphertext and optional associated
410
 *  authentication data. Returns an empty string if the ciphertext was
411
 *  authenticated successfully, otherwise raises an
412
 *  OpenSSL::Cipher::CipherError.
413
 *
414
 *  Only call this method after setting the authentication tag
415
 *  appropriate for your cipher mode and passing the entire contents
416
 *  of the ciphertext into the cipher, and before calling
417
 *  Cipher#final.
418
 */
419
static VALUE
420
ossl_cipher_verify(VALUE self)
421
{
422
    EVP_CIPHER_CTX *ctx;
423
    int             out_len = 0;
424

  
425
    GetCipher(self, ctx);
426

  
427
    if (!EVP_CipherUpdate(ctx, NULL, &out_len, NULL, 0))
428
        ossl_raise(eCipherError, "ciphertext failed to authenticate");
429

  
430
    return rb_str_new(0, 0);
431
}
432

  
433
/*
434
 *  call-seq:
406 435
 *     cipher.name -> string
407 436
 *
408 437
 *  Returns the name of the cipher which may differ slightly from the original
......
478 507
    return iv;
479 508
}
480 509

  
510
/*
511
 *  call-seq:
512
 *     cipher.aad = string -> string
513
 *
514
 *  Sets the cipher's additional authenticated data. This field may be
515
 *  set optionally before using AEAD cipher modes such as GCM or
516
 *  CCM. The contents of this field should be non-sensitive
517
 *  data which will be added to the ciphertext to generate the
518
 *  authentication tag which validates the contents of the ciphertext.
519
 *
520
 *  The AAD must be set prior to encryption or decryption. Only call
521
 *  this method after calling Cipher#encrypt when encrypting, and
522
 *  after Cipher#decrypt and Cipher#gcm_tag= when decrypting.
523
 */
524
static VALUE
525
ossl_cipher_set_aad(VALUE self, VALUE data)
526
{
527
    EVP_CIPHER_CTX *ctx;
528
    unsigned char  *in      = NULL;
529
    int             in_len  = 0;
530
    int             out_len = 0;
531

  
532
    StringValue(data);
533

  
534
    in     = (unsigned char *) RSTRING_PTR(data);
535
    in_len = RSTRING_LENINT(data);
536

  
537
    GetCipher(self, ctx);
538

  
539
    if (!EVP_CipherUpdate(ctx, NULL, &out_len, in, in_len))
540
        ossl_raise(eCipherError, "couldn't set additional authenticated data");
541

  
542
    return self;
543
}
544

  
545
/*
546
 *  call-seq:
547
 *     cipher.gcm_tag -> string
548
 *
549
 *  Gets the authentication tag generated by GCM cipher modes. This
550
 *  tag may be stored along with the ciphertext, then set on the
551
 *  decryption cipher to authenticate the contents of the ciphertext
552
 *  against changes.
553
 *
554
 *  The tag may only be retrieved after calling Cipher#final.
555
 */
556
static VALUE
557
ossl_cipher_get_gcm_tag(VALUE self)
558
{
559
    EVP_CIPHER_CTX *ctx;
560
    VALUE           tag;
561

  
562
    // GCM tags are 16 bytes in OpenSSL
563
    tag = rb_str_new(NULL, 16);
564

  
565
    GetCipher(self, ctx);
566

  
567
    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, (unsigned char *)RSTRING_PTR(tag)))
568
        ossl_raise(eCipherError, "Cipher#finish must be called before getting the tag");
569

  
570
    return tag;
571
}
572

  
573
/*
574
 *  call-seq:
575
 *     cipher.gcm_tag = string -> string
576
 *
577
 *  Sets the authentication tag to verify the contents of the
578
 *  ciphertext. The GCM tag must be set after calling Cipher#decrypt
579
 *  but before decrypting any of the ciphertext. After all decryption
580
 *  is performed, the tag can be verified by calling Cipher#verify.
581
 */
582
static VALUE
583
ossl_cipher_set_gcm_tag(VALUE self, VALUE data)
584
{
585
    EVP_CIPHER_CTX *ctx;
586
    unsigned char  *in     = NULL;
587
    int             in_len = 0;
588

  
589
    StringValue(data);
590

  
591
    in     = (unsigned char *) RSTRING_PTR(data);
592
    in_len = RSTRING_LENINT(data);
593

  
594
    GetCipher(self, ctx);
595

  
596
    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, in_len, in))
597
        ossl_raise(eCipherError, "unable to set GCM tag");
598

  
599
    return data;
600
}
481 601

  
482 602
/*
483 603
 *  call-seq:
......
742 862
    rb_define_method(cCipher, "pkcs5_keyivgen", ossl_cipher_pkcs5_keyivgen, -1);
743 863
    rb_define_method(cCipher, "update", ossl_cipher_update, -1);
744 864
    rb_define_method(cCipher, "final", ossl_cipher_final, 0);
865
    rb_define_method(cCipher, "verify", ossl_cipher_verify, 0);
745 866
    rb_define_method(cCipher, "name", ossl_cipher_name, 0);
746 867
    rb_define_method(cCipher, "key=", ossl_cipher_set_key, 1);
868
    rb_define_method(cCipher, "aad=", ossl_cipher_set_aad, 1);
869
    rb_define_method(cCipher, "gcm_tag=", ossl_cipher_set_gcm_tag, 1);
870
    rb_define_method(cCipher, "gcm_tag", ossl_cipher_get_gcm_tag, 0);
747 871
    rb_define_method(cCipher, "key_len=", ossl_cipher_set_key_length, 1);
748 872
    rb_define_method(cCipher, "key_len", ossl_cipher_key_length, 0);
749 873
    rb_define_method(cCipher, "iv=", ossl_cipher_set_iv, 1);
test/openssl/test_cipher.rb
101 101
      end
102 102
    end
103 103
  end
104

  
105
  if OpenSSL::OPENSSL_VERSION_NUMBER > 0x1000103f
106
    def test_aes_gcm
107
      pt = File.read(__FILE__)
108
      c1 = OpenSSL::Cipher.new('aes-256-gcm')
109
      c2 = OpenSSL::Cipher.new('aes-256-gcm')
110
      c3 = OpenSSL::Cipher.new('aes-256-gcm')
111

  
112
      c1.encrypt
113
      c1.pkcs5_keyivgen('passwd')
114
      c1.aad = 'aad'
115

  
116
      ct  = c1.update(pt) + c1.final
117
      tag = c1.gcm_tag
118

  
119
      c2.decrypt
120
      c2.pkcs5_keyivgen('passwd')
121
      c2.gcm_tag = tag
122
      c2.aad     = 'aad'
123

  
124
      assert_equal(pt, c2.update(ct) + c2.verify + c2.final)
125

  
126
      c3.decrypt
127
      c3.pkcs5_keyivgen('passwd')
128
      c3.gcm_tag = tag[0..-2] << tag[-1].succ
129
      c3.aad     = 'aad'
130

  
131
      assert_raise OpenSSL::Cipher::CipherError do
132
        c3.update(ct) + c3.verify + c3.final
133
      end
134
    end
135
  end
104 136
end
105 137

  
106 138
end