#ifdef CYLINK_DSS static const char rcsid[] = "$Header: /proj/cvs/isc/bind/src/lib/dst/cylink_link.c,v 1.7 1999/10/13 16:39:22 vixie Exp $"; /* * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. * * Permission to use, copy modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. */ /* * This file contains two components * 1. Interface to the CYLINK library to allow compilation of Bind * with TIS/DNSSEC when CYLINK is not available * all calls to CYLINK are contained inside this file. * 2. The glue to connvert DSA KEYS to and from external formats */ #include "port_before.h" #include #include #include #include #include #include #include #include #include "dst_internal.h" #include #include "port_after.h" typedef struct cylinkkey { char *dk_signer; uchar *dk_p; uchar *dk_q; uchar *dk_g; uchar *dk_x; uchar *dk_y; ushort dk_p_bytes; } DSA_Key; #define NULL_PRIV_KEY(k)(k == NULL || k->dk_p == NULL || k->dk_q == NULL || \ k->dk_g == NULL || k->dk_x == NULL) #define NULL_PUB_KEY(k)(k == NULL || k->dk_p == NULL || k->dk_q == NULL || \ k->dk_g == NULL || k->dk_y == NULL) static int dst_cylink_sign(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, u_char *signature, const int sig_len); static int dst_cylink_verify(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, const u_char *signature, const int sig_len); static int dst_cylink_to_dns_key(const DST_KEY *in_key, u_char *out_str, const int out_len); static int dst_cylink_from_dns_key(DST_KEY *s_key, const u_char *key, const int len); static int dst_cylink_key_to_file_format(const DST_KEY *key, char *buff, const int buff_len); static int dst_cylink_key_from_file_format(DST_KEY *d_key, const char *buff, const int buff_len); static void *dst_cylink_free_key_structure(void *key); static int dst_cylink_generate_keypair(DST_KEY *key, int exp); static int dst_cylink_compare_keys(const DST_KEY *key1, const DST_KEY *key2); static void *memcpyend(void *dest, const void *src, size_t n, size_t size); /* * dst_cylink_init() Function to answer set up function pointers for * CYLINK related functions */ int dst_cylink_init() { if (dst_t_func[KEY_DSA] != NULL) return (1); dst_t_func[KEY_DSA] = malloc(sizeof(struct dst_func)); if (dst_t_func[KEY_DSA] == NULL) return (0); memset(dst_t_func[KEY_DSA], 0, sizeof(struct dst_func)); dst_t_func[KEY_DSA]->sign = dst_cylink_sign; dst_t_func[KEY_DSA]->verify = dst_cylink_verify; dst_t_func[KEY_DSA]->compare = dst_cylink_compare_keys; dst_t_func[KEY_DSA]->generate = dst_cylink_generate_keypair; dst_t_func[KEY_DSA]->destroy = dst_cylink_free_key_structure; dst_t_func[KEY_DSA]->from_dns_key = dst_cylink_from_dns_key; dst_t_func[KEY_DSA]->to_dns_key = dst_cylink_to_dns_key; dst_t_func[KEY_DSA]->from_file_fmt = dst_cylink_key_from_file_format; dst_t_func[KEY_DSA]->to_file_fmt = dst_cylink_key_to_file_format; SetDataOrder(1); return (1); } /* * dst_cylink_sign * Call CYLINK signing functions to sign a block of data. * There are three steps to signing, INIT (initialize structures), * UPDATE (hash (more) data), FINAL (generate a signature). This * routine performs one or more of these steps. * Parameters * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. * algobj structure holds context for a sign done in multiple calls. * context the context to use for this computation * data data to be signed. * len length in bytes of data. * priv_key key to use for signing. * signature location to store signature. * sig_len size in bytes of signature field. * returns * N Success on SIG_MODE_FINAL = returns signature length in bytes * N is 41 for DNS * 0 Success on SIG_MODE_INIT and UPDATE * <0 Failure */ static int dst_cylink_sign(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, u_char *signature, const int sig_len) { int sign_len = 0; int status; SHA_context *ctx = NULL; if (mode & SIG_MODE_INIT) ctx = (SHA_context *) malloc(sizeof(SHA_context)); else if (context) ctx = (SHA_context *) *context; if (ctx == NULL) return (-1); if (mode & SIG_MODE_INIT) SHAInit(ctx); if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) { status = SHAUpdate(ctx, (u_char *) data, len); if (status != SUCCESS) return (SIGN_UPDATE_FAILURE); } if (mode & SIG_MODE_FINAL) { DSA_Key *key; uchar digest[SHA_LENGTH]; uchar rand[SHA_LENGTH]; uchar r[SHA_LENGTH], s[SHA_LENGTH]; if (signature == NULL || sig_len < 2 * SHA_LENGTH) return (SIGN_FINAL_FAILURE); if ((status = SHAFinal(ctx, digest)) != SUCCESS) return (SIGN_FINAL_FAILURE); SAFE_FREE(ctx); if (dkey == NULL || dkey->dk_KEY_struct == NULL) return (-1); key = (DSA_Key *) dkey->dk_KEY_struct; if (NULL_PRIV_KEY(key)) return (-2); dst_random(DST_RAND_STD, sizeof(rand), rand); status = GenDSSSignature(key->dk_p_bytes, key->dk_p, key->dk_q, key->dk_g, key->dk_x, rand, r, s, digest); if (status != SUCCESS) return (SIGN_FINAL_FAILURE); *signature = (dkey->dk_key_size - 512)/64; sign_len = 1; memcpy(signature + sign_len, r, SHA_LENGTH); sign_len += SHA_LENGTH; memcpy(signature + sign_len, s, SHA_LENGTH); sign_len += SHA_LENGTH; } else { if (context == NULL) return (-1); *context = (void *) ctx; } return (sign_len); } /* * Dst_cylink_verify * Calls CYLINK verification routines. There are three steps to * verification, INIT (initialize structures), UPDATE (hash (more) data), * FINAL (generate a signature). This routine performs one or more of * these steps. * Parameters * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. * dkey structure holds context for a verify done in multiple calls. * context algorithm specific context for the current context processing * data data signed. * len length in bytes of data. * pub_key key to use for verify. * signature signature. * sig_len length in bytes of signature. * returns * 0 Success * <0 Failure */ static int dst_cylink_verify(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, const u_char *signature, const int sig_len) { int status; SHA_context *ctx = NULL; if (mode & SIG_MODE_INIT) ctx = (SHA_context *) malloc(sizeof(SHA_context)); else if (context) ctx = (SHA_context *) *context; if (ctx == NULL) return (-1); if (mode & SIG_MODE_INIT) SHAInit(ctx); if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) { status = SHAUpdate(ctx, (u_char *) data, len); if (status != SUCCESS) return (VERIFY_UPDATE_FAILURE); } if (mode & SIG_MODE_FINAL) { DSA_Key *key; uchar digest[SHA_LENGTH]; uchar r[SHA_LENGTH], s[SHA_LENGTH]; if (dkey == NULL || dkey->dk_KEY_struct == NULL) return (-1); key = (DSA_Key *) dkey->dk_KEY_struct; if (NULL_PUB_KEY(key)) return (-2); if (signature == NULL || sig_len != (2 * SHA_LENGTH +1)) return (SIGN_FINAL_FAILURE); status = SHAFinal(ctx, digest); SAFE_FREE(ctx); if (status != SUCCESS) return (SIGN_FINAL_FAILURE); if (((int)*signature) != ((key->dk_p_bytes -64)/8)) return(VERIFY_FINAL_FAILURE); memcpy(r, signature +1, SHA_LENGTH); memcpy(s, signature + SHA_LENGTH +1, SHA_LENGTH); status = VerDSSSignature(key->dk_p_bytes, key->dk_p, key->dk_q, key->dk_g, key->dk_y, r, s, digest); if (status != SUCCESS) return (VERIFY_FINAL_FAILURE); } else { if (context == NULL) return (-1); *context = (void *) ctx; } return (0); } /* * dst_cylink_to_dns_key * Converts key from DSA to DNS distribution format * This function gets in a pointer to the public key and a work area * to write the key into. * Parameters * public KEY structure * out_str buffer to write encoded key into * out_len size of out_str * Return * N >= 0 length of encoded key * n < 0 error */ static int dst_cylink_to_dns_key(const DST_KEY *in_key, u_char *out_str, const int out_len) { u_char *op = out_str; int t; DSA_Key *key; if (in_key == NULL || in_key->dk_KEY_struct == NULL || out_len <= 0 || out_str == NULL) return (-1); key = (DSA_Key *) in_key->dk_KEY_struct; t = (key->dk_p_bytes - 64) / 8; *op++ = t; memcpy(op, key->dk_q, SHA_LENGTH); op += SHA_LENGTH; memcpy(op, key->dk_p, key->dk_p_bytes); op += key->dk_p_bytes; memcpy(op, key->dk_g, key->dk_p_bytes); op += key->dk_p_bytes; memcpy(op, key->dk_y, key->dk_p_bytes); op += key->dk_p_bytes; return (op - out_str); } /* * dst_cylink_from_dns_key * Converts from a DNS KEY RR format to an RSA KEY. * Parameters * len Length in bytes of DNS key * key DNS key * name Key name * s_key DST structure that will point to the RSA key this routine * will build. * Return * 0 The input key, s_key or name was null. * 1 Success */ static int dst_cylink_from_dns_key(DST_KEY *s_key, const u_char *key, const int len) { int t; const u_char *key_ptr = key; DSA_Key *d_key; if (s_key == NULL || len < 0 || key == NULL) return (0); if (len == 0) /* process null key */ return (1); if (key_ptr == NULL) return (0); t = (int) *key_ptr++; /* length of exponent in bytes */ if ((3 * (t * 8 + 64) + SHA_LENGTH + 1) != len) return (0); if ((d_key = (DSA_Key *) malloc(sizeof(DSA_Key))) == NULL) { EREPORT(("dst_cylink_from_dns_key(): Memory allocation error 1")); return (0); } memset(d_key, 0, sizeof(DSA_Key)); s_key->dk_KEY_struct = (void *) d_key; d_key->dk_signer = strdup(s_key->dk_key_name); d_key->dk_p_bytes = 64 + 8 * t; if ((d_key->dk_q = (uchar *) malloc(SHA_LENGTH)) == NULL) return (0); memcpy(d_key->dk_q, key_ptr, SHA_LENGTH); key_ptr += SHA_LENGTH; if ((d_key->dk_p = (uchar *) malloc(d_key->dk_p_bytes)) == NULL) return (0); memcpy(d_key->dk_p, key_ptr, d_key->dk_p_bytes); key_ptr += d_key->dk_p_bytes; if ((d_key->dk_g = (uchar *) malloc(d_key->dk_p_bytes)) == NULL) return (0); memcpy(d_key->dk_g, key_ptr, d_key->dk_p_bytes); key_ptr += d_key->dk_p_bytes; if ((d_key->dk_y = (uchar *) malloc(d_key->dk_p_bytes)) == NULL) return (0); memcpy(d_key->dk_y, key_ptr, d_key->dk_p_bytes); key_ptr += d_key->dk_p_bytes; s_key->dk_id = dst_s_id_calc(key, len); s_key->dk_key_size = d_key->dk_p_bytes * 8; return (1); } /************************************************************************** * dst_cylink_key_to_file_format * Encodes an DSA Key into the portable file format. * Parameters * key DSA KEY structure * buff output buffer * buff_len size of output buffer * Return * 0 Failure - null input rkey * -1 Failure - not enough space in output area * N Success - Length of data returned in buff */ static int dst_cylink_key_to_file_format(const DST_KEY *key, char *buff, const int buff_len) { char *bp; int len, b_len; DSA_Key *dkey; u_char num[256]; /* More than long enough for DSA keys */ if (key == NULL || key->dk_KEY_struct == NULL) /* no output */ return (0); if (buff == NULL || buff_len <= (int) strlen(key_file_fmt_str)) return (-1); /* no OR not enough space in output area */ dkey = (DSA_Key *) key->dk_KEY_struct; memset(buff, 0, buff_len); /* just in case */ /* write file header */ sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_DSA, "DSA"); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memcpy(num, dkey->dk_p, dkey->dk_p_bytes); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Prime(p): ", num, dkey->dk_p_bytes)) <= 0) return (-1); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memcpy(num, dkey->dk_q, dkey->dk_p_bytes); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Subprime(q): ", num, SHA_LENGTH)) <= 0) return (-2); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memcpy(num, dkey->dk_g, dkey->dk_p_bytes); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Base(g): ", num, dkey->dk_p_bytes)) <= 0) return (-3); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memcpy(num, dkey->dk_x, dkey->dk_p_bytes); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Private_value(x): ", num, SHA_LENGTH)) <= 0) return (-4); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memcpy(num, dkey->dk_y, dkey->dk_p_bytes); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Public_value(y): ", num, dkey->dk_p_bytes)) <= 0) return (-4); bp += len; b_len -= len; return (buff_len - b_len); } /************************************************************************** * dst_cylink_key_from_file_format * Converts contents of a private key file into a private DSA key. * Parameters * DSA_Key structure to put key into * buff buffer containing the encoded key * buff_len the length of the buffer * Return * n >= 0 Foot print of the key converted * n < 0 Error in conversion */ static int dst_cylink_key_from_file_format(DST_KEY *d_key, const char *buff, const int buff_len) { u_char s[DSS_LENGTH_MAX]; u_char dns[1024]; int len, s_len = sizeof(s); int foot = -1, dnslen; const char *p = buff; DSA_Key *dsa_key; if (d_key == NULL || buff == NULL || buff_len <= 0) return (-1); dsa_key = (DSA_Key *) malloc(sizeof(DSA_Key)); if (dsa_key == NULL) { return (-2); } memset(dsa_key, 0, sizeof(*dsa_key)); d_key->dk_KEY_struct = (void *) dsa_key; if (!dst_s_verify_str(&p, "Prime(p): ")) return (-3); memset(s, 0, s_len); if ((len = dst_s_conv_bignum_b64_to_u8(&p, s, s_len)) == 0) return (-4); dsa_key->dk_p_bytes = len; if ((dsa_key->dk_p = malloc(len)) == NULL) return (-5); memcpy(dsa_key->dk_p, s + s_len - len, len); while (*++p && p < (const char *) &buff[buff_len]) { if (dst_s_verify_str(&p, "Subprime(q): ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, s, s_len))) return (-6); if ((dsa_key->dk_q = malloc(SHA_LENGTH)) == NULL) return (-7); memcpyend(dsa_key->dk_q, s + s_len - len, len, SHA_LENGTH); } else if (dst_s_verify_str(&p, "Base(g): ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, s, s_len))) return (-8); if ((dsa_key->dk_g = malloc(dsa_key->dk_p_bytes)) == NULL) return (-9); memcpyend(dsa_key->dk_g, s + s_len - len, len, dsa_key->dk_p_bytes); } else if (dst_s_verify_str(&p, "Private_value(x): ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, s, s_len))) return (-10); if ((dsa_key->dk_x = malloc(SHA_LENGTH)) == NULL) return (-11); memcpyend(dsa_key->dk_x, s + s_len - len, len, SHA_LENGTH); } else if (dst_s_verify_str(&p, "Public_value(y): ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, s, s_len))) return (-10); if ((dsa_key->dk_y = malloc(dsa_key->dk_p_bytes)) == NULL) return (-11); memcpyend(dsa_key->dk_y, s + s_len - len, len, dsa_key->dk_p_bytes); } else { EREPORT(("Decode_DSAKey(): Bad keyword %s\n", p)); return (-12); } } /* while p */ d_key->dk_key_size = dsa_key->dk_p_bytes * 8; dnslen = d_key->dk_func->to_dns_key(d_key, dns, sizeof(dns)); foot = dst_s_id_calc(dns, dnslen); return (foot); } /************************************************************************** * dst_cylink_free_key_structure * Frees all dynamicly allocated structures in DSA_Key. */ static void * dst_cylink_free_key_structure(void *key) { DSA_Key *d_key = (DSA_Key *) key; if (d_key != NULL) { SAFE_FREE(d_key->dk_signer); SAFE_FREE(d_key->dk_p); SAFE_FREE(d_key->dk_q); SAFE_FREE(d_key->dk_g); SAFE_FREE(d_key->dk_x); SAFE_FREE(d_key->dk_y); SAFE_FREE(d_key); } return (NULL); } /************************************************************************** * dst_cylink_generate_keypair * Generates unique keys that are hard to predict. * Parameters * key generic Key structure * exp the public exponent * Return * 0 Failure * 1 Success */ static int dst_cylink_generate_keypair(DST_KEY *key, int nothing) { int status, dnslen, n; DSA_Key *dsa; u_char rand[SHA_LENGTH]; u_char dns[1024]; if (key == NULL || key->dk_alg != KEY_DSA) return (0); if ((dsa = (DSA_Key *) malloc(sizeof(DSA_Key))) == NULL) { EREPORT(("dst_cylink_generate_keypair: Memory allocation error 3")); return (0); } memset(dsa, 0, sizeof(*dsa)); dsa->dk_p_bytes = key->dk_key_size / 8; dsa->dk_p = (uchar *) malloc(dsa->dk_p_bytes); dsa->dk_q = (uchar *) malloc(SHA_LENGTH); dsa->dk_g = (uchar *) malloc(dsa->dk_p_bytes); dsa->dk_x = (uchar *) malloc(SHA_LENGTH); dsa->dk_y = (uchar *) malloc(dsa->dk_p_bytes); if (!dsa->dk_p || !dsa->dk_q || !dsa->dk_g || !dsa->dk_x || !dsa->dk_y) { EREPORT(("dst_cylink_generate_keypair: Memory allocation error 4")); return (0); } n = dst_random(DST_RAND_KEY, sizeof(rand), rand); if (n != sizeof(rand)) return (0); status = GenDSSParameters(dsa->dk_p_bytes, dsa->dk_p, dsa->dk_q, dsa->dk_g, rand, NULL); if (status != SUCCESS) return (0); status = GenDSSKey(dsa->dk_p_bytes, dsa->dk_p, dsa->dk_q, dsa->dk_g, dsa->dk_x, dsa->dk_y, rand); if (status != SUCCESS) return (0); memset(rand, 0, sizeof(rand)); key->dk_KEY_struct = (void *) dsa; dnslen = key->dk_func->to_dns_key(key, dns, sizeof(dns)); key->dk_id = dst_s_id_calc(dns, dnslen); return (1); } /* * dst_cylink_compare_keys * Compare two keys for equality. * Return * 0 The keys are equal * NON-ZERO The keys are not equal */ static int dst_cylink_compare_keys(const DST_KEY *key1, const DST_KEY *key2) { int status; DSA_Key *dkey1 = (DSA_Key *) key1->dk_KEY_struct; DSA_Key *dkey2 = (DSA_Key *) key2->dk_KEY_struct; if (dkey1 == NULL && dkey2 == NULL) return (0); else if (dkey1 == NULL) return (2); else if (dkey2 == NULL) return(1); if (dkey1->dk_p_bytes != dkey2->dk_p_bytes) return (201); status = memcmp(dkey1->dk_p, dkey2->dk_p, dkey1->dk_p_bytes) || memcmp(dkey1->dk_q, dkey2->dk_q, SHA_LENGTH) || memcmp(dkey1->dk_g, dkey2->dk_g, dkey1->dk_p_bytes) || memcmp(dkey1->dk_y, dkey2->dk_y, dkey1->dk_p_bytes); if (status) return (status); if (dkey1->dk_x || dkey2->dk_x) { if (dkey1->dk_x == NULL || dkey2->dk_x == NULL) return (202); return (memcmp(dkey1->dk_x, dkey2->dk_x, dkey1->dk_p_bytes)); } else return (0); } static void * memcpyend(void *dest, const void *src, size_t n, size_t size) { if (n < size) memset(dest, 0, size - n); memcpy((char *)dest + size - n, src, n); return dest; } #else int dst_cylink_init() { return (0); } #endif /* CYLINK */