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 |
|
|