1abf76e82c
Few cleanups and couple of new things: - add SHA2 algorithm to older OpenSSL - add BIGNUM math to have public-key cryptography work on non-OpenSSL build. - gen_random_bytes() function The status of SHA2 algoritms and public-key encryption can now be changed to 'always available.' That makes pgcrypto functionally complete and unless there will be new editions of AES, SHA2 or OpenPGP standards, there is no major changes planned.
945 lines
20 KiB
C
945 lines
20 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* $PostgreSQL: pgsql/contrib/pgcrypto/openssl.c,v 1.28 2006/07/13 04:15:25 neilc Exp $
|
|
*/
|
|
|
|
#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
|
|
|
|
#define AES_set_encrypt_key(key, kbits, ctx) \
|
|
aes_set_key((ctx), (key), (kbits), 1)
|
|
|
|
#define AES_set_decrypt_key(key, kbits, ctx) \
|
|
aes_set_key((ctx), (key), (kbits), 0)
|
|
|
|
#define AES_ecb_encrypt(src, dst, ctx, enc) \
|
|
do { \
|
|
memcpy((dst), (src), 16); \
|
|
if (enc) \
|
|
aes_ecb_encrypt((ctx), (dst), 16); \
|
|
else \
|
|
aes_ecb_decrypt((ctx), (dst), 16); \
|
|
} while (0)
|
|
|
|
#define AES_cbc_encrypt(src, dst, len, ctx, iv, enc) \
|
|
do { \
|
|
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); \
|
|
} \
|
|
} while (0)
|
|
|
|
/*
|
|
* 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 int (*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 */
|
|
|
|
static int
|
|
bf_init(PX_Cipher * c, const uint8 *key, unsigned klen, const uint8 *iv)
|
|
{
|
|
ossldata *od = c->ptr;
|
|
|
|
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 void
|
|
ossl_aes_key_init(ossldata * od, int type)
|
|
{
|
|
if (type == AES_ENCRYPT)
|
|
AES_set_encrypt_key(od->key, od->klen * 8, &od->u.aes_key);
|
|
else
|
|
AES_set_decrypt_key(od->key, od->klen * 8, &od->u.aes_key);
|
|
od->init = 1;
|
|
}
|
|
|
|
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;
|
|
|
|
if (!od->init)
|
|
ossl_aes_key_init(od, AES_ENCRYPT);
|
|
|
|
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;
|
|
|
|
if (!od->init)
|
|
ossl_aes_key_init(od, AES_DECRYPT);
|
|
|
|
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;
|
|
|
|
if (!od->init)
|
|
ossl_aes_key_init(od, AES_ENCRYPT);
|
|
|
|
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;
|
|
|
|
if (!od->init)
|
|
ossl_aes_key_init(od, AES_DECRYPT);
|
|
|
|
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))
|
|
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;
|
|
}
|