| /* $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" |
| |
| /* |
| * 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; |
| } |
| |
| len = BIO_get_mem_data(bio, &bp); |
| 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; |
| |
| es = CRYPTO_thread_id(); |
| |
| 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); |
| |
| 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: |
| if (!EVP_CipherInit(&ctx, e, (u_char *) key->v, |
| (u_char *) iv->v, enc)) { |
| OpenSSL_BUG(); |
| vfree(res); |
| return NULL; |
| } |
| } |
| |
| /* 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); |
| } |
| |
| 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(); |
| case 24: |
| case 192: |
| return EVP_aes_192_cbc(); |
| 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) |
| dh->length = publen; |
| |
| /* 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; |
| dh->length = pub2->l * 8; |
| |
| 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; |
| { |
| *var = vmalloc(bn->top * BN_BYTES); |
| 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) |
| { |
| 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; |
| } |
| |
| vchar_t * |
| base64_encode(char *in, long inlen) |
| { |
| 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; |
| } |
| |
| 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() |
| { |
| return SSLeay_version(SSLEAY_VERSION); |
| } |