/* $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 #include #include #include #include #include /* get openssl/ssleay version number */ #include #if !defined(OPENSSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x0090602fL) #error OpenSSL version 0.9.6 or later required. #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_OPENSSL_ENGINE_H #include #endif #ifndef ANDROID_CHANGES #include #include #else #define EVP_bf_cbc() NULL #define EVP_cast5_cbc() NULL #include "keystore_get.h" #endif #include #ifdef HAVE_OPENSSL_RC5_H #include #endif #ifdef HAVE_OPENSSL_IDEA_H #include #endif #if defined(HAVE_OPENSSL_AES_H) #include #elif defined(HAVE_OPENSSL_RIJNDAEL_H) #include #else #include "crypto/rijndael/rijndael-api-fst.h" #endif #if defined(HAVE_OPENSSL_CAMELLIA_H) #include #endif #ifdef WITH_SHA2 #ifdef HAVE_OPENSSL_SHA2_H #include #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 /* 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; ilength; 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_keystore(char *key) { BIO *bio = NULL; char value[KEYSTORE_MESSAGE_SIZE]; int length = keystore_get(key, strlen(key), value); if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) { BIO_write(bio, value, length); } return bio; } #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); #ifndef ANDROID_CHANGES 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 */ #else if (CAfile) { BIO *bio = BIO_from_keystore(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); } #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 BIO *bio = BIO_from_keystore(path); x509 = NULL; if (bio) { x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); BIO_free(bio); } #else /* Read private key */ fp = fopen(path, "r"); if (fp == NULL) return NULL; x509 = PEM_read_X509(fp, NULL, NULL, NULL); fclose (fp); #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 BIO *bio = BIO_from_keystore(path); if (bio) { evp = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); BIO_free(bio); } #else /* Read private key */ fp = fopen(path, "r"); if (fp == NULL) return NULL; evp = PEM_read_PrivateKey(fp, NULL, NULL, NULL); fclose (fp); #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); }