| /* |
| * openssl.c |
| * Wrapper for OpenSSL library. |
| * |
| * Copyright (c) 2001 Marko Kreen |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * contrib/pgcrypto/openssl.c |
| */ |
| |
| #include "postgres.h" |
| |
| #include "px.h" |
| |
| #include <openssl/evp.h> |
| #include <openssl/blowfish.h> |
| #include <openssl/cast.h> |
| #include <openssl/des.h> |
| #include <openssl/rand.h> |
| #include <openssl/err.h> |
| |
| /* |
| * Max lengths we might want to handle. |
| */ |
| #define MAX_KEY (512/8) |
| #define MAX_IV (128/8) |
| |
| /* |
| * Compatibility with OpenSSL 0.9.6 |
| * |
| * It needs AES and newer DES and digest API. |
| */ |
| #if OPENSSL_VERSION_NUMBER >= 0x00907000L |
| |
| /* |
| * Nothing needed for OpenSSL 0.9.7+ |
| */ |
| |
| #include <openssl/aes.h> |
| #else /* old OPENSSL */ |
| |
| /* |
| * Emulate OpenSSL AES. |
| */ |
| |
| #include "rijndael.c" |
| |
| #define AES_ENCRYPT 1 |
| #define AES_DECRYPT 0 |
| #define AES_KEY rijndael_ctx |
| |
| static int |
| AES_set_encrypt_key(const uint8 *key, int kbits, AES_KEY *ctx) |
| { |
| aes_set_key(ctx, key, kbits, 1); |
| return 0; |
| } |
| |
| static int |
| AES_set_decrypt_key(const uint8 *key, int kbits, AES_KEY *ctx) |
| { |
| aes_set_key(ctx, key, kbits, 0); |
| return 0; |
| } |
| |
| static void |
| AES_ecb_encrypt(const uint8 *src, uint8 *dst, AES_KEY *ctx, int enc) |
| { |
| memcpy(dst, src, 16); |
| if (enc) |
| aes_ecb_encrypt(ctx, dst, 16); |
| else |
| aes_ecb_decrypt(ctx, dst, 16); |
| } |
| |
| static void |
| AES_cbc_encrypt(const uint8 *src, uint8 *dst, int len, AES_KEY *ctx, uint8 *iv, int enc) |
| { |
| memcpy(dst, src, len); |
| if (enc) |
| { |
| aes_cbc_encrypt(ctx, iv, dst, len); |
| memcpy(iv, dst + len - 16, 16); |
| } |
| else |
| { |
| aes_cbc_decrypt(ctx, iv, dst, len); |
| memcpy(iv, src + len - 16, 16); |
| } |
| } |
| |
| /* |
| * Emulate DES_* API |
| */ |
| |
| #define DES_key_schedule des_key_schedule |
| #define DES_cblock des_cblock |
| #define DES_set_key(k, ks) \ |
| des_set_key((k), *(ks)) |
| #define DES_ecb_encrypt(i, o, k, e) \ |
| des_ecb_encrypt((i), (o), *(k), (e)) |
| #define DES_ncbc_encrypt(i, o, l, k, iv, e) \ |
| des_ncbc_encrypt((i), (o), (l), *(k), (iv), (e)) |
| #define DES_ecb3_encrypt(i, o, k1, k2, k3, e) \ |
| des_ecb3_encrypt((des_cblock *)(i), (des_cblock *)(o), \ |
| *(k1), *(k2), *(k3), (e)) |
| #define DES_ede3_cbc_encrypt(i, o, l, k1, k2, k3, iv, e) \ |
| des_ede3_cbc_encrypt((i), (o), \ |
| (l), *(k1), *(k2), *(k3), (iv), (e)) |
| |
| /* |
| * Emulate newer digest API. |
| */ |
| |
| static void |
| EVP_MD_CTX_init(EVP_MD_CTX *ctx) |
| { |
| memset(ctx, 0, sizeof(*ctx)); |
| } |
| |
| static int |
| EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx) |
| { |
| memset(ctx, 0, sizeof(*ctx)); |
| return 1; |
| } |
| |
| static int |
| EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *md, void *engine) |
| { |
| EVP_DigestInit(ctx, md); |
| return 1; |
| } |
| |
| static int |
| EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *res, unsigned int *len) |
| { |
| EVP_DigestFinal(ctx, res, len); |
| return 1; |
| } |
| #endif /* old OpenSSL */ |
| |
| /* |
| * Provide SHA2 for older OpenSSL < 0.9.8 |
| */ |
| #if OPENSSL_VERSION_NUMBER < 0x00908000L |
| |
| #include "sha2.c" |
| #include "internal-sha2.c" |
| |
| typedef void (*init_f) (PX_MD *md); |
| |
| static int |
| compat_find_digest(const char *name, PX_MD **res) |
| { |
| init_f init = NULL; |
| |
| if (pg_strcasecmp(name, "sha224") == 0) |
| init = init_sha224; |
| else if (pg_strcasecmp(name, "sha256") == 0) |
| init = init_sha256; |
| else if (pg_strcasecmp(name, "sha384") == 0) |
| init = init_sha384; |
| else if (pg_strcasecmp(name, "sha512") == 0) |
| init = init_sha512; |
| else |
| return PXE_NO_HASH; |
| |
| *res = px_alloc(sizeof(PX_MD)); |
| init(*res); |
| return 0; |
| } |
| #else |
| #define compat_find_digest(name, res) (PXE_NO_HASH) |
| #endif |
| |
| /* |
| * Hashes |
| */ |
| |
| typedef struct OSSLDigest |
| { |
| const EVP_MD *algo; |
| EVP_MD_CTX ctx; |
| } OSSLDigest; |
| |
| static unsigned |
| digest_result_size(PX_MD *h) |
| { |
| OSSLDigest *digest = (OSSLDigest *) h->p.ptr; |
| |
| return EVP_MD_CTX_size(&digest->ctx); |
| } |
| |
| static unsigned |
| digest_block_size(PX_MD *h) |
| { |
| OSSLDigest *digest = (OSSLDigest *) h->p.ptr; |
| |
| return EVP_MD_CTX_block_size(&digest->ctx); |
| } |
| |
| static void |
| digest_reset(PX_MD *h) |
| { |
| OSSLDigest *digest = (OSSLDigest *) h->p.ptr; |
| |
| EVP_DigestInit_ex(&digest->ctx, digest->algo, NULL); |
| } |
| |
| static void |
| digest_update(PX_MD *h, const uint8 *data, unsigned dlen) |
| { |
| OSSLDigest *digest = (OSSLDigest *) h->p.ptr; |
| |
| EVP_DigestUpdate(&digest->ctx, data, dlen); |
| } |
| |
| static void |
| digest_finish(PX_MD *h, uint8 *dst) |
| { |
| OSSLDigest *digest = (OSSLDigest *) h->p.ptr; |
| |
| EVP_DigestFinal_ex(&digest->ctx, dst, NULL); |
| } |
| |
| static void |
| digest_free(PX_MD *h) |
| { |
| OSSLDigest *digest = (OSSLDigest *) h->p.ptr; |
| |
| EVP_MD_CTX_cleanup(&digest->ctx); |
| |
| px_free(digest); |
| px_free(h); |
| } |
| |
| static int px_openssl_initialized = 0; |
| |
| /* PUBLIC functions */ |
| |
| int |
| px_find_digest(const char *name, PX_MD **res) |
| { |
| const EVP_MD *md; |
| PX_MD *h; |
| OSSLDigest *digest; |
| |
| if (!px_openssl_initialized) |
| { |
| px_openssl_initialized = 1; |
| OpenSSL_add_all_algorithms(); |
| } |
| |
| md = EVP_get_digestbyname(name); |
| if (md == NULL) |
| return compat_find_digest(name, res); |
| |
| digest = px_alloc(sizeof(*digest)); |
| digest->algo = md; |
| |
| EVP_MD_CTX_init(&digest->ctx); |
| if (EVP_DigestInit_ex(&digest->ctx, digest->algo, NULL) == 0) |
| return -1; |
| |
| h = px_alloc(sizeof(*h)); |
| h->result_size = digest_result_size; |
| h->block_size = digest_block_size; |
| h->reset = digest_reset; |
| h->update = digest_update; |
| h->finish = digest_finish; |
| h->free = digest_free; |
| h->p.ptr = (void *) digest; |
| |
| *res = h; |
| return 0; |
| } |
| |
| /* |
| * Ciphers |
| * |
| * The problem with OpenSSL is that the EVP* family |
| * of functions does not allow enough flexibility |
| * and forces some of the parameters (keylen, |
| * padding) to SSL defaults. |
| * |
| * So need to manage ciphers ourselves. |
| */ |
| |
| struct ossl_cipher |
| { |
| int (*init) (PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv); |
| int (*encrypt) (PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res); |
| int (*decrypt) (PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res); |
| |
| int block_size; |
| int max_key_size; |
| int stream_cipher; |
| }; |
| |
| typedef struct |
| { |
| union |
| { |
| struct |
| { |
| BF_KEY key; |
| int num; |
| } bf; |
| struct |
| { |
| DES_key_schedule key_schedule; |
| } des; |
| struct |
| { |
| DES_key_schedule k1, |
| k2, |
| k3; |
| } des3; |
| CAST_KEY cast_key; |
| AES_KEY aes_key; |
| } u; |
| uint8 key[MAX_KEY]; |
| uint8 iv[MAX_IV]; |
| unsigned klen; |
| unsigned init; |
| const struct ossl_cipher *ciph; |
| } ossldata; |
| |
| /* generic */ |
| |
| static unsigned |
| gen_ossl_block_size(PX_Cipher *c) |
| { |
| ossldata *od = (ossldata *) c->ptr; |
| |
| return od->ciph->block_size; |
| } |
| |
| static unsigned |
| gen_ossl_key_size(PX_Cipher *c) |
| { |
| ossldata *od = (ossldata *) c->ptr; |
| |
| return od->ciph->max_key_size; |
| } |
| |
| static unsigned |
| gen_ossl_iv_size(PX_Cipher *c) |
| { |
| unsigned ivlen; |
| ossldata *od = (ossldata *) c->ptr; |
| |
| ivlen = od->ciph->block_size; |
| return ivlen; |
| } |
| |
| static void |
| gen_ossl_free(PX_Cipher *c) |
| { |
| ossldata *od = (ossldata *) c->ptr; |
| |
| memset(od, 0, sizeof(*od)); |
| px_free(od); |
| px_free(c); |
| } |
| |
| /* Blowfish */ |
| |
| /* |
| * Check if strong crypto is supported. Some openssl installations |
| * support only short keys and unfortunately BF_set_key does not return any |
| * error value. This function tests if is possible to use strong key. |
| */ |
| static int |
| bf_check_supported_key_len(void) |
| { |
| static const uint8 key[56] = { |
| 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, |
| 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f, 0x00, 0x11, 0x22, 0x33, |
| 0x44, 0x55, 0x66, 0x77, 0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd, |
| 0x3b, 0x2f, 0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76, |
| 0x1f, 0x1f, 0x1f, 0x1f, 0x0e, 0x0e, 0x0e, 0x0e, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff |
| }; |
| |
| static const uint8 data[8] = {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}; |
| static const uint8 res[8] = {0xc0, 0x45, 0x04, 0x01, 0x2e, 0x4e, 0x1f, 0x53}; |
| static uint8 out[8]; |
| |
| BF_KEY bf_key; |
| |
| /* encrypt with 448bits key and verify output */ |
| BF_set_key(&bf_key, 56, key); |
| BF_ecb_encrypt(data, out, &bf_key, BF_ENCRYPT); |
| |
| if (memcmp(out, res, 8) != 0) |
| return 0; /* Output does not match -> strong cipher is |
| * not supported */ |
| return 1; |
| } |
| |
| static int |
| bf_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) |
| { |
| ossldata *od = c->ptr; |
| static int bf_is_strong = -1; |
| |
| /* |
| * Test if key len is supported. BF_set_key silently cut large keys and it |
| * could be be a problem when user transfer crypted data from one server |
| * to another. |
| */ |
| |
| if (bf_is_strong == -1) |
| bf_is_strong = bf_check_supported_key_len(); |
| |
| if (!bf_is_strong && klen > 16) |
| return PXE_KEY_TOO_BIG; |
| |
| /* Key len is supported. We can use it. */ |
| BF_set_key(&od->u.bf.key, klen, key); |
| if (iv) |
| memcpy(od->iv, iv, BF_BLOCK); |
| else |
| memset(od->iv, 0, BF_BLOCK); |
| od->u.bf.num = 0; |
| return 0; |
| } |
| |
| static int |
| bf_ecb_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) |
| { |
| unsigned bs = gen_ossl_block_size(c); |
| unsigned i; |
| ossldata *od = c->ptr; |
| |
| for (i = 0; i < dlen / bs; i++) |
| BF_ecb_encrypt(data + i * bs, res + i * bs, &od->u.bf.key, BF_ENCRYPT); |
| return 0; |
| } |
| |
| static int |
| bf_ecb_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) |
| { |
| unsigned bs = gen_ossl_block_size(c), |
| i; |
| ossldata *od = c->ptr; |
| |
| for (i = 0; i < dlen / bs; i++) |
| BF_ecb_encrypt(data + i * bs, res + i * bs, &od->u.bf.key, BF_DECRYPT); |
| return 0; |
| } |
| |
| static int |
| bf_cbc_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| |
| BF_cbc_encrypt(data, res, dlen, &od->u.bf.key, od->iv, BF_ENCRYPT); |
| return 0; |
| } |
| |
| static int |
| bf_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| |
| BF_cbc_encrypt(data, res, dlen, &od->u.bf.key, od->iv, BF_DECRYPT); |
| return 0; |
| } |
| |
| static int |
| bf_cfb64_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| |
| BF_cfb64_encrypt(data, res, dlen, &od->u.bf.key, od->iv, |
| &od->u.bf.num, BF_ENCRYPT); |
| return 0; |
| } |
| |
| static int |
| bf_cfb64_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| |
| BF_cfb64_encrypt(data, res, dlen, &od->u.bf.key, od->iv, |
| &od->u.bf.num, BF_DECRYPT); |
| return 0; |
| } |
| |
| /* DES */ |
| |
| static int |
| ossl_des_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) |
| { |
| ossldata *od = c->ptr; |
| DES_cblock xkey; |
| |
| memset(&xkey, 0, sizeof(xkey)); |
| memcpy(&xkey, key, klen > 8 ? 8 : klen); |
| DES_set_key(&xkey, &od->u.des.key_schedule); |
| memset(&xkey, 0, sizeof(xkey)); |
| |
| if (iv) |
| memcpy(od->iv, iv, 8); |
| else |
| memset(od->iv, 0, 8); |
| return 0; |
| } |
| |
| static int |
| ossl_des_ecb_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| unsigned bs = gen_ossl_block_size(c); |
| unsigned i; |
| ossldata *od = c->ptr; |
| |
| for (i = 0; i < dlen / bs; i++) |
| DES_ecb_encrypt((DES_cblock *) (data + i * bs), |
| (DES_cblock *) (res + i * bs), |
| &od->u.des.key_schedule, 1); |
| return 0; |
| } |
| |
| static int |
| ossl_des_ecb_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| unsigned bs = gen_ossl_block_size(c); |
| unsigned i; |
| ossldata *od = c->ptr; |
| |
| for (i = 0; i < dlen / bs; i++) |
| DES_ecb_encrypt((DES_cblock *) (data + i * bs), |
| (DES_cblock *) (res + i * bs), |
| &od->u.des.key_schedule, 0); |
| return 0; |
| } |
| |
| static int |
| ossl_des_cbc_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| |
| DES_ncbc_encrypt(data, res, dlen, &od->u.des.key_schedule, |
| (DES_cblock *) od->iv, 1); |
| return 0; |
| } |
| |
| static int |
| ossl_des_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| |
| DES_ncbc_encrypt(data, res, dlen, &od->u.des.key_schedule, |
| (DES_cblock *) od->iv, 0); |
| return 0; |
| } |
| |
| /* DES3 */ |
| |
| static int |
| ossl_des3_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) |
| { |
| ossldata *od = c->ptr; |
| DES_cblock xkey1, |
| xkey2, |
| xkey3; |
| |
| memset(&xkey1, 0, sizeof(xkey1)); |
| memset(&xkey2, 0, sizeof(xkey2)); |
| memset(&xkey3, 0, sizeof(xkey3)); |
| memcpy(&xkey1, key, klen > 8 ? 8 : klen); |
| if (klen > 8) |
| memcpy(&xkey2, key + 8, (klen - 8) > 8 ? 8 : (klen - 8)); |
| if (klen > 16) |
| memcpy(&xkey3, key + 16, (klen - 16) > 8 ? 8 : (klen - 16)); |
| |
| DES_set_key(&xkey1, &od->u.des3.k1); |
| DES_set_key(&xkey2, &od->u.des3.k2); |
| DES_set_key(&xkey3, &od->u.des3.k3); |
| memset(&xkey1, 0, sizeof(xkey1)); |
| memset(&xkey2, 0, sizeof(xkey2)); |
| memset(&xkey3, 0, sizeof(xkey3)); |
| |
| if (iv) |
| memcpy(od->iv, iv, 8); |
| else |
| memset(od->iv, 0, 8); |
| return 0; |
| } |
| |
| static int |
| ossl_des3_ecb_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| unsigned bs = gen_ossl_block_size(c); |
| unsigned i; |
| ossldata *od = c->ptr; |
| |
| for (i = 0; i < dlen / bs; i++) |
| DES_ecb3_encrypt((void *) (data + i * bs), (void *) (res + i * bs), |
| &od->u.des3.k1, &od->u.des3.k2, &od->u.des3.k3, 1); |
| return 0; |
| } |
| |
| static int |
| ossl_des3_ecb_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| unsigned bs = gen_ossl_block_size(c); |
| unsigned i; |
| ossldata *od = c->ptr; |
| |
| for (i = 0; i < dlen / bs; i++) |
| DES_ecb3_encrypt((void *) (data + i * bs), (void *) (res + i * bs), |
| &od->u.des3.k1, &od->u.des3.k2, &od->u.des3.k3, 0); |
| return 0; |
| } |
| |
| static int |
| ossl_des3_cbc_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| |
| DES_ede3_cbc_encrypt(data, res, dlen, |
| &od->u.des3.k1, &od->u.des3.k2, &od->u.des3.k3, |
| (DES_cblock *) od->iv, 1); |
| return 0; |
| } |
| |
| static int |
| ossl_des3_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| |
| DES_ede3_cbc_encrypt(data, res, dlen, |
| &od->u.des3.k1, &od->u.des3.k2, &od->u.des3.k3, |
| (DES_cblock *) od->iv, 0); |
| return 0; |
| } |
| |
| /* CAST5 */ |
| |
| static int |
| ossl_cast_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) |
| { |
| ossldata *od = c->ptr; |
| unsigned bs = gen_ossl_block_size(c); |
| |
| CAST_set_key(&od->u.cast_key, klen, key); |
| if (iv) |
| memcpy(od->iv, iv, bs); |
| else |
| memset(od->iv, 0, bs); |
| return 0; |
| } |
| |
| static int |
| ossl_cast_ecb_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) |
| { |
| unsigned bs = gen_ossl_block_size(c); |
| ossldata *od = c->ptr; |
| const uint8 *end = data + dlen - bs; |
| |
| for (; data <= end; data += bs, res += bs) |
| CAST_ecb_encrypt(data, res, &od->u.cast_key, CAST_ENCRYPT); |
| return 0; |
| } |
| |
| static int |
| ossl_cast_ecb_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) |
| { |
| unsigned bs = gen_ossl_block_size(c); |
| ossldata *od = c->ptr; |
| const uint8 *end = data + dlen - bs; |
| |
| for (; data <= end; data += bs, res += bs) |
| CAST_ecb_encrypt(data, res, &od->u.cast_key, CAST_DECRYPT); |
| return 0; |
| } |
| |
| static int |
| ossl_cast_cbc_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| |
| CAST_cbc_encrypt(data, res, dlen, &od->u.cast_key, od->iv, CAST_ENCRYPT); |
| return 0; |
| } |
| |
| static int |
| ossl_cast_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| |
| CAST_cbc_encrypt(data, res, dlen, &od->u.cast_key, od->iv, CAST_DECRYPT); |
| return 0; |
| } |
| |
| /* AES */ |
| |
| static int |
| ossl_aes_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) |
| { |
| ossldata *od = c->ptr; |
| unsigned bs = gen_ossl_block_size(c); |
| |
| if (klen <= 128 / 8) |
| od->klen = 128 / 8; |
| else if (klen <= 192 / 8) |
| od->klen = 192 / 8; |
| else if (klen <= 256 / 8) |
| od->klen = 256 / 8; |
| else |
| return PXE_KEY_TOO_BIG; |
| |
| memcpy(od->key, key, klen); |
| |
| if (iv) |
| memcpy(od->iv, iv, bs); |
| else |
| memset(od->iv, 0, bs); |
| return 0; |
| } |
| |
| static int |
| ossl_aes_key_init(ossldata *od, int type) |
| { |
| int err; |
| |
| /* |
| * Strong key support could be missing on some openssl installations. We |
| * must check return value from set key function. |
| */ |
| if (type == AES_ENCRYPT) |
| err = AES_set_encrypt_key(od->key, od->klen * 8, &od->u.aes_key); |
| else |
| err = AES_set_decrypt_key(od->key, od->klen * 8, &od->u.aes_key); |
| |
| if (err == 0) |
| { |
| od->init = 1; |
| return 0; |
| } |
| od->init = 0; |
| return PXE_KEY_TOO_BIG; |
| } |
| |
| static int |
| ossl_aes_ecb_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| unsigned bs = gen_ossl_block_size(c); |
| ossldata *od = c->ptr; |
| const uint8 *end = data + dlen - bs; |
| int err; |
| |
| if (!od->init) |
| if ((err = ossl_aes_key_init(od, AES_ENCRYPT)) != 0) |
| return err; |
| |
| for (; data <= end; data += bs, res += bs) |
| AES_ecb_encrypt(data, res, &od->u.aes_key, AES_ENCRYPT); |
| return 0; |
| } |
| |
| static int |
| ossl_aes_ecb_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| unsigned bs = gen_ossl_block_size(c); |
| ossldata *od = c->ptr; |
| const uint8 *end = data + dlen - bs; |
| int err; |
| |
| if (!od->init) |
| if ((err = ossl_aes_key_init(od, AES_DECRYPT)) != 0) |
| return err; |
| |
| for (; data <= end; data += bs, res += bs) |
| AES_ecb_encrypt(data, res, &od->u.aes_key, AES_DECRYPT); |
| return 0; |
| } |
| |
| static int |
| ossl_aes_cbc_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| int err; |
| |
| if (!od->init) |
| if ((err = ossl_aes_key_init(od, AES_ENCRYPT)) != 0) |
| return err; |
| |
| AES_cbc_encrypt(data, res, dlen, &od->u.aes_key, od->iv, AES_ENCRYPT); |
| return 0; |
| } |
| |
| static int |
| ossl_aes_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, |
| uint8 *res) |
| { |
| ossldata *od = c->ptr; |
| int err; |
| |
| if (!od->init) |
| if ((err = ossl_aes_key_init(od, AES_DECRYPT)) != 0) |
| return err; |
| |
| AES_cbc_encrypt(data, res, dlen, &od->u.aes_key, od->iv, AES_DECRYPT); |
| return 0; |
| } |
| |
| /* |
| * aliases |
| */ |
| |
| static PX_Alias ossl_aliases[] = { |
| {"bf", "bf-cbc"}, |
| {"blowfish", "bf-cbc"}, |
| {"blowfish-cbc", "bf-cbc"}, |
| {"blowfish-ecb", "bf-ecb"}, |
| {"blowfish-cfb", "bf-cfb"}, |
| {"des", "des-cbc"}, |
| {"3des", "des3-cbc"}, |
| {"3des-ecb", "des3-ecb"}, |
| {"3des-cbc", "des3-cbc"}, |
| {"cast5", "cast5-cbc"}, |
| {"aes", "aes-cbc"}, |
| {"rijndael", "aes-cbc"}, |
| {"rijndael-cbc", "aes-cbc"}, |
| {"rijndael-ecb", "aes-ecb"}, |
| {NULL} |
| }; |
| |
| static const struct ossl_cipher ossl_bf_cbc = { |
| bf_init, bf_cbc_encrypt, bf_cbc_decrypt, |
| 64 / 8, 448 / 8, 0 |
| }; |
| |
| static const struct ossl_cipher ossl_bf_ecb = { |
| bf_init, bf_ecb_encrypt, bf_ecb_decrypt, |
| 64 / 8, 448 / 8, 0 |
| }; |
| |
| static const struct ossl_cipher ossl_bf_cfb = { |
| bf_init, bf_cfb64_encrypt, bf_cfb64_decrypt, |
| 64 / 8, 448 / 8, 1 |
| }; |
| |
| static const struct ossl_cipher ossl_des_ecb = { |
| ossl_des_init, ossl_des_ecb_encrypt, ossl_des_ecb_decrypt, |
| 64 / 8, 64 / 8, 0 |
| }; |
| |
| static const struct ossl_cipher ossl_des_cbc = { |
| ossl_des_init, ossl_des_cbc_encrypt, ossl_des_cbc_decrypt, |
| 64 / 8, 64 / 8, 0 |
| }; |
| |
| static const struct ossl_cipher ossl_des3_ecb = { |
| ossl_des3_init, ossl_des3_ecb_encrypt, ossl_des3_ecb_decrypt, |
| 64 / 8, 192 / 8, 0 |
| }; |
| |
| static const struct ossl_cipher ossl_des3_cbc = { |
| ossl_des3_init, ossl_des3_cbc_encrypt, ossl_des3_cbc_decrypt, |
| 64 / 8, 192 / 8, 0 |
| }; |
| |
| static const struct ossl_cipher ossl_cast_ecb = { |
| ossl_cast_init, ossl_cast_ecb_encrypt, ossl_cast_ecb_decrypt, |
| 64 / 8, 128 / 8, 0 |
| }; |
| |
| static const struct ossl_cipher ossl_cast_cbc = { |
| ossl_cast_init, ossl_cast_cbc_encrypt, ossl_cast_cbc_decrypt, |
| 64 / 8, 128 / 8, 0 |
| }; |
| |
| static const struct ossl_cipher ossl_aes_ecb = { |
| ossl_aes_init, ossl_aes_ecb_encrypt, ossl_aes_ecb_decrypt, |
| 128 / 8, 256 / 8, 0 |
| }; |
| |
| static const struct ossl_cipher ossl_aes_cbc = { |
| ossl_aes_init, ossl_aes_cbc_encrypt, ossl_aes_cbc_decrypt, |
| 128 / 8, 256 / 8, 0 |
| }; |
| |
| /* |
| * Special handlers |
| */ |
| struct ossl_cipher_lookup |
| { |
| const char *name; |
| const struct ossl_cipher *ciph; |
| }; |
| |
| static const struct ossl_cipher_lookup ossl_cipher_types[] = { |
| {"bf-cbc", &ossl_bf_cbc}, |
| {"bf-ecb", &ossl_bf_ecb}, |
| {"bf-cfb", &ossl_bf_cfb}, |
| {"des-ecb", &ossl_des_ecb}, |
| {"des-cbc", &ossl_des_cbc}, |
| {"des3-ecb", &ossl_des3_ecb}, |
| {"des3-cbc", &ossl_des3_cbc}, |
| {"cast5-ecb", &ossl_cast_ecb}, |
| {"cast5-cbc", &ossl_cast_cbc}, |
| {"aes-ecb", &ossl_aes_ecb}, |
| {"aes-cbc", &ossl_aes_cbc}, |
| {NULL} |
| }; |
| |
| /* PUBLIC functions */ |
| |
| int |
| px_find_cipher(const char *name, PX_Cipher **res) |
| { |
| const struct ossl_cipher_lookup *i; |
| PX_Cipher *c = NULL; |
| ossldata *od; |
| |
| name = px_resolve_alias(ossl_aliases, name); |
| for (i = ossl_cipher_types; i->name; i++) |
| if (strcmp(i->name, name) == 0) |
| break; |
| if (i->name == NULL) |
| return PXE_NO_CIPHER; |
| |
| od = px_alloc(sizeof(*od)); |
| memset(od, 0, sizeof(*od)); |
| od->ciph = i->ciph; |
| |
| c = px_alloc(sizeof(*c)); |
| c->block_size = gen_ossl_block_size; |
| c->key_size = gen_ossl_key_size; |
| c->iv_size = gen_ossl_iv_size; |
| c->free = gen_ossl_free; |
| c->init = od->ciph->init; |
| c->encrypt = od->ciph->encrypt; |
| c->decrypt = od->ciph->decrypt; |
| c->ptr = od; |
| |
| *res = c; |
| return 0; |
| } |
| |
| |
| static int openssl_random_init = 0; |
| |
| /* |
| * OpenSSL random should re-feeded occasionally. From /dev/urandom |
| * preferably. |
| */ |
| static void |
| init_openssl_rand(void) |
| { |
| if (RAND_get_rand_method() == NULL) |
| RAND_set_rand_method(RAND_SSLeay()); |
| openssl_random_init = 1; |
| } |
| |
| int |
| px_get_random_bytes(uint8 *dst, unsigned count) |
| { |
| int res; |
| |
| if (!openssl_random_init) |
| init_openssl_rand(); |
| |
| res = RAND_bytes(dst, count); |
| if (res == 1) |
| return count; |
| |
| return PXE_OSSL_RAND_ERROR; |
| } |
| |
| int |
| px_get_pseudo_random_bytes(uint8 *dst, unsigned count) |
| { |
| int res; |
| |
| if (!openssl_random_init) |
| init_openssl_rand(); |
| |
| res = RAND_pseudo_bytes(dst, count); |
| if (res == 0 || res == 1) |
| return count; |
| |
| return PXE_OSSL_RAND_ERROR; |
| } |
| |
| int |
| px_add_entropy(const uint8 *data, unsigned count) |
| { |
| /* |
| * estimate 0 bits |
| */ |
| RAND_add(data, count, 0); |
| return 0; |
| } |