openssl_aead_ciphers.patch

OpenSSL AEAD mode support - Stephen Touset, 09/05/2012 04:11 AM

Download (13.2 KB)

View differences:

ext/openssl/ossl_cipher.c
22 22
#define GetCipher(obj, ctx) do { \
23 23
    GetCipherInit((obj), (ctx)); \
24 24
    if (!(ctx)) { \
25
	ossl_raise(rb_eRuntimeError, "Cipher not inititalized!"); \
25
    ossl_raise(rb_eRuntimeError, "Cipher not inititalized!"); \
26 26
    } \
27 27
} while (0)
28 28
#define SafeGetCipher(obj, ctx) do { \
......
61 61
    AllocCipher(ret, ctx);
62 62
    EVP_CIPHER_CTX_init(ctx);
63 63
    if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1)
64
	ossl_raise(eCipherError, NULL);
64
    ossl_raise(eCipherError, NULL);
65 65

  
66 66
    return ret;
67 67
}
......
73 73
ossl_cipher_free(EVP_CIPHER_CTX *ctx)
74 74
{
75 75
    if (ctx) {
76
	EVP_CIPHER_CTX_cleanup(ctx);
77
	ruby_xfree(ctx);
76
    EVP_CIPHER_CTX_cleanup(ctx);
77
    ruby_xfree(ctx);
78 78
    }
79 79
}
80 80

  
......
107 107
    name = StringValuePtr(str);
108 108
    GetCipherInit(self, ctx);
109 109
    if (ctx) {
110
	ossl_raise(rb_eRuntimeError, "Cipher already inititalized!");
110
    ossl_raise(rb_eRuntimeError, "Cipher already inititalized!");
111 111
    }
112 112
    AllocCipher(self, ctx);
113 113
    EVP_CIPHER_CTX_init(ctx);
114 114
    if (!(cipher = EVP_get_cipherbyname(name))) {
115
	ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%s)", name);
115
    ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%s)", name);
116 116
    }
117 117
    /*
118 118
     * The EVP which has EVP_CIPH_RAND_KEY flag (such as DES3) allows
......
122 122
     */
123 123
    memset(key, 0, EVP_MAX_KEY_LENGTH);
124 124
    if (EVP_CipherInit_ex(ctx, cipher, NULL, key, NULL, -1) != 1)
125
	ossl_raise(eCipherError, NULL);
125
    ossl_raise(eCipherError, NULL);
126 126

  
127 127
    return self;
128 128
}
......
137 137

  
138 138
    GetCipherInit(self, ctx1);
139 139
    if (!ctx1) {
140
	AllocCipher(self, ctx1);
140
    AllocCipher(self, ctx1);
141 141
    }
142 142
    SafeGetCipher(other, ctx2);
143 143
    if (EVP_CIPHER_CTX_copy(ctx1, ctx2) != 1)
144
	ossl_raise(eCipherError, NULL);
144
    ossl_raise(eCipherError, NULL);
145 145

  
146 146
    return self;
147 147
}
......
194 194

  
195 195
    GetCipher(self, ctx);
196 196
    if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, -1) != 1)
197
	ossl_raise(eCipherError, NULL);
197
    ossl_raise(eCipherError, NULL);
198 198

  
199 199
    return self;
200 200
}
......
208 208
    VALUE pass, init_v;
209 209

  
210 210
    if(rb_scan_args(argc, argv, "02", &pass, &init_v) > 0){
211
	/*
212
	 * oops. this code mistakes salt for IV.
213
	 * We deprecated the arguments for this method, but we decided
214
	 * keeping this behaviour for backward compatibility.
215
	 */
216
	const char *cname  = rb_class2name(rb_obj_class(self));
217
	rb_warn("arguments for %s#encrypt and %s#decrypt were deprecated; "
211
    /*
212
     * oops. this code mistakes salt for IV.
213
     * We deprecated the arguments for this method, but we decided
214
     * keeping this behaviour for backward compatibility.
215
     */
216
    const char *cname  = rb_class2name(rb_obj_class(self));
217
    rb_warn("arguments for %s#encrypt and %s#decrypt were deprecated; "
218 218
                "use %s#pkcs5_keyivgen to derive key and IV",
219 219
                cname, cname, cname);
220
	StringValue(pass);
221
	GetCipher(self, ctx);
222
	if (NIL_P(init_v)) memcpy(iv, "OpenSSL for Ruby rulez!", sizeof(iv));
223
	else{
224
	    StringValue(init_v);
225
	    if (EVP_MAX_IV_LENGTH > RSTRING_LEN(init_v)) {
226
		memset(iv, 0, EVP_MAX_IV_LENGTH);
227
		memcpy(iv, RSTRING_PTR(init_v), RSTRING_LEN(init_v));
228
	    }
229
	    else memcpy(iv, RSTRING_PTR(init_v), sizeof(iv));
230
	}
231
	EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), EVP_md5(), iv,
232
		       (unsigned char *)RSTRING_PTR(pass), RSTRING_LENINT(pass), 1, key, NULL);
