diff options
Diffstat (limited to 'ossl_cipher.c')
-rw-r--r-- | ossl_cipher.c | 542 |
1 files changed, 542 insertions, 0 deletions
diff --git a/ossl_cipher.c b/ossl_cipher.c new file mode 100644 index 0000000..4ec3b71 --- /dev/null +++ b/ossl_cipher.c @@ -0,0 +1,542 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define MakeCipher(obj, klass, ciphp) {\ + obj = Data_Make_Struct(klass, ossl_cipher, 0, ossl_cipher_free, ciphp);\ +} + +#define GetCipher(obj, ciphp) {\ + Data_Get_Struct(obj, ossl_cipher, ciphp);\ +} + +#define DefCipherConst(x) rb_define_const(mCipher, #x, INT2FIX(##x)) +/* + * Constants + */ +/* BASIC TYPES */ +#define UNSPEC 0x00 +#define ECB 0x01 +#define CFB 0x02 +#define OFB 0x04 +#define CBC 0x08 +#define EDE 0x10 +#define EDE3 0x20 +#define BIT40 0x40 +#define BIT64 0x80 +/* COMBINATIONS */ +#define EDE_CFB 0x12 +#define EDE3_CFB 0x22 +#define EDE_OFB 0x14 +#define EDE3_OFB 0x24 +#define EDE_CBC 0x18 +#define EDE3_CBC 0x28 +#define BIT40_CBC 0x48 +#define BIT64_CBC 0x88 + +/* + * Classes + */ +VALUE cCipher; +VALUE eCipherError; + +/* + * Struct + */ +typedef struct ossl_cipher_st { + int nid; + EVP_CIPHER_CTX *ctx; +} ossl_cipher; + +static void ossl_cipher_free(ossl_cipher *ciphp) +{ + if (ciphp) { + if (ciphp->ctx) OPENSSL_free(ciphp->ctx); + free(ciphp); + } +} + +/* + * PUBLIC + */ +int ossl_cipher_get_NID(VALUE obj) +{ + ossl_cipher *ciphp = NULL; + + GetCipher(obj, ciphp); + + return ciphp->nid; /*EVP_CIPHER_CTX_nid(ciphp->ctx);*/ +} + +const EVP_CIPHER *ossl_cipher_get_EVP_CIPHER(VALUE obj) +{ + ossl_cipher *ciphp = NULL; + + GetCipher(obj, ciphp); + + return EVP_get_cipherbynid(ciphp->nid); /*EVP_CIPHER_CTX_cipher(ciphp->ctx);*/ +} + +/* + * PRIVATE + */ +static VALUE ossl_cipher_s_new(int argc, VALUE *argv, VALUE klass) +{ + ossl_cipher *ciphp = NULL; + VALUE obj; + + if (klass == cCipher) + rb_raise(rb_eNotImpError, "cannot do Cipher.new - Cipher is an abstract class"); + MakeCipher(obj, klass, ciphp); + + if (!(ciphp->ctx = OPENSSL_malloc(sizeof(EVP_CIPHER_CTX)))) { + rb_raise(eCipherError, "%s", ossl_error()); + } + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE ossl_cipher_update(VALUE self, VALUE data) +{ + ossl_cipher *ciphp = NULL; + char *in = NULL, *out = NULL; + int in_len = 0, out_len = 0; + VALUE str; + + GetCipher(self, ciphp); + Check_SafeStr(data); + in = RSTRING(data)->ptr; + in_len = RSTRING(data)->len; + + if (!(out = OPENSSL_malloc(in_len + EVP_CIPHER_CTX_block_size(ciphp->ctx)))) { + rb_raise(eCipherError, "%s", ossl_error()); + } + if (!EVP_CipherUpdate(ciphp->ctx, out, &out_len, in, in_len)) { + rb_raise(eCipherError, "%s", ossl_error()); + } + + str = rb_str_new(out, out_len); + OPENSSL_free(out); + + return str; +} + +static VALUE ossl_cipher_encrypt(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + const EVP_CIPHER *cipher = NULL; + unsigned char iv[EVP_MAX_IV_LENGTH], key[EVP_MAX_KEY_LENGTH]; + VALUE pass, init_v, data; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "12", &pass, &init_v, &data); + + Check_SafeStr(pass); + if (NIL_P(init_v)) { + /* + * TODO: + * random IV generation! + */ + memcpy(iv, "OpenSSL for Ruby rulez!", sizeof(iv)); + /* + RAND_add(data,i,0); where from take data? + if (RAND_pseudo_bytes(iv, 8) < 0) { + rb_raise(eCipherError, "%s", ossl_error()); + } + */ + } else { + Check_SafeStr(init_v); + memcpy(iv, RSTRING(init_v)->ptr, sizeof(iv)); + } + EVP_CIPHER_CTX_init(ciphp->ctx); + + cipher = EVP_get_cipherbynid(ciphp->nid); + EVP_BytesToKey(cipher, EVP_md5(), iv, RSTRING(pass)->ptr, RSTRING(pass)->len, 1, key, NULL); + + if (!EVP_EncryptInit(ciphp->ctx, cipher, key, iv)) { + rb_raise(eCipherError, "%s", ossl_error()); + } + + if (!NIL_P(data)) { + return ossl_cipher_update(self, data); + } + return self; +} + +static VALUE ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + const EVP_CIPHER *cipher = NULL; + unsigned char iv[EVP_MAX_IV_LENGTH], key[EVP_MAX_KEY_LENGTH]; + VALUE pass, init_v, data; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "12", &pass, &init_v, &data); + + Check_SafeStr(pass); + if (NIL_P(init_v)) { + /* + * TODO: + * random IV generation! + */ + memcpy(iv, "OpenSSL for Ruby rulez!", sizeof(iv)); + } else { + Check_SafeStr(init_v); + memcpy(iv, RSTRING(init_v)->ptr, sizeof(iv)); + } + EVP_CIPHER_CTX_init(ciphp->ctx); + + cipher = EVP_get_cipherbynid(ciphp->nid); + /*if (!load_iv((unsigned char **)&header,&(cipher->iv[0]),8)) return(0); /* cipher = CIPHER_INFO */ + EVP_BytesToKey(cipher, EVP_md5(), iv, RSTRING(pass)->ptr, RSTRING(pass)->len, 1, key, NULL); + + if (!EVP_DecryptInit(ciphp->ctx, cipher, key, iv)) { + rb_raise(eCipherError, "%s", ossl_error()); + } + + if (!NIL_P(data)) { + return ossl_cipher_update(self, data); + } + return self; +} + +static VALUE ossl_cipher_cipher(VALUE self) +{ + ossl_cipher *ciphp = NULL; + + char *out = NULL; + int out_len = 0; + VALUE str; + + GetCipher(self, ciphp); + + if (!(out = OPENSSL_malloc(EVP_CIPHER_CTX_block_size(ciphp->ctx)))) { + rb_raise(eCipherError, "%s", ossl_error()); + } + if (!EVP_CipherFinal(ciphp->ctx, out, &out_len)) { + rb_raise(eCipherError, "%s", ossl_error()); + } + str = rb_str_new(out, out_len); + OPENSSL_free(out); + + return str; +} + +VALUE ossl_des_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode, type; + + GetCipher(self, ciphp); + + if (rb_scan_args(argc, argv, "11", &mode, &type) == 2) + spec = FIX2INT(mode) + FIX2INT(type); + else + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_des_ecb; + break; + case EDE: + nid = NID_des_ede; + break; + case EDE3: + nid = NID_des_ede3; + break; + case CFB: + nid = NID_des_cfb64; + break; + case EDE_CFB: + nid = NID_des_ede_cfb64; + break; + case EDE3_CFB: + nid = NID_des_ede3_cfb64; + break; + case OFB: + nid = NID_des_ofb64; + break; + case EDE_OFB: + nid = NID_des_ede_ofb64; + break; + case EDE3_OFB: + nid = NID_des_ede3_ofb64; + break; + case CBC: + nid = NID_des_cbc; + break; + case EDE_CBC: + nid = NID_des_ede_cbc; + break; + case EDE3_CBC: + nid = NID_des_ede3_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + break; + } + ciphp->nid = nid; + + return self; +} + +VALUE ossl_rc4_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode, type; + + GetCipher(self, ciphp); + + if (rb_scan_args(argc, argv, "01", &mode) == 1) + spec = FIX2INT(mode); + + switch (spec) { + case UNSPEC: + nid = NID_rc4; + break; + case BIT40: + nid = NID_rc4_40; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + break; + } + ciphp->nid = nid; + + return self; +} + +VALUE ossl_idea_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode, type; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "10", &mode); + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_idea_ecb; + break; + case CFB: + nid = NID_idea_cfb64; + break; + case OFB: + nid = NID_idea_ofb64; + break; + case CBC: + nid = NID_idea_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + break; + } + ciphp->nid = nid; + + return self; +} +VALUE ossl_rc2_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode, type; + + GetCipher(self, ciphp); + + if (rb_scan_args(argc, argv, "11", &mode, &type) == 2) + spec = FIX2INT(mode) + FIX2INT(type); + else + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_rc2_ecb; + break; + case CBC: + nid = NID_rc2_cbc; + break; + case BIT40_CBC: + nid = NID_rc2_40_cbc; + break; + case BIT64_CBC: + nid = NID_rc2_64_cbc; + break; + case CFB: + nid = NID_rc2_cfb64; + break; + case OFB: + nid = NID_rc2_ofb64; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + break; + } + ciphp->nid = nid; + + return self; +} +VALUE ossl_bf_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode, type; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "10", &mode); + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_bf_ecb; + break; + case CFB: + nid = NID_bf_cfb64; + break; + case OFB: + nid = NID_bf_ofb64; + break; + case CBC: + nid = NID_bf_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + break; + } + ciphp->nid = nid; + + return self; +} + +VALUE ossl_cast5_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode, type; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "10", &mode); + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_cast5_ecb; + break; + case CFB: + nid = NID_cast5_cfb64; + break; + case OFB: + nid = NID_cast5_ofb64; + break; + case CBC: + nid = NID_cast5_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + break; + } + ciphp->nid = nid; + + return self; +} + +VALUE ossl_rc5_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode, type; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "10", &mode); + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_rc5_ecb; + break; + case CFB: + nid = NID_rc5_cfb64; + break; + case OFB: + nid = NID_rc5_ofb64; + break; + case CBC: + nid = NID_rc5_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + break; + } + ciphp->nid = nid; + + return self; +} + +/* + * INIT + */ +void Init_ossl_cipher(VALUE mCipher) +{ + VALUE cDES, cRC4, cIdea, cRC2, cBlowFish, cCast5, cRC5; + + DefCipherConst(ECB); + DefCipherConst(EDE); + DefCipherConst(EDE3); + DefCipherConst(CFB); + DefCipherConst(OFB); + DefCipherConst(CBC); + DefCipherConst(BIT40); + DefCipherConst(BIT64); + + eCipherError = rb_define_class_under(mCipher, "Error", rb_eStandardError); + + cCipher = rb_define_class_under(mCipher, "ANY", rb_cObject); + rb_define_singleton_method(cCipher, "new", ossl_cipher_s_new, -1); + /*"initialize"*/ + rb_define_method(cCipher, "encrypt", ossl_cipher_encrypt, -1); + rb_define_method(cCipher, "decrypt", ossl_cipher_decrypt, -1); + rb_define_method(cCipher, "update", ossl_cipher_update, 1); + rb_define_alias(cCipher, "<<", "update"); + rb_define_method(cCipher, "cipher", ossl_cipher_cipher, 0); + + cDES = rb_define_class_under(mCipher, "DES", cCipher); + rb_define_method(cDES, "initialize", ossl_des_initialize, -1); + + cRC4 = rb_define_class_under(mCipher, "RC4", cCipher); + rb_define_method(cRC4, "initialize", ossl_rc4_initialize, -1); + + cIdea = rb_define_class_under(mCipher, "Idea", cCipher); + rb_define_method(cIdea, "initialize", ossl_idea_initialize, -1); + + cRC2 = rb_define_class_under(mCipher, "RC2", cCipher); + rb_define_method(cRC2, "initialize", ossl_rc2_initialize, -1); + + cBlowFish = rb_define_class_under(mCipher, "BlowFish", cCipher); + rb_define_method(cBlowFish, "initialize", ossl_bf_initialize, -1); + + cCast5 = rb_define_class_under(mCipher, "Cast5", cCipher); + rb_define_method(cCast5, "initialize", ossl_cast5_initialize, -1); + + cRC5 = rb_define_class_under(mCipher, "RC5", cCipher); + rb_define_method(cRC5, "initialize", ossl_rc5_initialize, -1); +} + |