/* $NetBSD: crypto_openssl.c,v 1.11.6.6 2009/04/29 10:50:25 tteras Exp $ */
|
|
/* Id: crypto_openssl.c,v 1.47 2006/05/06 20:42:09 manubsd Exp */
|
|
/*
|
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
* 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.
|
* 3. Neither the name of the project nor the names of its contributors
|
* may be used to endorse or promote products derived from this software
|
* without specific prior written permission.
|
*
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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.
|
*/
|
|
#include "config.h"
|
|
#include <sys/types.h>
|
#include <sys/param.h>
|
|
#include <stdlib.h>
|
#include <stdio.h>
|
#include <limits.h>
|
#include <string.h>
|
|
/* get openssl/ssleay version number */
|
#include <openssl/opensslv.h>
|
|
#if !defined(OPENSSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x0090602fL)
|
#error OpenSSL version 0.9.6 or later required.
|
#endif
|
|
#include <openssl/pem.h>
|
#include <openssl/evp.h>
|
#include <openssl/x509.h>
|
#include <openssl/x509v3.h>
|
#include <openssl/x509_vfy.h>
|
#include <openssl/bn.h>
|
#include <openssl/dh.h>
|
#include <openssl/md5.h>
|
#include <openssl/sha.h>
|
#include <openssl/hmac.h>
|
#include <openssl/des.h>
|
#include <openssl/crypto.h>
|
#ifdef HAVE_OPENSSL_ENGINE_H
|
#include <openssl/engine.h>
|
#endif
|
#ifndef ANDROID_CHANGES
|
#include <openssl/blowfish.h>
|
#include <openssl/cast.h>
|
#else
|
#define EVP_bf_cbc() NULL
|
#define EVP_cast5_cbc() NULL
|
#endif
|
#include <openssl/err.h>
|
#ifdef HAVE_OPENSSL_RC5_H
|
#include <openssl/rc5.h>
|
#endif
|
#ifdef HAVE_OPENSSL_IDEA_H
|
#include <openssl/idea.h>
|
#endif
|
#if defined(HAVE_OPENSSL_AES_H)
|
#include <openssl/aes.h>
|
#elif defined(HAVE_OPENSSL_RIJNDAEL_H)
|
#include <openssl/rijndael.h>
|
#else
|
#include "crypto/rijndael/rijndael-api-fst.h"
|
#endif
|
#if defined(HAVE_OPENSSL_CAMELLIA_H)
|
#include <openssl/camellia.h>
|
#endif
|
#ifdef WITH_SHA2
|
#ifdef HAVE_OPENSSL_SHA2_H
|
#include <openssl/sha2.h>
|
#else
|
#include "crypto/sha2/sha2.h"
|
#endif
|
#endif
|
#include "plog.h"
|
|
/* 0.9.7 stuff? */
|
#if OPENSSL_VERSION_NUMBER < 0x0090700fL
|
typedef STACK_OF(GENERAL_NAME) GENERAL_NAMES;
|
#else
|
#define USE_NEW_DES_API
|
#endif
|
|
#define OpenSSL_BUG() do { plog(LLV_ERROR, LOCATION, NULL, "OpenSSL function failed\n"); } while(0)
|
|
#include "var.h"
|
#include "misc.h"
|
#include "vmbuf.h"
|
#include "plog.h"
|
#include "crypto_openssl.h"
|
#include "debug.h"
|
#include "gcmalloc.h"
|
|
#if defined(OPENSSL_IS_BORINGSSL)
|
/* HMAC_cleanup is deprecated wrapper in OpenSSL and has been removed in
|
* BoringSSL. */
|
#define HMAC_cleanup(ctx) HMAC_CTX_cleanup(ctx)
|
#endif
|
|
/*
|
* I hate to cast every parameter to des_xx into void *, but it is
|
* necessary for SSLeay/OpenSSL portability. It sucks.
|
*/
|
|
static int cb_check_cert_local __P((int, X509_STORE_CTX *));
|
static int cb_check_cert_remote __P((int, X509_STORE_CTX *));
|
static X509 *mem2x509 __P((vchar_t *));
|
|
static caddr_t eay_hmac_init __P((vchar_t *, const EVP_MD *));
|
|
/* X509 Certificate */
|
/*
|
* convert the string of the subject name into DER
|
* e.g. str = "C=JP, ST=Kanagawa";
|
*/
|
vchar_t *
|
eay_str2asn1dn(str, len)
|
const char *str;
|
int len;
|
{
|
X509_NAME *name;
|
char *buf;
|
char *field, *value;
|
int i, j;
|
vchar_t *ret = NULL;
|
caddr_t p;
|
|
if (len == -1)
|
len = strlen(str);
|
|
buf = racoon_malloc(len + 1);
|
if (!buf) {
|
plog(LLV_WARNING, LOCATION, NULL,"failed to allocate buffer\n");
|
return NULL;
|
}
|
memcpy(buf, str, len);
|
|
name = X509_NAME_new();
|
|
field = &buf[0];
|
value = NULL;
|
for (i = 0; i < len; i++) {
|
if (!value && buf[i] == '=') {
|
buf[i] = '\0';
|
value = &buf[i + 1];
|
continue;
|
} else if (buf[i] == ',' || buf[i] == '/') {
|
buf[i] = '\0';
|
|
plog(LLV_DEBUG, LOCATION, NULL, "DN: %s=%s\n",
|
field, value);
|
|
if (!value) goto err;
|
if (!X509_NAME_add_entry_by_txt(name, field,
|
(value[0] == '*' && value[1] == 0) ?
|
V_ASN1_PRINTABLESTRING : MBSTRING_ASC,
|
(unsigned char *) value, -1, -1, 0)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"Invalid DN field: %s=%s\n",
|
field, value);
|
plog(LLV_ERROR, LOCATION, NULL,
|
"%s\n", eay_strerror());
|
goto err;
|
}
|
for (j = i + 1; j < len; j++) {
|
if (buf[j] != ' ')
|
break;
|
}
|
field = &buf[j];
|
value = NULL;
|
continue;
|
}
|
}
|
buf[len] = '\0';
|
|
plog(LLV_DEBUG, LOCATION, NULL, "DN: %s=%s\n",
|
field, value);
|
|
if (!value) goto err;
|
if (!X509_NAME_add_entry_by_txt(name, field,
|
(value[0] == '*' && value[1] == 0) ?
|
V_ASN1_PRINTABLESTRING : MBSTRING_ASC,
|
(unsigned char *) value, -1, -1, 0)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"Invalid DN field: %s=%s\n",
|
field, value);
|
plog(LLV_ERROR, LOCATION, NULL,
|
"%s\n", eay_strerror());
|
goto err;
|
}
|
|
i = i2d_X509_NAME(name, NULL);
|
if (!i)
|
goto err;
|
ret = vmalloc(i);
|
if (!ret)
|
goto err;
|
p = ret->v;
|
i = i2d_X509_NAME(name, (void *)&p);
|
if (!i)
|
goto err;
|
|
return ret;
|
|
err:
|
if (buf)
|
racoon_free(buf);
|
if (name)
|
X509_NAME_free(name);
|
if (ret)
|
vfree(ret);
|
return NULL;
|
}
|
|
/*
|
* convert the hex string of the subject name into DER
|
*/
|
vchar_t *
|
eay_hex2asn1dn(const char *hex, int len)
|
{
|
BIGNUM *bn = BN_new();
|
char *binbuf;
|
size_t binlen;
|
vchar_t *ret = NULL;
|
|
if (len == -1)
|
len = strlen(hex);
|
|
if (BN_hex2bn(&bn, hex) != len) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"conversion of Hex-encoded ASN1 string to binary failed: %s\n",
|
eay_strerror());
|
goto out;
|
}
|
|
binlen = BN_num_bytes(bn);
|
ret = vmalloc(binlen);
|
if (!ret) {
|
plog(LLV_WARNING, LOCATION, NULL,"failed to allocate buffer\n");
|
return NULL;
|
}
|
binbuf = ret->v;
|
|
BN_bn2bin(bn, (unsigned char *) binbuf);
|
|
out:
|
BN_free(bn);
|
|
return ret;
|
}
|
|
/*
|
* The following are derived from code in crypto/x509/x509_cmp.c
|
* in OpenSSL0.9.7c:
|
* X509_NAME_wildcmp() adds wildcard matching to the original
|
* X509_NAME_cmp(), nocase_cmp() and nocase_spacenorm_cmp() are as is.
|
*/
|
#include <ctype.h>
|
/* Case insensitive string comparision */
|
static int nocase_cmp(const ASN1_STRING *a, const ASN1_STRING *b)
|
{
|
int i;
|
|
if (a->length != b->length)
|
return (a->length - b->length);
|
|
for (i=0; i<a->length; i++)
|
{
|
int ca, cb;
|
|
ca = tolower(a->data[i]);
|
cb = tolower(b->data[i]);
|
|
if (ca != cb)
|
return(ca-cb);
|
}
|
return 0;
|
}
|
|
/* Case insensitive string comparision with space normalization
|
* Space normalization - ignore leading, trailing spaces,
|
* multiple spaces between characters are replaced by single space
|
*/
|
static int nocase_spacenorm_cmp(const ASN1_STRING *a, const ASN1_STRING *b)
|
{
|
unsigned char *pa = NULL, *pb = NULL;
|
int la, lb;
|
|
la = a->length;
|
lb = b->length;
|
pa = a->data;
|
pb = b->data;
|
|
/* skip leading spaces */
|
while (la > 0 && isspace(*pa))
|
{
|
la--;
|
pa++;
|
}
|
while (lb > 0 && isspace(*pb))
|
{
|
lb--;
|
pb++;
|
}
|
|
/* skip trailing spaces */
|
while (la > 0 && isspace(pa[la-1]))
|
la--;
|
while (lb > 0 && isspace(pb[lb-1]))
|
lb--;
|
|
/* compare strings with space normalization */
|
while (la > 0 && lb > 0)
|
{
|
int ca, cb;
|
|
/* compare character */
|
ca = tolower(*pa);
|
cb = tolower(*pb);
|
if (ca != cb)
|
return (ca - cb);
|
|
pa++; pb++;
|
la--; lb--;
|
|
if (la <= 0 || lb <= 0)
|
break;
|
|
/* is white space next character ? */
|
if (isspace(*pa) && isspace(*pb))
|
{
|
/* skip remaining white spaces */
|
while (la > 0 && isspace(*pa))
|
{
|
la--;
|
pa++;
|
}
|
while (lb > 0 && isspace(*pb))
|
{
|
lb--;
|
pb++;
|
}
|
}
|
}
|
if (la > 0 || lb > 0)
|
return la - lb;
|
|
return 0;
|
}
|
|
static int X509_NAME_wildcmp(const X509_NAME *a, const X509_NAME *b)
|
{
|
int i,j;
|
X509_NAME_ENTRY *na,*nb;
|
|
if (sk_X509_NAME_ENTRY_num(a->entries)
|
!= sk_X509_NAME_ENTRY_num(b->entries))
|
return sk_X509_NAME_ENTRY_num(a->entries)
|
-sk_X509_NAME_ENTRY_num(b->entries);
|
for (i=sk_X509_NAME_ENTRY_num(a->entries)-1; i>=0; i--)
|
{
|
na=sk_X509_NAME_ENTRY_value(a->entries,i);
|
nb=sk_X509_NAME_ENTRY_value(b->entries,i);
|
j=OBJ_cmp(na->object,nb->object);
|
if (j) return(j);
|
if ((na->value->length == 1 && na->value->data[0] == '*')
|
|| (nb->value->length == 1 && nb->value->data[0] == '*'))
|
continue;
|
j=na->value->type-nb->value->type;
|
if (j) return(j);
|
if (na->value->type == V_ASN1_PRINTABLESTRING)
|
j=nocase_spacenorm_cmp(na->value, nb->value);
|
else if (na->value->type == V_ASN1_IA5STRING
|
&& OBJ_obj2nid(na->object) == NID_pkcs9_emailAddress)
|
j=nocase_cmp(na->value, nb->value);
|
else
|
{
|
j=na->value->length-nb->value->length;
|
if (j) return(j);
|
j=memcmp(na->value->data,nb->value->data,
|
na->value->length);
|
}
|
if (j) return(j);
|
j=na->set-nb->set;
|
if (j) return(j);
|
}
|
|
return(0);
|
}
|
|
/*
|
* compare two subjectNames.
|
* OUT: 0: equal
|
* positive:
|
* -1: other error.
|
*/
|
int
|
eay_cmp_asn1dn(n1, n2)
|
vchar_t *n1, *n2;
|
{
|
X509_NAME *a = NULL, *b = NULL;
|
caddr_t p;
|
int i = -1;
|
|
p = n1->v;
|
if (!d2i_X509_NAME(&a, (void *)&p, n1->l))
|
goto end;
|
p = n2->v;
|
if (!d2i_X509_NAME(&b, (void *)&p, n2->l))
|
goto end;
|
|
i = X509_NAME_wildcmp(a, b);
|
|
end:
|
if (a)
|
X509_NAME_free(a);
|
if (b)
|
X509_NAME_free(b);
|
return i;
|
}
|
|
#ifdef ANDROID_CHANGES
|
|
static BIO *BIO_from_android(char *path)
|
{
|
void *data;
|
if (sscanf(path, pname, &data) == 1) {
|
return BIO_new_mem_buf(data, -1);
|
}
|
return NULL;
|
}
|
|
#endif
|
|
/*
|
* this functions is derived from apps/verify.c in OpenSSL0.9.5
|
*/
|
int
|
eay_check_x509cert(cert, CApath, CAfile, local)
|
vchar_t *cert;
|
char *CApath;
|
char *CAfile;
|
int local;
|
{
|
X509_STORE *cert_ctx = NULL;
|
X509_LOOKUP *lookup = NULL;
|
X509 *x509 = NULL;
|
X509_STORE_CTX *csc;
|
int error = -1;
|
|
cert_ctx = X509_STORE_new();
|
if (cert_ctx == NULL)
|
goto end;
|
|
if (local)
|
X509_STORE_set_verify_cb_func(cert_ctx, cb_check_cert_local);
|
else
|
X509_STORE_set_verify_cb_func(cert_ctx, cb_check_cert_remote);
|
|
#ifdef ANDROID_CHANGES
|
if (pname) {
|
BIO *bio = BIO_from_android(CAfile);
|
STACK_OF(X509_INFO) *stack;
|
X509_INFO *info;
|
int i;
|
|
if (!bio) {
|
goto end;
|
}
|
stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
|
BIO_free(bio);
|
if (!stack) {
|
goto end;
|
}
|
for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
|
info = sk_X509_INFO_value(stack, i);
|
if (info->x509) {
|
X509_STORE_add_cert(cert_ctx, info->x509);
|
}
|
if (info->crl) {
|
X509_STORE_add_crl(cert_ctx, info->crl);
|
}
|
}
|
sk_X509_INFO_pop_free(stack, X509_INFO_free);
|
} else {
|
#endif
|
lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file());
|
if (lookup == NULL)
|
goto end;
|
|
X509_LOOKUP_load_file(lookup, CAfile,
|
(CAfile == NULL) ? X509_FILETYPE_DEFAULT : X509_FILETYPE_PEM);
|
|
lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_hash_dir());
|
if (lookup == NULL)
|
goto end;
|
error = X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM);
|
if(!error) {
|
error = -1;
|
goto end;
|
}
|
error = -1; /* initialized */
|
#ifdef ANDROID_CHANGES
|
}
|
#endif
|
|
/* read the certificate to be verified */
|
x509 = mem2x509(cert);
|
if (x509 == NULL)
|
goto end;
|
|
csc = X509_STORE_CTX_new();
|
if (csc == NULL)
|
goto end;
|
X509_STORE_CTX_init(csc, cert_ctx, x509, NULL);
|
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
|
X509_STORE_CTX_set_flags (csc, X509_V_FLAG_CRL_CHECK);
|
X509_STORE_CTX_set_flags (csc, X509_V_FLAG_CRL_CHECK_ALL);
|
#endif
|
error = X509_verify_cert(csc);
|
X509_STORE_CTX_free(csc);
|
|
/*
|
* if x509_verify_cert() is successful then the value of error is
|
* set non-zero.
|
*/
|
error = error ? 0 : -1;
|
|
end:
|
if (error)
|
plog(LLV_WARNING, LOCATION, NULL,"%s\n", eay_strerror());
|
if (cert_ctx != NULL)
|
X509_STORE_free(cert_ctx);
|
if (x509 != NULL)
|
X509_free(x509);
|
|
return(error);
|
}
|
|
/*
|
* callback function for verifing certificate.
|
* this function is derived from cb() in openssl/apps/s_server.c
|
*/
|
static int
|
cb_check_cert_local(ok, ctx)
|
int ok;
|
X509_STORE_CTX *ctx;
|
{
|
char buf[256];
|
int log_tag;
|
|
if (!ok) {
|
X509_NAME_oneline(
|
X509_get_subject_name(ctx->current_cert),
|
buf,
|
256);
|
/*
|
* since we are just checking the certificates, it is
|
* ok if they are self signed. But we should still warn
|
* the user.
|
*/
|
switch (ctx->error) {
|
case X509_V_ERR_CERT_HAS_EXPIRED:
|
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
|
case X509_V_ERR_INVALID_CA:
|
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
|
case X509_V_ERR_INVALID_PURPOSE:
|
case X509_V_ERR_UNABLE_TO_GET_CRL:
|
ok = 1;
|
log_tag = LLV_WARNING;
|
break;
|
default:
|
log_tag = LLV_ERROR;
|
}
|
plog(log_tag, LOCATION, NULL,
|
"%s(%d) at depth:%d SubjectName:%s\n",
|
X509_verify_cert_error_string(ctx->error),
|
ctx->error,
|
ctx->error_depth,
|
buf);
|
}
|
ERR_clear_error();
|
|
return ok;
|
}
|
|
/*
|
* callback function for verifing remote certificates.
|
* this function is derived from cb() in openssl/apps/s_server.c
|
*/
|
static int
|
cb_check_cert_remote(ok, ctx)
|
int ok;
|
X509_STORE_CTX *ctx;
|
{
|
char buf[256];
|
int log_tag;
|
|
if (!ok) {
|
X509_NAME_oneline(
|
X509_get_subject_name(ctx->current_cert),
|
buf,
|
256);
|
switch (ctx->error) {
|
case X509_V_ERR_UNABLE_TO_GET_CRL:
|
ok = 1;
|
log_tag = LLV_WARNING;
|
break;
|
default:
|
log_tag = LLV_ERROR;
|
}
|
plog(log_tag, LOCATION, NULL,
|
"%s(%d) at depth:%d SubjectName:%s\n",
|
X509_verify_cert_error_string(ctx->error),
|
ctx->error,
|
ctx->error_depth,
|
buf);
|
}
|
ERR_clear_error();
|
|
return ok;
|
}
|
|
/*
|
* get a subjectAltName from X509 certificate.
|
*/
|
vchar_t *
|
eay_get_x509asn1subjectname(cert)
|
vchar_t *cert;
|
{
|
X509 *x509 = NULL;
|
u_char *bp;
|
vchar_t *name = NULL;
|
int len;
|
|
bp = (unsigned char *) cert->v;
|
|
x509 = mem2x509(cert);
|
if (x509 == NULL)
|
goto error;
|
|
/* get the length of the name */
|
len = i2d_X509_NAME(x509->cert_info->subject, NULL);
|
name = vmalloc(len);
|
if (!name)
|
goto error;
|
/* get the name */
|
bp = (unsigned char *) name->v;
|
len = i2d_X509_NAME(x509->cert_info->subject, &bp);
|
|
X509_free(x509);
|
|
return name;
|
|
error:
|
plog(LLV_ERROR, LOCATION, NULL, "%s\n", eay_strerror());
|
|
if (name != NULL)
|
vfree(name);
|
|
if (x509 != NULL)
|
X509_free(x509);
|
|
return NULL;
|
}
|
|
/*
|
* get the subjectAltName from X509 certificate.
|
* the name must be terminated by '\0'.
|
*/
|
int
|
eay_get_x509subjectaltname(cert, altname, type, pos)
|
vchar_t *cert;
|
char **altname;
|
int *type;
|
int pos;
|
{
|
X509 *x509 = NULL;
|
GENERAL_NAMES *gens = NULL;
|
GENERAL_NAME *gen;
|
int len;
|
int error = -1;
|
|
*altname = NULL;
|
*type = GENT_OTHERNAME;
|
|
x509 = mem2x509(cert);
|
if (x509 == NULL)
|
goto end;
|
|
gens = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL);
|
if (gens == NULL)
|
goto end;
|
|
/* there is no data at "pos" */
|
if (pos > sk_GENERAL_NAME_num(gens))
|
goto end;
|
|
gen = sk_GENERAL_NAME_value(gens, pos - 1);
|
|
/* read DNSName / Email */
|
if (gen->type == GEN_DNS ||
|
gen->type == GEN_EMAIL ||
|
gen->type == GEN_URI )
|
{
|
/* make sure if the data is terminated by '\0'. */
|
if (gen->d.ia5->data[gen->d.ia5->length] != '\0')
|
{
|
plog(LLV_ERROR, LOCATION, NULL,
|
"data is not terminated by NUL.");
|
racoon_hexdump(gen->d.ia5->data, gen->d.ia5->length + 1);
|
goto end;
|
}
|
|
len = gen->d.ia5->length + 1;
|
*altname = racoon_malloc(len);
|
if (!*altname)
|
goto end;
|
|
strlcpy(*altname, (char *) gen->d.ia5->data, len);
|
*type = gen->type;
|
error = 0;
|
}
|
/* read IP address */
|
else if (gen->type == GEN_IPADD)
|
{
|
unsigned char p[5], *ip;
|
ip = p;
|
|
/* only support IPv4 */
|
if (gen->d.ip->length != 4)
|
goto end;
|
|
/* convert Octet String to String
|
* XXX ???????
|
*/
|
/*i2d_ASN1_OCTET_STRING(gen->d.ip,&ip);*/
|
ip = gen->d.ip->data;
|
|
/* XXX Magic, enough for an IPv4 address
|
*/
|
*altname = racoon_malloc(20);
|
if (!*altname)
|
goto end;
|
|
sprintf(*altname, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
|
*type = gen->type;
|
error = 0;
|
}
|
/* XXX other possible types ?
|
* For now, error will be -1 if unsupported type
|
*/
|
|
end:
|
if (error) {
|
if (*altname) {
|
racoon_free(*altname);
|
*altname = NULL;
|
}
|
plog(LLV_ERROR, LOCATION, NULL, "%s\n", eay_strerror());
|
}
|
if (x509)
|
X509_free(x509);
|
if (gens)
|
/* free the whole stack. */
|
sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
|
|
return error;
|
}
|
|
|
/*
|
* decode a X509 certificate and make a readable text terminated '\n'.
|
* return the buffer allocated, so must free it later.
|
*/
|
char *
|
eay_get_x509text(cert)
|
vchar_t *cert;
|
{
|
X509 *x509 = NULL;
|
BIO *bio = NULL;
|
char *text = NULL;
|
u_char *bp = NULL;
|
int len = 0;
|
int error = -1;
|
|
x509 = mem2x509(cert);
|
if (x509 == NULL)
|
goto end;
|
|
bio = BIO_new(BIO_s_mem());
|
if (bio == NULL)
|
goto end;
|
|
error = X509_print(bio, x509);
|
if (error != 1) {
|
error = -1;
|
goto end;
|
}
|
|
#if defined(ANDROID_CHANGES)
|
len = BIO_get_mem_data(bio, (char**) &bp);
|
#else
|
len = BIO_get_mem_data(bio, &bp);
|
#endif
|
text = racoon_malloc(len + 1);
|
if (text == NULL)
|
goto end;
|
memcpy(text, bp, len);
|
text[len] = '\0';
|
|
error = 0;
|
|
end:
|
if (error) {
|
if (text) {
|
racoon_free(text);
|
text = NULL;
|
}
|
plog(LLV_ERROR, LOCATION, NULL, "%s\n", eay_strerror());
|
}
|
if (bio)
|
BIO_free(bio);
|
if (x509)
|
X509_free(x509);
|
|
return text;
|
}
|
|
/* get X509 structure from buffer. */
|
static X509 *
|
mem2x509(cert)
|
vchar_t *cert;
|
{
|
X509 *x509;
|
|
#ifndef EAYDEBUG
|
{
|
u_char *bp;
|
|
bp = (unsigned char *) cert->v;
|
|
x509 = d2i_X509(NULL, (void *)&bp, cert->l);
|
}
|
#else
|
{
|
BIO *bio;
|
int len;
|
|
bio = BIO_new(BIO_s_mem());
|
if (bio == NULL)
|
return NULL;
|
len = BIO_write(bio, cert->v, cert->l);
|
if (len == -1)
|
return NULL;
|
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
BIO_free(bio);
|
}
|
#endif
|
return x509;
|
}
|
|
/*
|
* get a X509 certificate from local file.
|
* a certificate must be PEM format.
|
* Input:
|
* path to a certificate.
|
* Output:
|
* NULL if error occured
|
* other is the cert.
|
*/
|
vchar_t *
|
eay_get_x509cert(path)
|
char *path;
|
{
|
FILE *fp;
|
X509 *x509;
|
vchar_t *cert;
|
u_char *bp;
|
int len;
|
int error;
|
|
#ifdef ANDROID_CHANGES
|
if (pname) {
|
BIO *bio = BIO_from_android(path);
|
if (!bio) {
|
return NULL;
|
}
|
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
BIO_free(bio);
|
} else {
|
#endif
|
/* Read private key */
|
fp = fopen(path, "r");
|
if (fp == NULL)
|
return NULL;
|
x509 = PEM_read_X509(fp, NULL, NULL, NULL);
|
fclose (fp);
|
#ifdef ANDROID_CHANGES
|
}
|
#endif
|
|
if (x509 == NULL)
|
return NULL;
|
|
len = i2d_X509(x509, NULL);
|
cert = vmalloc(len);
|
if (cert == NULL) {
|
X509_free(x509);
|
return NULL;
|
}
|
bp = (unsigned char *) cert->v;
|
error = i2d_X509(x509, &bp);
|
X509_free(x509);
|
|
if (error == 0) {
|
vfree(cert);
|
return NULL;
|
}
|
|
return cert;
|
}
|
|
/*
|
* check a X509 signature
|
* XXX: to be get hash type from my cert ?
|
* to be handled EVP_dss().
|
* OUT: return -1 when error.
|
* 0
|
*/
|
int
|
eay_check_x509sign(source, sig, cert)
|
vchar_t *source;
|
vchar_t *sig;
|
vchar_t *cert;
|
{
|
X509 *x509;
|
u_char *bp;
|
EVP_PKEY *evp;
|
int res;
|
|
bp = (unsigned char *) cert->v;
|
|
x509 = d2i_X509(NULL, (void *)&bp, cert->l);
|
if (x509 == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL, "d2i_X509(): %s\n", eay_strerror());
|
return -1;
|
}
|
|
evp = X509_get_pubkey(x509);
|
if (! evp) {
|
plog(LLV_ERROR, LOCATION, NULL, "X509_get_pubkey(): %s\n", eay_strerror());
|
X509_free(x509);
|
return -1;
|
}
|
|
res = eay_rsa_verify(source, sig, evp->pkey.rsa);
|
|
EVP_PKEY_free(evp);
|
X509_free(x509);
|
|
return res;
|
}
|
|
/*
|
* check RSA signature
|
* OUT: return -1 when error.
|
* 0 on success
|
*/
|
int
|
eay_check_rsasign(source, sig, rsa)
|
vchar_t *source;
|
vchar_t *sig;
|
RSA *rsa;
|
{
|
return eay_rsa_verify(source, sig, rsa);
|
}
|
|
/*
|
* get PKCS#1 Private Key of PEM format from local file.
|
*/
|
vchar_t *
|
eay_get_pkcs1privkey(path)
|
char *path;
|
{
|
FILE *fp;
|
EVP_PKEY *evp = NULL;
|
vchar_t *pkey = NULL;
|
u_char *bp;
|
int pkeylen;
|
int error = -1;
|
|
#ifdef ANDROID_CHANGES
|
if (pname) {
|
BIO *bio = BIO_from_android(path);
|
if (!bio) {
|
return NULL;
|
}
|
evp = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
BIO_free(bio);
|
} else {
|
#endif
|
/* Read private key */
|
fp = fopen(path, "r");
|
if (fp == NULL)
|
return NULL;
|
|
evp = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
|
|
fclose (fp);
|
#ifdef ANDROID_CHANGES
|
}
|
#endif
|
|
if (evp == NULL)
|
return NULL;
|
|
pkeylen = i2d_PrivateKey(evp, NULL);
|
if (pkeylen == 0)
|
goto end;
|
pkey = vmalloc(pkeylen);
|
if (pkey == NULL)
|
goto end;
|
bp = (unsigned char *) pkey->v;
|
pkeylen = i2d_PrivateKey(evp, &bp);
|
if (pkeylen == 0)
|
goto end;
|
|
error = 0;
|
|
end:
|
if (evp != NULL)
|
EVP_PKEY_free(evp);
|
if (error != 0 && pkey != NULL) {
|
vfree(pkey);
|
pkey = NULL;
|
}
|
|
return pkey;
|
}
|
|
/*
|
* get PKCS#1 Public Key of PEM format from local file.
|
*/
|
vchar_t *
|
eay_get_pkcs1pubkey(path)
|
char *path;
|
{
|
FILE *fp;
|
EVP_PKEY *evp = NULL;
|
vchar_t *pkey = NULL;
|
X509 *x509 = NULL;
|
u_char *bp;
|
int pkeylen;
|
int error = -1;
|
|
/* Read private key */
|
fp = fopen(path, "r");
|
if (fp == NULL)
|
return NULL;
|
|
x509 = PEM_read_X509(fp, NULL, NULL, NULL);
|
|
fclose (fp);
|
|
if (x509 == NULL)
|
return NULL;
|
|
/* Get public key - eay */
|
evp = X509_get_pubkey(x509);
|
if (evp == NULL)
|
return NULL;
|
|
pkeylen = i2d_PublicKey(evp, NULL);
|
if (pkeylen == 0)
|
goto end;
|
pkey = vmalloc(pkeylen);
|
if (pkey == NULL)
|
goto end;
|
bp = (unsigned char *) pkey->v;
|
pkeylen = i2d_PublicKey(evp, &bp);
|
if (pkeylen == 0)
|
goto end;
|
|
error = 0;
|
end:
|
if (evp != NULL)
|
EVP_PKEY_free(evp);
|
if (error != 0 && pkey != NULL) {
|
vfree(pkey);
|
pkey = NULL;
|
}
|
|
return pkey;
|
}
|
|
vchar_t *
|
eay_get_x509sign(src, privkey)
|
vchar_t *src, *privkey;
|
{
|
EVP_PKEY *evp;
|
u_char *bp = (unsigned char *) privkey->v;
|
vchar_t *sig = NULL;
|
int len;
|
int pad = RSA_PKCS1_PADDING;
|
|
/* XXX to be handled EVP_PKEY_DSA */
|
evp = d2i_PrivateKey(EVP_PKEY_RSA, NULL, (void *)&bp, privkey->l);
|
if (evp == NULL)
|
return NULL;
|
|
sig = eay_rsa_sign(src, evp->pkey.rsa);
|
|
EVP_PKEY_free(evp);
|
|
return sig;
|
}
|
|
vchar_t *
|
eay_get_rsasign(src, rsa)
|
vchar_t *src;
|
RSA *rsa;
|
{
|
return eay_rsa_sign(src, rsa);
|
}
|
|
vchar_t *
|
eay_rsa_sign(vchar_t *src, RSA *rsa)
|
{
|
int len;
|
vchar_t *sig = NULL;
|
int pad = RSA_PKCS1_PADDING;
|
|
len = RSA_size(rsa);
|
|
sig = vmalloc(len);
|
if (sig == NULL)
|
return NULL;
|
|
len = RSA_private_encrypt(src->l, (unsigned char *) src->v,
|
(unsigned char *) sig->v, rsa, pad);
|
|
if (len == 0 || len != sig->l) {
|
vfree(sig);
|
sig = NULL;
|
}
|
|
return sig;
|
}
|
|
int
|
eay_rsa_verify(src, sig, rsa)
|
vchar_t *src, *sig;
|
RSA *rsa;
|
{
|
vchar_t *xbuf = NULL;
|
int pad = RSA_PKCS1_PADDING;
|
int len = 0;
|
int error;
|
|
len = RSA_size(rsa);
|
xbuf = vmalloc(len);
|
if (xbuf == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL, "%s\n", eay_strerror());
|
return -1;
|
}
|
|
len = RSA_public_decrypt(sig->l, (unsigned char *) sig->v,
|
(unsigned char *) xbuf->v, rsa, pad);
|
if (len == 0 || len != src->l) {
|
plog(LLV_ERROR, LOCATION, NULL, "%s\n", eay_strerror());
|
vfree(xbuf);
|
return -1;
|
}
|
|
error = memcmp(src->v, xbuf->v, src->l);
|
vfree(xbuf);
|
if (error != 0)
|
return -1;
|
|
return 0;
|
}
|
|
/*
|
* get error string
|
* MUST load ERR_load_crypto_strings() first.
|
*/
|
char *
|
eay_strerror()
|
{
|
static char ebuf[512];
|
int len = 0, n;
|
unsigned long l;
|
char buf[200];
|
const char *file, *data;
|
int line, flags;
|
unsigned long es;
|
|
#if defined(ANDROID_CHANGES)
|
es = 0;
|
#else
|
es = CRYPTO_thread_id();
|
#endif
|
|
while ((l = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0){
|
n = snprintf(ebuf + len, sizeof(ebuf) - len,
|
"%lu:%s:%s:%d:%s ",
|
es, ERR_error_string(l, buf), file, line,
|
(flags & ERR_TXT_STRING) ? data : "");
|
if (n < 0 || n >= sizeof(ebuf) - len)
|
break;
|
len += n;
|
if (sizeof(ebuf) < len)
|
break;
|
}
|
|
return ebuf;
|
}
|
|
vchar_t *
|
evp_crypt(vchar_t *data, vchar_t *key, vchar_t *iv, const EVP_CIPHER *e, int enc)
|
{
|
vchar_t *res;
|
EVP_CIPHER_CTX ctx;
|
|
if (!e)
|
return NULL;
|
|
if (data->l % EVP_CIPHER_block_size(e))
|
return NULL;
|
|
if ((res = vmalloc(data->l)) == NULL)
|
return NULL;
|
|
EVP_CIPHER_CTX_init(&ctx);
|
|
#if !defined(OPENSSL_IS_BORINGSSL)
|
switch(EVP_CIPHER_nid(e)){
|
case NID_bf_cbc:
|
case NID_bf_ecb:
|
case NID_bf_cfb64:
|
case NID_bf_ofb64:
|
case NID_cast5_cbc:
|
case NID_cast5_ecb:
|
case NID_cast5_cfb64:
|
case NID_cast5_ofb64:
|
/* XXX: can we do that also for algos with a fixed key size ?
|
*/
|
/* init context without key/iv
|
*/
|
if (!EVP_CipherInit(&ctx, e, NULL, NULL, enc))
|
{
|
OpenSSL_BUG();
|
vfree(res);
|
return NULL;
|
}
|
|
/* update key size
|
*/
|
if (!EVP_CIPHER_CTX_set_key_length(&ctx, key->l))
|
{
|
OpenSSL_BUG();
|
vfree(res);
|
return NULL;
|
}
|
|
/* finalize context init with desired key size
|
*/
|
if (!EVP_CipherInit(&ctx, NULL, (u_char *) key->v,
|
(u_char *) iv->v, enc))
|
{
|
OpenSSL_BUG();
|
vfree(res);
|
return NULL;
|
}
|
break;
|
default:
|
#endif /* OPENSSL_IS_BORINGSSL */
|
if (!EVP_CipherInit(&ctx, e, (u_char *) key->v,
|
(u_char *) iv->v, enc)) {
|
OpenSSL_BUG();
|
vfree(res);
|
return NULL;
|
}
|
#if !defined(OPENSSL_IS_BORINGSSL)
|
}
|
#endif
|
|
/* disable openssl padding */
|
EVP_CIPHER_CTX_set_padding(&ctx, 0);
|
|
if (!EVP_Cipher(&ctx, (u_char *) res->v, (u_char *) data->v, data->l)) {
|
OpenSSL_BUG();
|
vfree(res);
|
return NULL;
|
}
|
|
EVP_CIPHER_CTX_cleanup(&ctx);
|
|
return res;
|
}
|
|
int
|
evp_weakkey(vchar_t *key, const EVP_CIPHER *e)
|
{
|
return 0;
|
}
|
|
int
|
evp_keylen(int len, const EVP_CIPHER *e)
|
{
|
if (!e)
|
return -1;
|
/* EVP functions return lengths in bytes, ipsec-tools
|
* uses lengths in bits, therefore conversion is required. --AK
|
*/
|
if (len != 0 && len != (EVP_CIPHER_key_length(e) << 3))
|
return -1;
|
|
return EVP_CIPHER_key_length(e) << 3;
|
}
|
|
/*
|
* DES-CBC
|
*/
|
vchar_t *
|
eay_des_encrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, EVP_des_cbc(), 1);
|
}
|
|
vchar_t *
|
eay_des_decrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, EVP_des_cbc(), 0);
|
}
|
|
#if defined(OPENSSL_IS_BORINGSSL)
|
/* BoringSSL doesn't implement DES_is_weak_key because the concept is nonsense.
|
* Thankfully, ipsec-tools never actually uses the result of this function. */
|
static int
|
DES_is_weak_key(const DES_cblock *key)
|
{
|
return 0;
|
}
|
#endif /* OPENSSL_IS_BORINGSSL */
|
|
int
|
eay_des_weakkey(key)
|
vchar_t *key;
|
{
|
#ifdef USE_NEW_DES_API
|
return DES_is_weak_key((void *)key->v);
|
#else
|
return des_is_weak_key((void *)key->v);
|
#endif
|
}
|
|
int
|
eay_des_keylen(len)
|
int len;
|
{
|
return evp_keylen(len, EVP_des_cbc());
|
}
|
|
#ifdef HAVE_OPENSSL_IDEA_H
|
/*
|
* IDEA-CBC
|
*/
|
vchar_t *
|
eay_idea_encrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
vchar_t *res;
|
IDEA_KEY_SCHEDULE ks;
|
|
idea_set_encrypt_key((unsigned char *)key->v, &ks);
|
|
/* allocate buffer for result */
|
if ((res = vmalloc(data->l)) == NULL)
|
return NULL;
|
|
/* decryption data */
|
idea_cbc_encrypt((unsigned char *)data->v, (unsigned char *)res->v, data->l,
|
&ks, (unsigned char *)iv->v, IDEA_ENCRYPT);
|
|
return res;
|
}
|
|
vchar_t *
|
eay_idea_decrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
vchar_t *res;
|
IDEA_KEY_SCHEDULE ks, dks;
|
|
idea_set_encrypt_key((unsigned char *)key->v, &ks);
|
idea_set_decrypt_key(&ks, &dks);
|
|
/* allocate buffer for result */
|
if ((res = vmalloc(data->l)) == NULL)
|
return NULL;
|
|
/* decryption data */
|
idea_cbc_encrypt((unsigned char *)data->v, (unsigned char *)res->v, data->l,
|
&dks, (unsigned char *)iv->v, IDEA_DECRYPT);
|
|
return res;
|
}
|
|
int
|
eay_idea_weakkey(key)
|
vchar_t *key;
|
{
|
return 0; /* XXX */
|
}
|
|
int
|
eay_idea_keylen(len)
|
int len;
|
{
|
if (len != 0 && len != 128)
|
return -1;
|
return 128;
|
}
|
#endif
|
|
/*
|
* BLOWFISH-CBC
|
*/
|
vchar_t *
|
eay_bf_encrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, EVP_bf_cbc(), 1);
|
}
|
|
vchar_t *
|
eay_bf_decrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, EVP_bf_cbc(), 0);
|
}
|
|
int
|
eay_bf_weakkey(key)
|
vchar_t *key;
|
{
|
return 0; /* XXX to be done. refer to RFC 2451 */
|
}
|
|
int
|
eay_bf_keylen(len)
|
int len;
|
{
|
if (len == 0)
|
return 448;
|
if (len < 40 || len > 448)
|
return -1;
|
return len;
|
}
|
|
#ifdef HAVE_OPENSSL_RC5_H
|
/*
|
* RC5-CBC
|
*/
|
vchar_t *
|
eay_rc5_encrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
vchar_t *res;
|
RC5_32_KEY ks;
|
|
/* in RFC 2451, there is information about the number of round. */
|
RC5_32_set_key(&ks, key->l, (unsigned char *)key->v, 16);
|
|
/* allocate buffer for result */
|
if ((res = vmalloc(data->l)) == NULL)
|
return NULL;
|
|
/* decryption data */
|
RC5_32_cbc_encrypt((unsigned char *)data->v, (unsigned char *)res->v, data->l,
|
&ks, (unsigned char *)iv->v, RC5_ENCRYPT);
|
|
return res;
|
}
|
|
vchar_t *
|
eay_rc5_decrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
vchar_t *res;
|
RC5_32_KEY ks;
|
|
/* in RFC 2451, there is information about the number of round. */
|
RC5_32_set_key(&ks, key->l, (unsigned char *)key->v, 16);
|
|
/* allocate buffer for result */
|
if ((res = vmalloc(data->l)) == NULL)
|
return NULL;
|
|
/* decryption data */
|
RC5_32_cbc_encrypt((unsigned char *)data->v, (unsigned char *)res->v, data->l,
|
&ks, (unsigned char *)iv->v, RC5_DECRYPT);
|
|
return res;
|
}
|
|
int
|
eay_rc5_weakkey(key)
|
vchar_t *key;
|
{
|
return 0; /* No known weak keys when used with 16 rounds. */
|
|
}
|
|
int
|
eay_rc5_keylen(len)
|
int len;
|
{
|
if (len == 0)
|
return 128;
|
if (len < 40 || len > 2040)
|
return -1;
|
return len;
|
}
|
#endif
|
|
/*
|
* 3DES-CBC
|
*/
|
vchar_t *
|
eay_3des_encrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, EVP_des_ede3_cbc(), 1);
|
}
|
|
vchar_t *
|
eay_3des_decrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, EVP_des_ede3_cbc(), 0);
|
}
|
|
int
|
eay_3des_weakkey(key)
|
vchar_t *key;
|
{
|
#ifdef USE_NEW_DES_API
|
return (DES_is_weak_key((void *)key->v) ||
|
DES_is_weak_key((void *)(key->v + 8)) ||
|
DES_is_weak_key((void *)(key->v + 16)));
|
#else
|
if (key->l < 24)
|
return 0;
|
|
return (des_is_weak_key((void *)key->v) ||
|
des_is_weak_key((void *)(key->v + 8)) ||
|
des_is_weak_key((void *)(key->v + 16)));
|
#endif
|
}
|
|
int
|
eay_3des_keylen(len)
|
int len;
|
{
|
if (len != 0 && len != 192)
|
return -1;
|
return 192;
|
}
|
|
/*
|
* CAST-CBC
|
*/
|
vchar_t *
|
eay_cast_encrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, EVP_cast5_cbc(), 1);
|
}
|
|
vchar_t *
|
eay_cast_decrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, EVP_cast5_cbc(), 0);
|
}
|
|
int
|
eay_cast_weakkey(key)
|
vchar_t *key;
|
{
|
return 0; /* No known weak keys. */
|
}
|
|
int
|
eay_cast_keylen(len)
|
int len;
|
{
|
if (len == 0)
|
return 128;
|
if (len < 40 || len > 128)
|
return -1;
|
return len;
|
}
|
|
/*
|
* AES(RIJNDAEL)-CBC
|
*/
|
#ifndef HAVE_OPENSSL_AES_H
|
vchar_t *
|
eay_aes_encrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
vchar_t *res;
|
keyInstance k;
|
cipherInstance c;
|
|
memset(&k, 0, sizeof(k));
|
if (rijndael_makeKey(&k, DIR_ENCRYPT, key->l << 3, key->v) < 0)
|
return NULL;
|
|
/* allocate buffer for result */
|
if ((res = vmalloc(data->l)) == NULL)
|
return NULL;
|
|
/* encryption data */
|
memset(&c, 0, sizeof(c));
|
if (rijndael_cipherInit(&c, MODE_CBC, iv->v) < 0){
|
vfree(res);
|
return NULL;
|
}
|
if (rijndael_blockEncrypt(&c, &k, data->v, data->l << 3, res->v) < 0){
|
vfree(res);
|
return NULL;
|
}
|
|
return res;
|
}
|
|
vchar_t *
|
eay_aes_decrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
vchar_t *res;
|
keyInstance k;
|
cipherInstance c;
|
|
memset(&k, 0, sizeof(k));
|
if (rijndael_makeKey(&k, DIR_DECRYPT, key->l << 3, key->v) < 0)
|
return NULL;
|
|
/* allocate buffer for result */
|
if ((res = vmalloc(data->l)) == NULL)
|
return NULL;
|
|
/* decryption data */
|
memset(&c, 0, sizeof(c));
|
if (rijndael_cipherInit(&c, MODE_CBC, iv->v) < 0){
|
vfree(res);
|
return NULL;
|
}
|
if (rijndael_blockDecrypt(&c, &k, data->v, data->l << 3, res->v) < 0){
|
vfree(res);
|
return NULL;
|
}
|
|
return res;
|
}
|
#else
|
static inline const EVP_CIPHER *
|
aes_evp_by_keylen(int keylen)
|
{
|
switch(keylen) {
|
case 16:
|
case 128:
|
return EVP_aes_128_cbc();
|
#if !defined(ANDROID_CHANGES)
|
case 24:
|
case 192:
|
return EVP_aes_192_cbc();
|
#endif
|
case 32:
|
case 256:
|
return EVP_aes_256_cbc();
|
default:
|
return NULL;
|
}
|
}
|
|
vchar_t *
|
eay_aes_encrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, aes_evp_by_keylen(key->l), 1);
|
}
|
|
vchar_t *
|
eay_aes_decrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, aes_evp_by_keylen(key->l), 0);
|
}
|
#endif
|
|
int
|
eay_aes_weakkey(key)
|
vchar_t *key;
|
{
|
return 0;
|
}
|
|
int
|
eay_aes_keylen(len)
|
int len;
|
{
|
if (len == 0)
|
return 128;
|
if (len != 128 && len != 192 && len != 256)
|
return -1;
|
return len;
|
}
|
|
#if defined(HAVE_OPENSSL_CAMELLIA_H)
|
/*
|
* CAMELLIA-CBC
|
*/
|
static inline const EVP_CIPHER *
|
camellia_evp_by_keylen(int keylen)
|
{
|
switch(keylen) {
|
case 16:
|
case 128:
|
return EVP_camellia_128_cbc();
|
case 24:
|
case 192:
|
return EVP_camellia_192_cbc();
|
case 32:
|
case 256:
|
return EVP_camellia_256_cbc();
|
default:
|
return NULL;
|
}
|
}
|
|
vchar_t *
|
eay_camellia_encrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, camellia_evp_by_keylen(key->l), 1);
|
}
|
|
vchar_t *
|
eay_camellia_decrypt(data, key, iv)
|
vchar_t *data, *key, *iv;
|
{
|
return evp_crypt(data, key, iv, camellia_evp_by_keylen(key->l), 0);
|
}
|
|
int
|
eay_camellia_weakkey(key)
|
vchar_t *key;
|
{
|
return 0;
|
}
|
|
int
|
eay_camellia_keylen(len)
|
int len;
|
{
|
if (len == 0)
|
return 128;
|
if (len != 128 && len != 192 && len != 256)
|
return -1;
|
return len;
|
}
|
|
#endif
|
|
/* for ipsec part */
|
int
|
eay_null_hashlen()
|
{
|
return 0;
|
}
|
|
int
|
eay_kpdk_hashlen()
|
{
|
return 0;
|
}
|
|
int
|
eay_twofish_keylen(len)
|
int len;
|
{
|
if (len < 0 || len > 256)
|
return -1;
|
return len;
|
}
|
|
int
|
eay_null_keylen(len)
|
int len;
|
{
|
return 0;
|
}
|
|
/*
|
* HMAC functions
|
*/
|
static caddr_t
|
eay_hmac_init(key, md)
|
vchar_t *key;
|
const EVP_MD *md;
|
{
|
HMAC_CTX *c = racoon_malloc(sizeof(*c));
|
|
HMAC_Init(c, key->v, key->l, md);
|
|
return (caddr_t)c;
|
}
|
|
#ifdef WITH_SHA2
|
/*
|
* HMAC SHA2-512
|
*/
|
vchar_t *
|
eay_hmacsha2_512_one(key, data)
|
vchar_t *key, *data;
|
{
|
vchar_t *res;
|
caddr_t ctx;
|
|
ctx = eay_hmacsha2_512_init(key);
|
eay_hmacsha2_512_update(ctx, data);
|
res = eay_hmacsha2_512_final(ctx);
|
|
return(res);
|
}
|
|
caddr_t
|
eay_hmacsha2_512_init(key)
|
vchar_t *key;
|
{
|
return eay_hmac_init(key, EVP_sha2_512());
|
}
|
|
void
|
eay_hmacsha2_512_update(c, data)
|
caddr_t c;
|
vchar_t *data;
|
{
|
HMAC_Update((HMAC_CTX *)c, (unsigned char *) data->v, data->l);
|
}
|
|
vchar_t *
|
eay_hmacsha2_512_final(c)
|
caddr_t c;
|
{
|
vchar_t *res;
|
unsigned int l;
|
|
if ((res = vmalloc(SHA512_DIGEST_LENGTH)) == 0)
|
return NULL;
|
|
HMAC_Final((HMAC_CTX *)c, (unsigned char *) res->v, &l);
|
res->l = l;
|
HMAC_cleanup((HMAC_CTX *)c);
|
(void)racoon_free(c);
|
|
if (SHA512_DIGEST_LENGTH != res->l) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"hmac sha2_512 length mismatch %zd.\n", res->l);
|
vfree(res);
|
return NULL;
|
}
|
|
return(res);
|
}
|
|
/*
|
* HMAC SHA2-384
|
*/
|
vchar_t *
|
eay_hmacsha2_384_one(key, data)
|
vchar_t *key, *data;
|
{
|
vchar_t *res;
|
caddr_t ctx;
|
|
ctx = eay_hmacsha2_384_init(key);
|
eay_hmacsha2_384_update(ctx, data);
|
res = eay_hmacsha2_384_final(ctx);
|
|
return(res);
|
}
|
|
caddr_t
|
eay_hmacsha2_384_init(key)
|
vchar_t *key;
|
{
|
return eay_hmac_init(key, EVP_sha2_384());
|
}
|
|
void
|
eay_hmacsha2_384_update(c, data)
|
caddr_t c;
|
vchar_t *data;
|
{
|
HMAC_Update((HMAC_CTX *)c, (unsigned char *) data->v, data->l);
|
}
|
|
vchar_t *
|
eay_hmacsha2_384_final(c)
|
caddr_t c;
|
{
|
vchar_t *res;
|
unsigned int l;
|
|
if ((res = vmalloc(SHA384_DIGEST_LENGTH)) == 0)
|
return NULL;
|
|
HMAC_Final((HMAC_CTX *)c, (unsigned char *) res->v, &l);
|
res->l = l;
|
HMAC_cleanup((HMAC_CTX *)c);
|
(void)racoon_free(c);
|
|
if (SHA384_DIGEST_LENGTH != res->l) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"hmac sha2_384 length mismatch %zd.\n", res->l);
|
vfree(res);
|
return NULL;
|
}
|
|
return(res);
|
}
|
|
/*
|
* HMAC SHA2-256
|
*/
|
vchar_t *
|
eay_hmacsha2_256_one(key, data)
|
vchar_t *key, *data;
|
{
|
vchar_t *res;
|
caddr_t ctx;
|
|
ctx = eay_hmacsha2_256_init(key);
|
eay_hmacsha2_256_update(ctx, data);
|
res = eay_hmacsha2_256_final(ctx);
|
|
return(res);
|
}
|
|
caddr_t
|
eay_hmacsha2_256_init(key)
|
vchar_t *key;
|
{
|
return eay_hmac_init(key, EVP_sha2_256());
|
}
|
|
void
|
eay_hmacsha2_256_update(c, data)
|
caddr_t c;
|
vchar_t *data;
|
{
|
HMAC_Update((HMAC_CTX *)c, (unsigned char *) data->v, data->l);
|
}
|
|
vchar_t *
|
eay_hmacsha2_256_final(c)
|
caddr_t c;
|
{
|
vchar_t *res;
|
unsigned int l;
|
|
if ((res = vmalloc(SHA256_DIGEST_LENGTH)) == 0)
|
return NULL;
|
|
HMAC_Final((HMAC_CTX *)c, (unsigned char *) res->v, &l);
|
res->l = l;
|
HMAC_cleanup((HMAC_CTX *)c);
|
(void)racoon_free(c);
|
|
if (SHA256_DIGEST_LENGTH != res->l) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"hmac sha2_256 length mismatch %zd.\n", res->l);
|
vfree(res);
|
return NULL;
|
}
|
|
return(res);
|
}
|
#endif /* WITH_SHA2 */
|
|
/*
|
* HMAC SHA1
|
*/
|
vchar_t *
|
eay_hmacsha1_one(key, data)
|
vchar_t *key, *data;
|
{
|
vchar_t *res;
|
caddr_t ctx;
|
|
ctx = eay_hmacsha1_init(key);
|
eay_hmacsha1_update(ctx, data);
|
res = eay_hmacsha1_final(ctx);
|
|
return(res);
|
}
|
|
caddr_t
|
eay_hmacsha1_init(key)
|
vchar_t *key;
|
{
|
return eay_hmac_init(key, EVP_sha1());
|
}
|
|
void
|
eay_hmacsha1_update(c, data)
|
caddr_t c;
|
vchar_t *data;
|
{
|
HMAC_Update((HMAC_CTX *)c, (unsigned char *) data->v, data->l);
|
}
|
|
vchar_t *
|
eay_hmacsha1_final(c)
|
caddr_t c;
|
{
|
vchar_t *res;
|
unsigned int l;
|
|
if ((res = vmalloc(SHA_DIGEST_LENGTH)) == 0)
|
return NULL;
|
|
HMAC_Final((HMAC_CTX *)c, (unsigned char *) res->v, &l);
|
res->l = l;
|
HMAC_cleanup((HMAC_CTX *)c);
|
(void)racoon_free(c);
|
|
if (SHA_DIGEST_LENGTH != res->l) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"hmac sha1 length mismatch %zd.\n", res->l);
|
vfree(res);
|
return NULL;
|
}
|
|
return(res);
|
}
|
|
/*
|
* HMAC MD5
|
*/
|
vchar_t *
|
eay_hmacmd5_one(key, data)
|
vchar_t *key, *data;
|
{
|
vchar_t *res;
|
caddr_t ctx;
|
|
ctx = eay_hmacmd5_init(key);
|
eay_hmacmd5_update(ctx, data);
|
res = eay_hmacmd5_final(ctx);
|
|
return(res);
|
}
|
|
caddr_t
|
eay_hmacmd5_init(key)
|
vchar_t *key;
|
{
|
return eay_hmac_init(key, EVP_md5());
|
}
|
|
void
|
eay_hmacmd5_update(c, data)
|
caddr_t c;
|
vchar_t *data;
|
{
|
HMAC_Update((HMAC_CTX *)c, (unsigned char *) data->v, data->l);
|
}
|
|
vchar_t *
|
eay_hmacmd5_final(c)
|
caddr_t c;
|
{
|
vchar_t *res;
|
unsigned int l;
|
|
if ((res = vmalloc(MD5_DIGEST_LENGTH)) == 0)
|
return NULL;
|
|
HMAC_Final((HMAC_CTX *)c, (unsigned char *) res->v, &l);
|
res->l = l;
|
HMAC_cleanup((HMAC_CTX *)c);
|
(void)racoon_free(c);
|
|
if (MD5_DIGEST_LENGTH != res->l) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"hmac md5 length mismatch %zd.\n", res->l);
|
vfree(res);
|
return NULL;
|
}
|
|
return(res);
|
}
|
|
#ifdef WITH_SHA2
|
/*
|
* SHA2-512 functions
|
*/
|
caddr_t
|
eay_sha2_512_init()
|
{
|
SHA512_CTX *c = racoon_malloc(sizeof(*c));
|
|
SHA512_Init(c);
|
|
return((caddr_t)c);
|
}
|
|
void
|
eay_sha2_512_update(c, data)
|
caddr_t c;
|
vchar_t *data;
|
{
|
SHA512_Update((SHA512_CTX *)c, (unsigned char *) data->v, data->l);
|
|
return;
|
}
|
|
vchar_t *
|
eay_sha2_512_final(c)
|
caddr_t c;
|
{
|
vchar_t *res;
|
|
if ((res = vmalloc(SHA512_DIGEST_LENGTH)) == 0)
|
return(0);
|
|
SHA512_Final((unsigned char *) res->v, (SHA512_CTX *)c);
|
(void)racoon_free(c);
|
|
return(res);
|
}
|
|
vchar_t *
|
eay_sha2_512_one(data)
|
vchar_t *data;
|
{
|
caddr_t ctx;
|
vchar_t *res;
|
|
ctx = eay_sha2_512_init();
|
eay_sha2_512_update(ctx, data);
|
res = eay_sha2_512_final(ctx);
|
|
return(res);
|
}
|
|
int
|
eay_sha2_512_hashlen()
|
{
|
return SHA512_DIGEST_LENGTH << 3;
|
}
|
#endif
|
|
#ifdef WITH_SHA2
|
/*
|
* SHA2-384 functions
|
*/
|
caddr_t
|
eay_sha2_384_init()
|
{
|
SHA384_CTX *c = racoon_malloc(sizeof(*c));
|
|
SHA384_Init(c);
|
|
return((caddr_t)c);
|
}
|
|
void
|
eay_sha2_384_update(c, data)
|
caddr_t c;
|
vchar_t *data;
|
{
|
SHA384_Update((SHA384_CTX *)c, (unsigned char *) data->v, data->l);
|
|
return;
|
}
|
|
vchar_t *
|
eay_sha2_384_final(c)
|
caddr_t c;
|
{
|
vchar_t *res;
|
|
if ((res = vmalloc(SHA384_DIGEST_LENGTH)) == 0)
|
return(0);
|
|
SHA384_Final((unsigned char *) res->v, (SHA384_CTX *)c);
|
(void)racoon_free(c);
|
|
return(res);
|
}
|
|
vchar_t *
|
eay_sha2_384_one(data)
|
vchar_t *data;
|
{
|
caddr_t ctx;
|
vchar_t *res;
|
|
ctx = eay_sha2_384_init();
|
eay_sha2_384_update(ctx, data);
|
res = eay_sha2_384_final(ctx);
|
|
return(res);
|
}
|
|
int
|
eay_sha2_384_hashlen()
|
{
|
return SHA384_DIGEST_LENGTH << 3;
|
}
|
#endif
|
|
#ifdef WITH_SHA2
|
/*
|
* SHA2-256 functions
|
*/
|
caddr_t
|
eay_sha2_256_init()
|
{
|
SHA256_CTX *c = racoon_malloc(sizeof(*c));
|
|
SHA256_Init(c);
|
|
return((caddr_t)c);
|
}
|
|
void
|
eay_sha2_256_update(c, data)
|
caddr_t c;
|
vchar_t *data;
|
{
|
SHA256_Update((SHA256_CTX *)c, (unsigned char *) data->v, data->l);
|
|
return;
|
}
|
|
vchar_t *
|
eay_sha2_256_final(c)
|
caddr_t c;
|
{
|
vchar_t *res;
|
|
if ((res = vmalloc(SHA256_DIGEST_LENGTH)) == 0)
|
return(0);
|
|
SHA256_Final((unsigned char *) res->v, (SHA256_CTX *)c);
|
(void)racoon_free(c);
|
|
return(res);
|
}
|
|
vchar_t *
|
eay_sha2_256_one(data)
|
vchar_t *data;
|
{
|
caddr_t ctx;
|
vchar_t *res;
|
|
ctx = eay_sha2_256_init();
|
eay_sha2_256_update(ctx, data);
|
res = eay_sha2_256_final(ctx);
|
|
return(res);
|
}
|
|
int
|
eay_sha2_256_hashlen()
|
{
|
return SHA256_DIGEST_LENGTH << 3;
|
}
|
#endif
|
|
/*
|
* SHA functions
|
*/
|
caddr_t
|
eay_sha1_init()
|
{
|
SHA_CTX *c = racoon_malloc(sizeof(*c));
|
|
SHA1_Init(c);
|
|
return((caddr_t)c);
|
}
|
|
void
|
eay_sha1_update(c, data)
|
caddr_t c;
|
vchar_t *data;
|
{
|
SHA1_Update((SHA_CTX *)c, data->v, data->l);
|
|
return;
|
}
|
|
vchar_t *
|
eay_sha1_final(c)
|
caddr_t c;
|
{
|
vchar_t *res;
|
|
if ((res = vmalloc(SHA_DIGEST_LENGTH)) == 0)
|
return(0);
|
|
SHA1_Final((unsigned char *) res->v, (SHA_CTX *)c);
|
(void)racoon_free(c);
|
|
return(res);
|
}
|
|
vchar_t *
|
eay_sha1_one(data)
|
vchar_t *data;
|
{
|
caddr_t ctx;
|
vchar_t *res;
|
|
ctx = eay_sha1_init();
|
eay_sha1_update(ctx, data);
|
res = eay_sha1_final(ctx);
|
|
return(res);
|
}
|
|
int
|
eay_sha1_hashlen()
|
{
|
return SHA_DIGEST_LENGTH << 3;
|
}
|
|
/*
|
* MD5 functions
|
*/
|
caddr_t
|
eay_md5_init()
|
{
|
MD5_CTX *c = racoon_malloc(sizeof(*c));
|
|
MD5_Init(c);
|
|
return((caddr_t)c);
|
}
|
|
void
|
eay_md5_update(c, data)
|
caddr_t c;
|
vchar_t *data;
|
{
|
MD5_Update((MD5_CTX *)c, data->v, data->l);
|
|
return;
|
}
|
|
vchar_t *
|
eay_md5_final(c)
|
caddr_t c;
|
{
|
vchar_t *res;
|
|
if ((res = vmalloc(MD5_DIGEST_LENGTH)) == 0)
|
return(0);
|
|
MD5_Final((unsigned char *) res->v, (MD5_CTX *)c);
|
(void)racoon_free(c);
|
|
return(res);
|
}
|
|
vchar_t *
|
eay_md5_one(data)
|
vchar_t *data;
|
{
|
caddr_t ctx;
|
vchar_t *res;
|
|
ctx = eay_md5_init();
|
eay_md5_update(ctx, data);
|
res = eay_md5_final(ctx);
|
|
return(res);
|
}
|
|
int
|
eay_md5_hashlen()
|
{
|
return MD5_DIGEST_LENGTH << 3;
|
}
|
|
/*
|
* eay_set_random
|
* size: number of bytes.
|
*/
|
vchar_t *
|
eay_set_random(size)
|
u_int32_t size;
|
{
|
BIGNUM *r = NULL;
|
vchar_t *res = 0;
|
|
if ((r = BN_new()) == NULL)
|
goto end;
|
BN_rand(r, size * 8, 0, 0);
|
eay_bn2v(&res, r);
|
|
end:
|
if (r)
|
BN_free(r);
|
return(res);
|
}
|
|
/* DH */
|
int
|
eay_dh_generate(prime, g, publen, pub, priv)
|
vchar_t *prime, **pub, **priv;
|
u_int publen;
|
u_int32_t g;
|
{
|
BIGNUM *p = NULL;
|
DH *dh = NULL;
|
int error = -1;
|
|
/* initialize */
|
/* pre-process to generate number */
|
if (eay_v2bn(&p, prime) < 0)
|
goto end;
|
|
if ((dh = DH_new()) == NULL)
|
goto end;
|
dh->p = p;
|
p = NULL; /* p is now part of dh structure */
|
dh->g = NULL;
|
if ((dh->g = BN_new()) == NULL)
|
goto end;
|
if (!BN_set_word(dh->g, g))
|
goto end;
|
|
if (publen != 0) {
|
#if defined(OPENSSL_IS_BORINGSSL)
|
dh->priv_length = publen;
|
#else
|
dh->length = publen;
|
#endif
|
}
|
|
/* generate public and private number */
|
if (!DH_generate_key(dh))
|
goto end;
|
|
/* copy results to buffers */
|
if (eay_bn2v(pub, dh->pub_key) < 0)
|
goto end;
|
if (eay_bn2v(priv, dh->priv_key) < 0) {
|
vfree(*pub);
|
goto end;
|
}
|
|
error = 0;
|
|
end:
|
if (dh != NULL)
|
DH_free(dh);
|
if (p != 0)
|
BN_free(p);
|
return(error);
|
}
|
|
int
|
eay_dh_compute(prime, g, pub, priv, pub2, key)
|
vchar_t *prime, *pub, *priv, *pub2, **key;
|
u_int32_t g;
|
{
|
BIGNUM *dh_pub = NULL;
|
DH *dh = NULL;
|
int l;
|
unsigned char *v = NULL;
|
int error = -1;
|
|
/* make public number to compute */
|
if (eay_v2bn(&dh_pub, pub2) < 0)
|
goto end;
|
|
/* make DH structure */
|
if ((dh = DH_new()) == NULL)
|
goto end;
|
if (eay_v2bn(&dh->p, prime) < 0)
|
goto end;
|
if (eay_v2bn(&dh->pub_key, pub) < 0)
|
goto end;
|
if (eay_v2bn(&dh->priv_key, priv) < 0)
|
goto end;
|
#if defined(OPENSSL_IS_BORINGSSL)
|
dh->priv_length = pub2->l * 8;
|
#else
|
dh->length = pub2->l * 8;
|
#endif
|
|
dh->g = NULL;
|
if ((dh->g = BN_new()) == NULL)
|
goto end;
|
if (!BN_set_word(dh->g, g))
|
goto end;
|
|
if ((v = racoon_calloc(prime->l, sizeof(u_char))) == NULL)
|
goto end;
|
if ((l = DH_compute_key(v, dh_pub, dh)) == -1)
|
goto end;
|
memcpy((*key)->v + (prime->l - l), v, l);
|
|
error = 0;
|
|
end:
|
if (dh_pub != NULL)
|
BN_free(dh_pub);
|
if (dh != NULL)
|
DH_free(dh);
|
if (v != NULL)
|
racoon_free(v);
|
return(error);
|
}
|
|
/*
|
* convert vchar_t <-> BIGNUM.
|
*
|
* vchar_t: unit is u_char, network endian, most significant byte first.
|
* BIGNUM: unit is BN_ULONG, each of BN_ULONG is in host endian,
|
* least significant BN_ULONG must come first.
|
*
|
* hex value of "0x3ffe050104" is represented as follows:
|
* vchar_t: 3f fe 05 01 04
|
* BIGNUM (BN_ULONG = u_int8_t): 04 01 05 fe 3f
|
* BIGNUM (BN_ULONG = u_int16_t): 0x0104 0xfe05 0x003f
|
* BIGNUM (BN_ULONG = u_int32_t_t): 0xfe050104 0x0000003f
|
*/
|
int
|
eay_v2bn(bn, var)
|
BIGNUM **bn;
|
vchar_t *var;
|
{
|
if ((*bn = BN_bin2bn((unsigned char *) var->v, var->l, NULL)) == NULL)
|
return -1;
|
|
return 0;
|
}
|
|
int
|
eay_bn2v(var, bn)
|
vchar_t **var;
|
BIGNUM *bn;
|
{
|
#if defined(ANDROID_CHANGES)
|
*var = vmalloc(BN_num_bytes(bn));
|
#else
|
*var = vmalloc(bn->top * BN_BYTES);
|
#endif
|
if (*var == NULL)
|
return(-1);
|
|
(*var)->l = BN_bn2bin(bn, (unsigned char *) (*var)->v);
|
|
return 0;
|
}
|
|
void
|
eay_init()
|
{
|
OpenSSL_add_all_algorithms();
|
ERR_load_crypto_strings();
|
#ifdef HAVE_OPENSSL_ENGINE_H
|
ENGINE_load_builtin_engines();
|
ENGINE_register_all_complete();
|
#endif
|
}
|
|
vchar_t *
|
base64_decode(char *in, long inlen)
|
{
|
#if defined(OPENSSL_IS_BORINGSSL)
|
vchar_t *res;
|
size_t decoded_size;
|
|
if (!EVP_DecodedLength(&decoded_size, inlen)) {
|
return NULL;
|
}
|
res = vmalloc(decoded_size);
|
if (res == NULL) {
|
return NULL;
|
}
|
if (!EVP_DecodeBase64((uint8_t*) res->v, &res->l, decoded_size, (uint8_t*) in, inlen)) {
|
vfree(res);
|
return NULL;
|
}
|
return res;
|
#else
|
BIO *bio=NULL, *b64=NULL;
|
vchar_t *res = NULL;
|
char *outb;
|
long outlen;
|
|
outb = malloc(inlen * 2);
|
if (outb == NULL)
|
goto out;
|
bio = BIO_new_mem_buf(in, inlen);
|
b64 = BIO_new(BIO_f_base64());
|
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
bio = BIO_push(b64, bio);
|
|
outlen = BIO_read(bio, outb, inlen * 2);
|
if (outlen <= 0) {
|
plog(LLV_ERROR, LOCATION, NULL, "%s\n", eay_strerror());
|
goto out;
|
}
|
|
res = vmalloc(outlen);
|
if (!res)
|
goto out;
|
|
memcpy(res->v, outb, outlen);
|
|
out:
|
if (outb)
|
free(outb);
|
if (bio)
|
BIO_free_all(bio);
|
|
return res;
|
#endif
|
}
|
|
vchar_t *
|
base64_encode(char *in, long inlen)
|
{
|
#if defined(OPENSSL_IS_BORINGSSL)
|
vchar_t *res;
|
size_t encoded_size;
|
|
if (!EVP_EncodedLength(&encoded_size, inlen)) {
|
return NULL;
|
}
|
res = vmalloc(encoded_size+1);
|
if (res == NULL) {
|
return NULL;
|
}
|
EVP_EncodeBlock((uint8_t*) res->v, (uint8_t*) in, inlen);
|
res->v[encoded_size] = 0;
|
return res;
|
#else
|
BIO *bio=NULL, *b64=NULL;
|
char *ptr;
|
long plen = -1;
|
vchar_t *res = NULL;
|
|
bio = BIO_new(BIO_s_mem());
|
b64 = BIO_new(BIO_f_base64());
|
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
bio = BIO_push(b64, bio);
|
|
BIO_write(bio, in, inlen);
|
BIO_flush(bio);
|
|
plen = BIO_get_mem_data(bio, &ptr);
|
res = vmalloc(plen+1);
|
if (!res)
|
goto out;
|
|
memcpy (res->v, ptr, plen);
|
res->v[plen] = '\0';
|
|
out:
|
if (bio)
|
BIO_free_all(bio);
|
|
return res;
|
#endif
|
}
|
|
static RSA *
|
binbuf_pubkey2rsa(vchar_t *binbuf)
|
{
|
BIGNUM *exp, *mod;
|
RSA *rsa_pub = NULL;
|
|
if (binbuf->v[0] > binbuf->l - 1) {
|
plog(LLV_ERROR, LOCATION, NULL, "Plain RSA pubkey format error: decoded string doesn't make sense.\n");
|
goto out;
|
}
|
|
exp = BN_bin2bn((unsigned char *) (binbuf->v + 1), binbuf->v[0], NULL);
|
mod = BN_bin2bn((unsigned char *) (binbuf->v + binbuf->v[0] + 1),
|
binbuf->l - binbuf->v[0] - 1, NULL);
|
rsa_pub = RSA_new();
|
|
if (!exp || !mod || !rsa_pub) {
|
plog(LLV_ERROR, LOCATION, NULL, "Plain RSA pubkey parsing error: %s\n", eay_strerror());
|
if (exp)
|
BN_free(exp);
|
if (mod)
|
BN_free(exp);
|
if (rsa_pub)
|
RSA_free(rsa_pub);
|
rsa_pub = NULL;
|
goto out;
|
}
|
|
rsa_pub->n = mod;
|
rsa_pub->e = exp;
|
|
out:
|
return rsa_pub;
|
}
|
|
RSA *
|
base64_pubkey2rsa(char *in)
|
{
|
BIGNUM *exp, *mod;
|
RSA *rsa_pub = NULL;
|
vchar_t *binbuf;
|
|
if (strncmp(in, "0s", 2) != 0) {
|
plog(LLV_ERROR, LOCATION, NULL, "Plain RSA pubkey format error: doesn't start with '0s'\n");
|
return NULL;
|
}
|
|
binbuf = base64_decode(in + 2, strlen(in + 2));
|
if (!binbuf) {
|
plog(LLV_ERROR, LOCATION, NULL, "Plain RSA pubkey format error: Base64 decoding failed.\n");
|
return NULL;
|
}
|
|
if (binbuf->v[0] > binbuf->l - 1) {
|
plog(LLV_ERROR, LOCATION, NULL, "Plain RSA pubkey format error: decoded string doesn't make sense.\n");
|
goto out;
|
}
|
|
rsa_pub = binbuf_pubkey2rsa(binbuf);
|
|
out:
|
if (binbuf)
|
vfree(binbuf);
|
|
return rsa_pub;
|
}
|
|
RSA *
|
bignum_pubkey2rsa(BIGNUM *in)
|
{
|
RSA *rsa_pub = NULL;
|
vchar_t *binbuf;
|
|
binbuf = vmalloc(BN_num_bytes(in));
|
if (!binbuf) {
|
plog(LLV_ERROR, LOCATION, NULL, "Plain RSA pubkey conversion: memory allocation failed..\n");
|
return NULL;
|
}
|
|
BN_bn2bin(in, (unsigned char *) binbuf->v);
|
|
rsa_pub = binbuf_pubkey2rsa(binbuf);
|
|
out:
|
if (binbuf)
|
vfree(binbuf);
|
|
return rsa_pub;
|
}
|
|
u_int32_t
|
eay_random()
|
{
|
u_int32_t result;
|
vchar_t *vrand;
|
|
vrand = eay_set_random(sizeof(result));
|
memcpy(&result, vrand->v, sizeof(result));
|
vfree(vrand);
|
|
return result;
|
}
|
|
const char *
|
eay_version()
|
{
|
#if defined(OPENSSL_IS_BORINGSSL)
|
return "(BoringSSL)";
|
#else
|
return SSLeay_version(SSLEAY_VERSION);
|
#endif
|
}
|