233
	p_key = key;
234
	p_iv = iv;
220
    StringValue(pass);
221
    GetCipher(self, ctx);
222
    if (NIL_P(init_v)) memcpy(iv, "OpenSSL for Ruby rulez!", sizeof(iv));
223
    else{
224
        StringValue(init_v);
225
        if (EVP_MAX_IV_LENGTH > RSTRING_LEN(init_v)) {
226
        memset(iv, 0, EVP_MAX_IV_LENGTH);
227
        memcpy(iv, RSTRING_PTR(init_v), RSTRING_LEN(init_v));
228
        }
229
        else memcpy(iv, RSTRING_PTR(init_v), sizeof(iv));
230
    }
231
    EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), EVP_md5(), iv,
232
               (unsigned char *)RSTRING_PTR(pass), RSTRING_LENINT(pass), 1, key, NULL);
233
    p_key = key;
234
    p_iv = iv;
235 235
    }
236 236
    else {
237
	GetCipher(self, ctx);
237
    GetCipher(self, ctx);
238 238
    }
239 239
    if (EVP_CipherInit_ex(ctx, NULL, NULL, p_key, p_iv, mode) != 1) {
240
	ossl_raise(eCipherError, NULL);
240
    ossl_raise(eCipherError, NULL);
241 241
    }
242 242

  
243 243
    return self;
......
311 311
    rb_scan_args(argc, argv, "13", &vpass, &vsalt, &viter, &vdigest);
312 312
    StringValue(vpass);
313 313
    if(!NIL_P(vsalt)){
314
	StringValue(vsalt);
315
	if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN)
316
	    ossl_raise(eCipherError, "salt must be an 8-octet string");
317
	salt = (unsigned char *)RSTRING_PTR(vsalt);
314
    StringValue(vsalt);
315
    if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN)
316
        ossl_raise(eCipherError, "salt must be an 8-octet string");
317
    salt = (unsigned char *)RSTRING_PTR(vsalt);
318 318
    }
319 319
    iter = NIL_P(viter) ? 2048 : NUM2INT(viter);
320 320
    digest = NIL_P(vdigest) ? EVP_md5() : GetDigestPtr(vdigest);
321 321
    GetCipher(self, ctx);
322 322
    EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt,
323
		   (unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv);
323
           (unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv);
324 324
    if (EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, -1) != 1)
325
	ossl_raise(eCipherError, NULL);
325
    ossl_raise(eCipherError, NULL);
326 326
    OPENSSL_cleanse(key, sizeof key);
327 327
    OPENSSL_cleanse(iv, sizeof iv);
328 328

  
......
368 368
    }
369 369

  
370 370
    if (!EVP_CipherUpdate(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len))
371
	ossl_raise(eCipherError, NULL);
371
    ossl_raise(eCipherError, NULL);
372 372
    assert(out_len < RSTRING_LEN(str));
373 373
    rb_str_set_len(str, out_len);
374 374

  
......
394 394
    GetCipher(self, ctx);
395 395
    str = rb_str_new(0, EVP_CIPHER_CTX_block_size(ctx));
396 396
    if (!EVP_CipherFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), &out_len))
397
	ossl_raise(eCipherError, NULL);
397
    ossl_raise(eCipherError, NULL);
398 398
    assert(out_len <= RSTRING_LEN(str));
399 399
    rb_str_set_len(str, out_len);
400 400

  
......
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
......
473 502
        ossl_raise(eCipherError, "iv length too short");
474 503

  
475 504
    if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, (unsigned char *)RSTRING_PTR(iv), -1) != 1)
476
	ossl_raise(eCipherError, NULL);
505
    ossl_raise(eCipherError, NULL);
477 506

  
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:
......
523 643

  
524 644
    GetCipher(self, ctx);
525 645
    if (EVP_CIPHER_CTX_set_padding(ctx, pad) != 1)
526
	ossl_raise(eCipherError, NULL);
646
    ossl_raise(eCipherError, NULL);
527 647
    return padding;
528 648
}
529 649
#else
......
534 654
    static VALUE						\
535 655
    ossl_cipher_##func(VALUE self)				\
536 656
    {								\
537
	EVP_CIPHER_CTX *ctx;					\
538
	GetCipher(self, ctx);					\
539
	return INT2NUM(EVP_CIPHER_##func(EVP_CIPHER_CTX_cipher(ctx)));	\
657
    EVP_CIPHER_CTX *ctx;					\
658
    GetCipher(self, ctx);					\
659
    return INT2NUM(EVP_CIPHER_##func(EVP_CIPHER_CTX_cipher(ctx)));	\
540 660
    }
541 661

  
542 662
/*
......
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);
......
751 875
    rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0);
752 876
    rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1);
753 877
}
754

  
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