/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * This file contains DSA helper routines common to * the PKCS11 soft token code and the kernel DSA code. */ #include #include #ifdef _KERNEL #include #else #include #include #endif #include #include "dsa_impl.h" static CK_RV convert_rv(BIG_ERR_CODE err) { switch (err) { case BIG_OK: return (CKR_OK); case BIG_NO_MEM: return (CKR_HOST_MEMORY); case BIG_NO_RANDOM: return (CKR_DEVICE_ERROR); case BIG_INVALID_ARGS: return (CKR_ARGUMENTS_BAD); case BIG_DIV_BY_0: default: return (CKR_GENERAL_ERROR); } } /* size is in bits */ static BIG_ERR_CODE DSA_key_init(DSAkey *key, int size) { BIG_ERR_CODE err = BIG_OK; int len, len160; len = BITLEN2BIGNUMLEN(size); len160 = BIG_CHUNKS_FOR_160BITS; key->size = size; if ((err = big_init(&(key->q), len160)) != BIG_OK) return (err); if ((err = big_init(&(key->p), len)) != BIG_OK) goto ret1; if ((err = big_init(&(key->g), len)) != BIG_OK) goto ret2; if ((err = big_init(&(key->x), len160)) != BIG_OK) goto ret3; if ((err = big_init(&(key->y), len)) != BIG_OK) goto ret4; if ((err = big_init(&(key->k), len160)) != BIG_OK) goto ret5; if ((err = big_init(&(key->r), len160)) != BIG_OK) goto ret6; if ((err = big_init(&(key->s), len160)) != BIG_OK) goto ret7; if ((err = big_init(&(key->v), len160)) != BIG_OK) goto ret8; return (BIG_OK); ret8: big_finish(&(key->s)); ret7: big_finish(&(key->r)); ret6: big_finish(&(key->k)); ret5: big_finish(&(key->y)); ret4: big_finish(&(key->x)); ret3: big_finish(&(key->g)); ret2: big_finish(&(key->p)); ret1: big_finish(&(key->q)); return (err); } static void DSA_key_finish(DSAkey *key) { big_finish(&(key->v)); big_finish(&(key->s)); big_finish(&(key->r)); big_finish(&(key->k)); big_finish(&(key->y)); big_finish(&(key->x)); big_finish(&(key->g)); big_finish(&(key->p)); big_finish(&(key->q)); } /* * Generate DSA private x and public y from prime p, subprime q, and base g. */ static CK_RV generate_dsa_key(DSAkey *key, int (*rfunc)(void *, size_t)) { BIG_ERR_CODE err; int (*rf)(void *, size_t); rf = rfunc; if (rf == NULL) { #ifdef _KERNEL rf = random_get_pseudo_bytes; #else rf = pkcs11_get_urandom; #endif } do { if ((err = big_random(&(key->x), DSA_SUBPRIME_BITS, rf)) != BIG_OK) { return (convert_rv(err)); } } while (big_cmp_abs(&(key->x), &(key->q)) > 0); if ((err = big_modexp(&(key->y), &(key->g), (&key->x), (&key->p), NULL)) != BIG_OK) return (convert_rv(err)); return (CKR_OK); } CK_RV dsa_genkey_pair(DSAbytekey *bkey) { CK_RV rv = CKR_OK; BIG_ERR_CODE brv; DSAkey dsakey; uint32_t prime_bytes; uint32_t subprime_bytes; prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits); if ((prime_bytes < MIN_DSA_KEY_LEN) || (prime_bytes > MAX_DSA_KEY_LEN)) { return (CKR_ATTRIBUTE_VALUE_INVALID); } /* * There is no check here that prime_bits must be a multiple of 64, * and thus that prime_bytes must be a multiple of 8. */ subprime_bytes = CRYPTO_BITS2BYTES(bkey->subprime_bits); if (subprime_bytes != DSA_SUBPRIME_BYTES) { return (CKR_ATTRIBUTE_VALUE_INVALID); } if (bkey->public_y == NULL || bkey->private_x == NULL) { return (CKR_ARGUMENTS_BAD); } /* * Initialize the DSA key. * Note: big_extend takes length in words. */ if ((brv = DSA_key_init(&dsakey, bkey->prime_bits)) != BIG_OK) { rv = convert_rv(brv); goto cleanexit; } /* Convert prime p to bignum. */ if ((brv = big_extend(&(dsakey.p), CHARLEN2BIGNUMLEN(prime_bytes))) != BIG_OK) { rv = convert_rv(brv); goto cleanexit; } bytestring2bignum(&(dsakey.p), bkey->prime, prime_bytes); /* Convert prime q to bignum. */ if ((brv = big_extend(&(dsakey.q), CHARLEN2BIGNUMLEN(subprime_bytes))) != BIG_OK) { rv = convert_rv(brv); goto cleanexit; } bytestring2bignum(&(dsakey.q), bkey->subprime, subprime_bytes); /* Convert base g to bignum. */ if ((brv = big_extend(&(dsakey.g), CHARLEN2BIGNUMLEN(bkey->base_bytes))) != BIG_OK) { rv = convert_rv(brv); goto cleanexit; } bytestring2bignum(&(dsakey.g), bkey->base, bkey->base_bytes); /* * Generate DSA key pair. * Note: bignum.len is length of value in words. */ if ((rv = generate_dsa_key(&dsakey, bkey->rfunc)) != CKR_OK) { goto cleanexit; } bkey->public_y_bits = CRYPTO_BYTES2BITS(prime_bytes); bignum2bytestring(bkey->public_y, &(dsakey.y), prime_bytes); bkey->private_x_bits = CRYPTO_BYTES2BITS(DSA_SUBPRIME_BYTES); bignum2bytestring(bkey->private_x, &(dsakey.x), DSA_SUBPRIME_BYTES); cleanexit: DSA_key_finish(&dsakey); return (rv); } /* * DSA sign operation */ CK_RV dsa_sign(DSAbytekey *bkey, uchar_t *in, uint32_t inlen, uchar_t *out) { CK_RV rv = CKR_OK; BIG_ERR_CODE brv; DSAkey dsakey; BIGNUM msg, tmp, tmp1; uint32_t prime_bytes; uint32_t subprime_bytes; uint32_t value_bytes; int (*rf)(void *, size_t); prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits); subprime_bytes = CRYPTO_BITS2BYTES(bkey->subprime_bits); if (DSA_SUBPRIME_BYTES != subprime_bytes) { return (CKR_KEY_SIZE_RANGE); } value_bytes = CRYPTO_BITS2BYTES(bkey->private_x_bits); /* len of x */ if (DSA_SUBPRIME_BYTES < value_bytes) { return (CKR_KEY_SIZE_RANGE); } /* * Initialize the DH key. * Note: big_extend takes length in words. */ if ((brv = DSA_key_init(&dsakey, bkey->prime_bits)) != BIG_OK) { return (CKR_HOST_MEMORY); } if ((brv = big_extend(&(dsakey.p), CHARLEN2BIGNUMLEN(prime_bytes))) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&(dsakey.p), bkey->prime, prime_bytes); if ((brv = big_extend(&(dsakey.q), CHARLEN2BIGNUMLEN(subprime_bytes))) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&(dsakey.q), bkey->subprime, subprime_bytes); if ((brv = big_extend(&(dsakey.g), CHARLEN2BIGNUMLEN(bkey->base_bytes))) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&(dsakey.g), bkey->base, bkey->base_bytes); if ((brv = big_extend(&(dsakey.x), CHARLEN2BIGNUMLEN(value_bytes))) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&(dsakey.x), bkey->private_x, value_bytes); if ((brv = big_init(&msg, BIG_CHUNKS_FOR_160BITS)) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&msg, in, inlen); /* * Compute signature. */ if ((brv = big_init(&tmp, CHARLEN2BIGNUMLEN(prime_bytes) + 2 * BIG_CHUNKS_FOR_160BITS + 1)) != BIG_OK) { rv = convert_rv(brv); goto clean2; } if ((brv = big_init(&tmp1, 2 * BIG_CHUNKS_FOR_160BITS + 1)) != BIG_OK) { rv = convert_rv(brv); goto clean3; } rf = bkey->rfunc; if (rf == NULL) { #ifdef _KERNEL rf = random_get_pseudo_bytes; #else rf = pkcs11_get_urandom; #endif } if ((brv = big_random(&(dsakey.k), DSA_SUBPRIME_BITS, rf)) != BIG_OK) { rv = convert_rv(brv); goto clean4; } if ((brv = big_div_pos(NULL, &(dsakey.k), &(dsakey.k), &(dsakey.q))) != BIG_OK) { rv = convert_rv(brv); goto clean4; } if ((brv = big_modexp(&tmp, &(dsakey.g), &(dsakey.k), &(dsakey.p), NULL)) != BIG_OK) { rv = convert_rv(brv); goto clean4; } if ((brv = big_div_pos(NULL, &(dsakey.r), &tmp, &(dsakey.q))) != BIG_OK) { rv = convert_rv(brv); goto clean4; } if ((brv = big_ext_gcd_pos(NULL, NULL, &tmp, &(dsakey.q), &(dsakey.k))) != BIG_OK) { rv = convert_rv(brv); goto clean4; } if (tmp.sign == -1) if ((brv = big_add(&tmp, &tmp, &(dsakey.q))) != BIG_OK) { rv = convert_rv(brv); goto clean4; /* tmp <- k^-1 */ } if ((brv = big_mul(&tmp1, &(dsakey.x), &(dsakey.r))) != BIG_OK) { rv = convert_rv(brv); goto clean4; } if ((brv = big_add(&tmp1, &tmp1, &msg)) != BIG_OK) { rv = convert_rv(brv); goto clean4; } if ((brv = big_mul(&tmp, &tmp1, &tmp)) != BIG_OK) { rv = convert_rv(brv); goto clean4; } if ((brv = big_div_pos(NULL, &(dsakey.s), &tmp, &(dsakey.q))) != BIG_OK) { rv = convert_rv(brv); goto clean4; } /* * Signature is in DSA key r and s values, copy to out */ bignum2bytestring(out, &(dsakey.r), DSA_SUBPRIME_BYTES); bignum2bytestring(out + DSA_SUBPRIME_BYTES, &(dsakey.s), DSA_SUBPRIME_BYTES); clean4: big_finish(&tmp1); clean3: big_finish(&tmp); clean2: big_finish(&msg); clean1: DSA_key_finish(&dsakey); return (rv); } /* * DSA verify operation */ CK_RV dsa_verify(DSAbytekey *bkey, uchar_t *data, uchar_t *sig) { CK_RV rv = CKR_OK; BIG_ERR_CODE brv; DSAkey dsakey; BIGNUM msg, tmp1, tmp2, tmp3; uint32_t prime_bytes; uint32_t subprime_bytes; uint32_t value_bytes; prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits); subprime_bytes = CRYPTO_BITS2BYTES(bkey->subprime_bits); if (DSA_SUBPRIME_BYTES != subprime_bytes) { return (CKR_KEY_SIZE_RANGE); } if (prime_bytes < bkey->base_bytes) { return (CKR_KEY_SIZE_RANGE); } value_bytes = CRYPTO_BITS2BYTES(bkey->public_y_bits); /* len of y */ if (prime_bytes < value_bytes) { return (CKR_KEY_SIZE_RANGE); } /* * Initialize the DSA key. * Note: big_extend takes length in words. */ if (DSA_key_init(&dsakey, bkey->prime_bits) != BIG_OK) { return (CKR_HOST_MEMORY); } if ((brv = big_extend(&(dsakey.p), CHARLEN2BIGNUMLEN(prime_bytes))) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&(dsakey.p), bkey->prime, prime_bytes); if ((brv = big_extend(&(dsakey.q), CHARLEN2BIGNUMLEN(subprime_bytes))) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&(dsakey.q), bkey->subprime, subprime_bytes); if ((brv = big_extend(&(dsakey.g), CHARLEN2BIGNUMLEN(bkey->base_bytes))) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&(dsakey.g), bkey->base, bkey->base_bytes); if ((brv = big_extend(&(dsakey.y), CHARLEN2BIGNUMLEN(value_bytes))) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&(dsakey.y), bkey->public_y, value_bytes); /* * Copy signature to DSA key r and s values */ if ((brv = big_extend(&(dsakey.r), CHARLEN2BIGNUMLEN(DSA_SUBPRIME_BYTES))) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&(dsakey.r), sig, DSA_SUBPRIME_BYTES); if ((brv = big_extend(&(dsakey.s), CHARLEN2BIGNUMLEN(DSA_SUBPRIME_BYTES))) != BIG_OK) { rv = convert_rv(brv); goto clean1; } bytestring2bignum(&(dsakey.s), sig + DSA_SUBPRIME_BYTES, DSA_SUBPRIME_BYTES); if (big_init(&msg, BIG_CHUNKS_FOR_160BITS) != BIG_OK) { rv = CKR_HOST_MEMORY; goto clean1; } bytestring2bignum(&msg, data, DSA_SUBPRIME_BYTES); if (big_init(&tmp1, 2 * CHARLEN2BIGNUMLEN(prime_bytes)) != BIG_OK) { rv = CKR_HOST_MEMORY; goto clean2; } if (big_init(&tmp2, CHARLEN2BIGNUMLEN(prime_bytes)) != BIG_OK) { rv = CKR_HOST_MEMORY; goto clean3; } if (big_init(&tmp3, 2 * BIG_CHUNKS_FOR_160BITS) != BIG_OK) { rv = CKR_HOST_MEMORY; goto clean4; } /* * Verify signature against msg. */ if (big_ext_gcd_pos(NULL, &tmp2, NULL, &(dsakey.s), &(dsakey.q)) != BIG_OK) { rv = convert_rv(brv); goto clean5; } if (tmp2.sign == -1) if (big_add(&tmp2, &tmp2, &(dsakey.q)) != BIG_OK) { rv = convert_rv(brv); goto clean5; /* tmp2 <- w */ } if (big_mul(&tmp1, &msg, &tmp2) != BIG_OK) { rv = convert_rv(brv); goto clean5; } if (big_div_pos(NULL, &tmp1, &tmp1, &(dsakey.q)) != BIG_OK) { rv = convert_rv(brv); goto clean5; /* tmp1 <- u_1 */ } if (big_mul(&tmp2, &tmp2, &(dsakey.r)) != BIG_OK) { rv = convert_rv(brv); goto clean5; } if (big_div_pos(NULL, &tmp2, &tmp2, &(dsakey.q)) != BIG_OK) { rv = convert_rv(brv); goto clean5; /* tmp2 <- u_2 */ } if (big_modexp(&tmp1, &(dsakey.g), &tmp1, &(dsakey.p), NULL) != BIG_OK) { rv = convert_rv(brv); goto clean5; } if (big_modexp(&tmp2, &(dsakey.y), &tmp2, &(dsakey.p), NULL) != BIG_OK) { rv = convert_rv(brv); goto clean5; } if (big_mul(&tmp1, &tmp1, &tmp2) != BIG_OK) { rv = convert_rv(brv); goto clean5; } if (big_div_pos(NULL, &tmp1, &tmp1, &(dsakey.p)) != BIG_OK) { rv = convert_rv(brv); goto clean5; } if (big_div_pos(NULL, &tmp1, &tmp1, &(dsakey.q)) != BIG_OK) { rv = convert_rv(brv); goto clean5; } if (big_cmp_abs(&tmp1, &(dsakey.r)) == 0) rv = CKR_OK; else rv = CKR_SIGNATURE_INVALID; clean5: big_finish(&tmp3); clean4: big_finish(&tmp2); clean3: big_finish(&tmp1); clean2: big_finish(&msg); clean1: DSA_key_finish(&dsakey); return (rv); }