diff options
Diffstat (limited to 'usr/src')
80 files changed, 24188 insertions, 187 deletions
diff --git a/usr/src/cmd/cmd-crypto/etc/kcf.conf b/usr/src/cmd/cmd-crypto/etc/kcf.conf index 95d2cb7bcb..365f278319 100644 --- a/usr/src/cmd/cmd-crypto/etc/kcf.conf +++ b/usr/src/cmd/cmd-crypto/etc/kcf.conf @@ -44,6 +44,7 @@ des:supportedlist=CKM_DES_CBC,CKM_DES_ECB,CKM_DES3_CBC,CKM_DES3_ECB aes:supportedlist=CKM_AES_ECB,CKM_AES_CBC,CKM_AES_CTR arcfour:supportedlist=CKM_RC4 blowfish:supportedlist=CKM_BLOWFISH_ECB,CKM_BLOWFISH_CBC +ecc:supportedlist=CKM_EC_KEY_PAIR_GEN,CKM_ECDH1_DERIVE,CKM_ECDSA,CKM_ECDSA_SHA1 sha1:supportedlist=CKM_SHA_1,CKM_SHA_1_HMAC_GENERAL,CKM_SHA_1_HMAC sha2:supportedlist=CKM_SHA256,CKM_SHA256_HMAC,CKM_SHA256_HMAC_GENERAL,CKM_SHA384,CKM_SHA384_HMAC,CKM_SHA384_HMAC_GENERAL,CKM_SHA512,CKM_SHA512_HMAC,CKM_SHA512_HMAC_GENERAL md4:supportedlist=CKM_MD4 diff --git a/usr/src/common/crypto/ecc/THIRDPARTYLICENSE b/usr/src/common/crypto/ecc/THIRDPARTYLICENSE new file mode 100644 index 0000000000..b286702b01 --- /dev/null +++ b/usr/src/common/crypto/ecc/THIRDPARTYLICENSE @@ -0,0 +1,34 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ______________________________________________. + * + * The Initial Developer of the Original Code is ______. + * Portions created by ______________________ are Copyright (C) ______ + * _______________________. All Rights Reserved. + * + * Contributor(s): ___________________________________________________. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ diff --git a/usr/src/common/crypto/ecc/ec.c b/usr/src/common/crypto/ecc/ec.c new file mode 100644 index 0000000000..49a3643763 --- /dev/null +++ b/usr/src/common/crypto/ecc/ec.c @@ -0,0 +1,1068 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Elliptic Curve Cryptography library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta <vipul.gupta@sun.com> and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mplogic.h" +#include "ec.h" +#include "ecl.h" + +#include <sys/types.h> +#ifndef _KERNEL +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#endif +#include "ecl-exp.h" +#include "mpi.h" +#include "ecc_impl.h" + +#ifdef _KERNEL +#define PORT_ZFree(p, l) bzero((p), (l)); kmem_free((p), (l)) +#else +#define PORT_ZFree(p, l) bzero((p), (l)); free((p)) +#endif + +/* + * Returns true if pointP is the point at infinity, false otherwise + */ +PRBool +ec_point_at_infinity(SECItem *pointP) +{ + unsigned int i; + + for (i = 1; i < pointP->len; i++) { + if (pointP->data[i] != 0x00) return PR_FALSE; + } + + return PR_TRUE; +} + +/* + * Computes scalar point multiplication pointQ = k1 * G + k2 * pointP for + * the curve whose parameters are encoded in params with base point G. + */ +SECStatus +ec_points_mul(const ECParams *params, const mp_int *k1, const mp_int *k2, + const SECItem *pointP, SECItem *pointQ, int kmflag) +{ + mp_int Px, Py, Qx, Qy; + mp_int Gx, Gy, order, irreducible, a, b; +#if 0 /* currently don't support non-named curves */ + unsigned int irr_arr[5]; +#endif + ECGroup *group = NULL; + SECStatus rv = SECFailure; + mp_err err = MP_OKAY; + int len; + +#if EC_DEBUG + int i; + char mpstr[256]; + + printf("ec_points_mul: params [len=%d]:", params->DEREncoding.len); + for (i = 0; i < params->DEREncoding.len; i++) + printf("%02x:", params->DEREncoding.data[i]); + printf("\n"); + + if (k1 != NULL) { + mp_tohex(k1, mpstr); + printf("ec_points_mul: scalar k1: %s\n", mpstr); + mp_todecimal(k1, mpstr); + printf("ec_points_mul: scalar k1: %s (dec)\n", mpstr); + } + + if (k2 != NULL) { + mp_tohex(k2, mpstr); + printf("ec_points_mul: scalar k2: %s\n", mpstr); + mp_todecimal(k2, mpstr); + printf("ec_points_mul: scalar k2: %s (dec)\n", mpstr); + } + + if (pointP != NULL) { + printf("ec_points_mul: pointP [len=%d]:", pointP->len); + for (i = 0; i < pointP->len; i++) + printf("%02x:", pointP->data[i]); + printf("\n"); + } +#endif + + /* NOTE: We only support uncompressed points for now */ + len = (params->fieldID.size + 7) >> 3; + if (pointP != NULL) { + if ((pointP->data[0] != EC_POINT_FORM_UNCOMPRESSED) || + (pointP->len != (2 * len + 1))) { + return SECFailure; + }; + } + + MP_DIGITS(&Px) = 0; + MP_DIGITS(&Py) = 0; + MP_DIGITS(&Qx) = 0; + MP_DIGITS(&Qy) = 0; + MP_DIGITS(&Gx) = 0; + MP_DIGITS(&Gy) = 0; + MP_DIGITS(&order) = 0; + MP_DIGITS(&irreducible) = 0; + MP_DIGITS(&a) = 0; + MP_DIGITS(&b) = 0; + CHECK_MPI_OK( mp_init(&Px, kmflag) ); + CHECK_MPI_OK( mp_init(&Py, kmflag) ); + CHECK_MPI_OK( mp_init(&Qx, kmflag) ); + CHECK_MPI_OK( mp_init(&Qy, kmflag) ); + CHECK_MPI_OK( mp_init(&Gx, kmflag) ); + CHECK_MPI_OK( mp_init(&Gy, kmflag) ); + CHECK_MPI_OK( mp_init(&order, kmflag) ); + CHECK_MPI_OK( mp_init(&irreducible, kmflag) ); + CHECK_MPI_OK( mp_init(&a, kmflag) ); + CHECK_MPI_OK( mp_init(&b, kmflag) ); + + if ((k2 != NULL) && (pointP != NULL)) { + /* Initialize Px and Py */ + CHECK_MPI_OK( mp_read_unsigned_octets(&Px, pointP->data + 1, (mp_size) len) ); + CHECK_MPI_OK( mp_read_unsigned_octets(&Py, pointP->data + 1 + len, (mp_size) len) ); + } + + /* construct from named params, if possible */ + if (params->name != ECCurve_noName) { + group = ECGroup_fromName(params->name, kmflag); + } + +#if 0 /* currently don't support non-named curves */ + if (group == NULL) { + /* Set up mp_ints containing the curve coefficients */ + CHECK_MPI_OK( mp_read_unsigned_octets(&Gx, params->base.data + 1, + (mp_size) len) ); + CHECK_MPI_OK( mp_read_unsigned_octets(&Gy, params->base.data + 1 + len, + (mp_size) len) ); + SECITEM_TO_MPINT( params->order, &order ); + SECITEM_TO_MPINT( params->curve.a, &a ); + SECITEM_TO_MPINT( params->curve.b, &b ); + if (params->fieldID.type == ec_field_GFp) { + SECITEM_TO_MPINT( params->fieldID.u.prime, &irreducible ); + group = ECGroup_consGFp(&irreducible, &a, &b, &Gx, &Gy, &order, params->cofactor); + } else { + SECITEM_TO_MPINT( params->fieldID.u.poly, &irreducible ); + irr_arr[0] = params->fieldID.size; + irr_arr[1] = params->fieldID.k1; + irr_arr[2] = params->fieldID.k2; + irr_arr[3] = params->fieldID.k3; + irr_arr[4] = 0; + group = ECGroup_consGF2m(&irreducible, irr_arr, &a, &b, &Gx, &Gy, &order, params->cofactor); + } + } +#endif + if (group == NULL) + goto cleanup; + + if ((k2 != NULL) && (pointP != NULL)) { + CHECK_MPI_OK( ECPoints_mul(group, k1, k2, &Px, &Py, &Qx, &Qy) ); + } else { + CHECK_MPI_OK( ECPoints_mul(group, k1, NULL, NULL, NULL, &Qx, &Qy) ); + } + + /* Construct the SECItem representation of point Q */ + pointQ->data[0] = EC_POINT_FORM_UNCOMPRESSED; + CHECK_MPI_OK( mp_to_fixlen_octets(&Qx, pointQ->data + 1, + (mp_size) len) ); + CHECK_MPI_OK( mp_to_fixlen_octets(&Qy, pointQ->data + 1 + len, + (mp_size) len) ); + + rv = SECSuccess; + +#if EC_DEBUG + printf("ec_points_mul: pointQ [len=%d]:", pointQ->len); + for (i = 0; i < pointQ->len; i++) + printf("%02x:", pointQ->data[i]); + printf("\n"); +#endif + +cleanup: + ECGroup_free(group); + mp_clear(&Px); + mp_clear(&Py); + mp_clear(&Qx); + mp_clear(&Qy); + mp_clear(&Gx); + mp_clear(&Gy); + mp_clear(&order); + mp_clear(&irreducible); + mp_clear(&a); + mp_clear(&b); + if (err) { + MP_TO_SEC_ERROR(err); + rv = SECFailure; + } + + return rv; +} + +/* Generates a new EC key pair. The private key is a supplied + * value and the public key is the result of performing a scalar + * point multiplication of that value with the curve's base point. + */ +SECStatus +ec_NewKey(ECParams *ecParams, ECPrivateKey **privKey, + const unsigned char *privKeyBytes, int privKeyLen, int kmflag) +{ + SECStatus rv = SECFailure; + PRArenaPool *arena; + ECPrivateKey *key; + mp_int k; + mp_err err = MP_OKAY; + int len; + +#if EC_DEBUG + printf("ec_NewKey called\n"); +#endif + +int printf(); + if (!ecParams || !privKey || !privKeyBytes || (privKeyLen < 0)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* Initialize an arena for the EC key. */ + if (!(arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE))) + return SECFailure; + + key = (ECPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(ECPrivateKey), + kmflag); + if (!key) { + PORT_FreeArena(arena, PR_TRUE); + return SECFailure; + } + + /* Set the version number (SEC 1 section C.4 says it should be 1) */ + SECITEM_AllocItem(arena, &key->version, 1, kmflag); + key->version.data[0] = 1; + + /* Copy all of the fields from the ECParams argument to the + * ECParams structure within the private key. + */ + key->ecParams.arena = arena; + key->ecParams.type = ecParams->type; + key->ecParams.fieldID.size = ecParams->fieldID.size; + key->ecParams.fieldID.type = ecParams->fieldID.type; + if (ecParams->fieldID.type == ec_field_GFp) { + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.prime, + &ecParams->fieldID.u.prime, kmflag)); + } else { + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.poly, + &ecParams->fieldID.u.poly, kmflag)); + } + key->ecParams.fieldID.k1 = ecParams->fieldID.k1; + key->ecParams.fieldID.k2 = ecParams->fieldID.k2; + key->ecParams.fieldID.k3 = ecParams->fieldID.k3; + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.a, + &ecParams->curve.a, kmflag)); + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.b, + &ecParams->curve.b, kmflag)); + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.seed, + &ecParams->curve.seed, kmflag)); + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.base, + &ecParams->base, kmflag)); + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.order, + &ecParams->order, kmflag)); + key->ecParams.cofactor = ecParams->cofactor; + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.DEREncoding, + &ecParams->DEREncoding, kmflag)); + key->ecParams.name = ecParams->name; + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curveOID, + &ecParams->curveOID, kmflag)); + + len = (ecParams->fieldID.size + 7) >> 3; + SECITEM_AllocItem(arena, &key->publicValue, 2*len + 1, kmflag); + len = ecParams->order.len; + SECITEM_AllocItem(arena, &key->privateValue, len, kmflag); + + /* Copy private key */ + if (privKeyLen >= len) { + memcpy(key->privateValue.data, privKeyBytes, len); + } else { + memset(key->privateValue.data, 0, (len - privKeyLen)); + memcpy(key->privateValue.data + (len - privKeyLen), privKeyBytes, privKeyLen); + } + + /* Compute corresponding public key */ + MP_DIGITS(&k) = 0; + CHECK_MPI_OK( mp_init(&k, kmflag) ); + CHECK_MPI_OK( mp_read_unsigned_octets(&k, key->privateValue.data, + (mp_size) len) ); + + rv = ec_points_mul(ecParams, &k, NULL, NULL, &(key->publicValue), kmflag); + if (rv != SECSuccess) goto cleanup; + *privKey = key; + +cleanup: + mp_clear(&k); + if (rv) + PORT_FreeArena(arena, PR_TRUE); + +#if EC_DEBUG + printf("ec_NewKey returning %s\n", + (rv == SECSuccess) ? "success" : "failure"); +#endif + + return rv; + +} + +/* Generates a new EC key pair. The private key is a supplied + * random value (in seed) and the public key is the result of + * performing a scalar point multiplication of that value with + * the curve's base point. + */ +SECStatus +EC_NewKeyFromSeed(ECParams *ecParams, ECPrivateKey **privKey, + const unsigned char *seed, int seedlen, int kmflag) +{ + SECStatus rv = SECFailure; + rv = ec_NewKey(ecParams, privKey, seed, seedlen, kmflag); + return rv; +} + +/* Generate a random private key using the algorithm A.4.1 of ANSI X9.62, + * modified a la FIPS 186-2 Change Notice 1 to eliminate the bias in the + * random number generator. + * + * Parameters + * - order: a buffer that holds the curve's group order + * - len: the length in octets of the order buffer + * + * Return Value + * Returns a buffer of len octets that holds the private key. The caller + * is responsible for freeing the buffer with PORT_ZFree. + */ +static unsigned char * +ec_GenerateRandomPrivateKey(const unsigned char *order, int len, int kmflag) +{ + SECStatus rv = SECSuccess; + mp_err err; + unsigned char *privKeyBytes = NULL; + mp_int privKeyVal, order_1, one; + + MP_DIGITS(&privKeyVal) = 0; + MP_DIGITS(&order_1) = 0; + MP_DIGITS(&one) = 0; + CHECK_MPI_OK( mp_init(&privKeyVal, kmflag) ); + CHECK_MPI_OK( mp_init(&order_1, kmflag) ); + CHECK_MPI_OK( mp_init(&one, kmflag) ); + + /* Generates 2*len random bytes using the global random bit generator + * (which implements Algorithm 1 of FIPS 186-2 Change Notice 1) then + * reduces modulo the group order. + */ + if ((privKeyBytes = PORT_Alloc(2*len, kmflag)) == NULL) goto cleanup; + CHECK_SEC_OK( RNG_GenerateGlobalRandomBytes(privKeyBytes, 2*len) ); + CHECK_MPI_OK( mp_read_unsigned_octets(&privKeyVal, privKeyBytes, 2*len) ); + CHECK_MPI_OK( mp_read_unsigned_octets(&order_1, order, len) ); + CHECK_MPI_OK( mp_set_int(&one, 1) ); + CHECK_MPI_OK( mp_sub(&order_1, &one, &order_1) ); + CHECK_MPI_OK( mp_mod(&privKeyVal, &order_1, &privKeyVal) ); + CHECK_MPI_OK( mp_add(&privKeyVal, &one, &privKeyVal) ); + CHECK_MPI_OK( mp_to_fixlen_octets(&privKeyVal, privKeyBytes, len) ); + memset(privKeyBytes+len, 0, len); +cleanup: + mp_clear(&privKeyVal); + mp_clear(&order_1); + mp_clear(&one); + if (err < MP_OKAY) { + MP_TO_SEC_ERROR(err); + rv = SECFailure; + } + if (rv != SECSuccess && privKeyBytes) { +#ifdef _KERNEL + kmem_free(privKeyBytes, 2*len); +#else + free(privKeyBytes); +#endif + privKeyBytes = NULL; + } + return privKeyBytes; +} + +/* Generates a new EC key pair. The private key is a random value and + * the public key is the result of performing a scalar point multiplication + * of that value with the curve's base point. + */ +SECStatus +EC_NewKey(ECParams *ecParams, ECPrivateKey **privKey, int kmflag) +{ + SECStatus rv = SECFailure; + int len; + unsigned char *privKeyBytes = NULL; + + if (!ecParams) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + len = ecParams->order.len; + privKeyBytes = ec_GenerateRandomPrivateKey(ecParams->order.data, len, + kmflag); + if (privKeyBytes == NULL) goto cleanup; + /* generate public key */ + CHECK_SEC_OK( ec_NewKey(ecParams, privKey, privKeyBytes, len, kmflag) ); + +cleanup: + if (privKeyBytes) { + PORT_ZFree(privKeyBytes, len * 2); + } +#if EC_DEBUG + printf("EC_NewKey returning %s\n", + (rv == SECSuccess) ? "success" : "failure"); +#endif + + return rv; +} + +/* Validates an EC public key as described in Section 5.2.2 of + * X9.62. The ECDH primitive when used without the cofactor does + * not address small subgroup attacks, which may occur when the + * public key is not valid. These attacks can be prevented by + * validating the public key before using ECDH. + */ +SECStatus +EC_ValidatePublicKey(ECParams *ecParams, SECItem *publicValue, int kmflag) +{ + mp_int Px, Py; + ECGroup *group = NULL; + SECStatus rv = SECFailure; + mp_err err = MP_OKAY; + int len; + + if (!ecParams || !publicValue) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* NOTE: We only support uncompressed points for now */ + len = (ecParams->fieldID.size + 7) >> 3; + if (publicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM); + return SECFailure; + } else if (publicValue->len != (2 * len + 1)) { + PORT_SetError(SEC_ERROR_BAD_KEY); + return SECFailure; + } + + MP_DIGITS(&Px) = 0; + MP_DIGITS(&Py) = 0; + CHECK_MPI_OK( mp_init(&Px, kmflag) ); + CHECK_MPI_OK( mp_init(&Py, kmflag) ); + + /* Initialize Px and Py */ + CHECK_MPI_OK( mp_read_unsigned_octets(&Px, publicValue->data + 1, (mp_size) len) ); + CHECK_MPI_OK( mp_read_unsigned_octets(&Py, publicValue->data + 1 + len, (mp_size) len) ); + + /* construct from named params */ + group = ECGroup_fromName(ecParams->name, kmflag); + if (group == NULL) { + /* + * ECGroup_fromName fails if ecParams->name is not a valid + * ECCurveName value, or if we run out of memory, or perhaps + * for other reasons. Unfortunately if ecParams->name is a + * valid ECCurveName value, we don't know what the right error + * code should be because ECGroup_fromName doesn't return an + * error code to the caller. Set err to MP_UNDEF because + * that's what ECGroup_fromName uses internally. + */ + if ((ecParams->name <= ECCurve_noName) || + (ecParams->name >= ECCurve_pastLastCurve)) { + err = MP_BADARG; + } else { + err = MP_UNDEF; + } + goto cleanup; + } + + /* validate public point */ + if ((err = ECPoint_validate(group, &Px, &Py)) < MP_YES) { + if (err == MP_NO) { + PORT_SetError(SEC_ERROR_BAD_KEY); + rv = SECFailure; + err = MP_OKAY; /* don't change the error code */ + } + goto cleanup; + } + + rv = SECSuccess; + +cleanup: + ECGroup_free(group); + mp_clear(&Px); + mp_clear(&Py); + if (err) { + MP_TO_SEC_ERROR(err); + rv = SECFailure; + } + return rv; +} + +/* +** Performs an ECDH key derivation by computing the scalar point +** multiplication of privateValue and publicValue (with or without the +** cofactor) and returns the x-coordinate of the resulting elliptic +** curve point in derived secret. If successful, derivedSecret->data +** is set to the address of the newly allocated buffer containing the +** derived secret, and derivedSecret->len is the size of the secret +** produced. It is the caller's responsibility to free the allocated +** buffer containing the derived secret. +*/ +SECStatus +ECDH_Derive(SECItem *publicValue, + ECParams *ecParams, + SECItem *privateValue, + PRBool withCofactor, + SECItem *derivedSecret, + int kmflag) +{ + SECStatus rv = SECFailure; + unsigned int len = 0; + SECItem pointQ = {siBuffer, NULL, 0}; + mp_int k; /* to hold the private value */ + mp_int cofactor; + mp_err err = MP_OKAY; +#if EC_DEBUG + int i; +#endif + + if (!publicValue || !ecParams || !privateValue || + !derivedSecret) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + memset(derivedSecret, 0, sizeof *derivedSecret); + len = (ecParams->fieldID.size + 7) >> 3; + pointQ.len = 2*len + 1; + if ((pointQ.data = PORT_Alloc(2*len + 1, kmflag)) == NULL) goto cleanup; + + MP_DIGITS(&k) = 0; + CHECK_MPI_OK( mp_init(&k, kmflag) ); + CHECK_MPI_OK( mp_read_unsigned_octets(&k, privateValue->data, + (mp_size) privateValue->len) ); + + if (withCofactor && (ecParams->cofactor != 1)) { + /* multiply k with the cofactor */ + MP_DIGITS(&cofactor) = 0; + CHECK_MPI_OK( mp_init(&cofactor, kmflag) ); + mp_set(&cofactor, ecParams->cofactor); + CHECK_MPI_OK( mp_mul(&k, &cofactor, &k) ); + } + + /* Multiply our private key and peer's public point */ + if ((ec_points_mul(ecParams, NULL, &k, publicValue, &pointQ, kmflag) != SECSuccess) || + ec_point_at_infinity(&pointQ)) + goto cleanup; + + /* Allocate memory for the derived secret and copy + * the x co-ordinate of pointQ into it. + */ + SECITEM_AllocItem(NULL, derivedSecret, len, kmflag); + memcpy(derivedSecret->data, pointQ.data + 1, len); + + rv = SECSuccess; + +#if EC_DEBUG + printf("derived_secret:\n"); + for (i = 0; i < derivedSecret->len; i++) + printf("%02x:", derivedSecret->data[i]); + printf("\n"); +#endif + +cleanup: + mp_clear(&k); + + if (pointQ.data) { + PORT_ZFree(pointQ.data, 2*len + 1); + } + + return rv; +} + +/* Computes the ECDSA signature (a concatenation of two values r and s) + * on the digest using the given key and the random value kb (used in + * computing s). + */ +SECStatus +ECDSA_SignDigestWithSeed(ECPrivateKey *key, SECItem *signature, + const SECItem *digest, const unsigned char *kb, const int kblen, int kmflag) +{ + SECStatus rv = SECFailure; + mp_int x1; + mp_int d, k; /* private key, random integer */ + mp_int r, s; /* tuple (r, s) is the signature */ + mp_int n; + mp_err err = MP_OKAY; + ECParams *ecParams = NULL; + SECItem kGpoint = { siBuffer, NULL, 0}; + int flen = 0; /* length in bytes of the field size */ + unsigned olen; /* length in bytes of the base point order */ + +#if EC_DEBUG + char mpstr[256]; +#endif + + /* Initialize MPI integers. */ + /* must happen before the first potential call to cleanup */ + MP_DIGITS(&x1) = 0; + MP_DIGITS(&d) = 0; + MP_DIGITS(&k) = 0; + MP_DIGITS(&r) = 0; + MP_DIGITS(&s) = 0; + MP_DIGITS(&n) = 0; + + /* Check args */ + if (!key || !signature || !digest || !kb || (kblen < 0)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto cleanup; + } + + ecParams = &(key->ecParams); + flen = (ecParams->fieldID.size + 7) >> 3; + olen = ecParams->order.len; + if (signature->data == NULL) { + /* a call to get the signature length only */ + goto finish; + } + if (signature->len < 2*olen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + rv = SECBufferTooSmall; + goto cleanup; + } + + + CHECK_MPI_OK( mp_init(&x1, kmflag) ); + CHECK_MPI_OK( mp_init(&d, kmflag) ); + CHECK_MPI_OK( mp_init(&k, kmflag) ); + CHECK_MPI_OK( mp_init(&r, kmflag) ); + CHECK_MPI_OK( mp_init(&s, kmflag) ); + CHECK_MPI_OK( mp_init(&n, kmflag) ); + + SECITEM_TO_MPINT( ecParams->order, &n ); + SECITEM_TO_MPINT( key->privateValue, &d ); + CHECK_MPI_OK( mp_read_unsigned_octets(&k, kb, kblen) ); + /* Make sure k is in the interval [1, n-1] */ + if ((mp_cmp_z(&k) <= 0) || (mp_cmp(&k, &n) >= 0)) { +#if EC_DEBUG + printf("k is outside [1, n-1]\n"); + mp_tohex(&k, mpstr); + printf("k : %s \n", mpstr); + mp_tohex(&n, mpstr); + printf("n : %s \n", mpstr); +#endif + PORT_SetError(SEC_ERROR_NEED_RANDOM); + goto cleanup; + } + + /* + ** ANSI X9.62, Section 5.3.2, Step 2 + ** + ** Compute kG + */ + kGpoint.len = 2*flen + 1; + kGpoint.data = PORT_Alloc(2*flen + 1, kmflag); + if ((kGpoint.data == NULL) || + (ec_points_mul(ecParams, &k, NULL, NULL, &kGpoint, kmflag) + != SECSuccess)) + goto cleanup; + + /* + ** ANSI X9.62, Section 5.3.3, Step 1 + ** + ** Extract the x co-ordinate of kG into x1 + */ + CHECK_MPI_OK( mp_read_unsigned_octets(&x1, kGpoint.data + 1, + (mp_size) flen) ); + + /* + ** ANSI X9.62, Section 5.3.3, Step 2 + ** + ** r = x1 mod n NOTE: n is the order of the curve + */ + CHECK_MPI_OK( mp_mod(&x1, &n, &r) ); + + /* + ** ANSI X9.62, Section 5.3.3, Step 3 + ** + ** verify r != 0 + */ + if (mp_cmp_z(&r) == 0) { + PORT_SetError(SEC_ERROR_NEED_RANDOM); + goto cleanup; + } + + /* + ** ANSI X9.62, Section 5.3.3, Step 4 + ** + ** s = (k**-1 * (HASH(M) + d*r)) mod n + */ + SECITEM_TO_MPINT(*digest, &s); /* s = HASH(M) */ + + /* In the definition of EC signing, digests are truncated + * to the length of n in bits. + * (see SEC 1 "Elliptic Curve Digit Signature Algorithm" section 4.1.*/ + if (digest->len*8 > ecParams->fieldID.size) { + mpl_rsh(&s,&s,digest->len*8 - ecParams->fieldID.size); + } + +#if EC_DEBUG + mp_todecimal(&n, mpstr); + printf("n : %s (dec)\n", mpstr); + mp_todecimal(&d, mpstr); + printf("d : %s (dec)\n", mpstr); + mp_tohex(&x1, mpstr); + printf("x1: %s\n", mpstr); + mp_todecimal(&s, mpstr); + printf("digest: %s (decimal)\n", mpstr); + mp_todecimal(&r, mpstr); + printf("r : %s (dec)\n", mpstr); + mp_tohex(&r, mpstr); + printf("r : %s\n", mpstr); +#endif + + CHECK_MPI_OK( mp_invmod(&k, &n, &k) ); /* k = k**-1 mod n */ + CHECK_MPI_OK( mp_mulmod(&d, &r, &n, &d) ); /* d = d * r mod n */ + CHECK_MPI_OK( mp_addmod(&s, &d, &n, &s) ); /* s = s + d mod n */ + CHECK_MPI_OK( mp_mulmod(&s, &k, &n, &s) ); /* s = s * k mod n */ + +#if EC_DEBUG + mp_todecimal(&s, mpstr); + printf("s : %s (dec)\n", mpstr); + mp_tohex(&s, mpstr); + printf("s : %s\n", mpstr); +#endif + + /* + ** ANSI X9.62, Section 5.3.3, Step 5 + ** + ** verify s != 0 + */ + if (mp_cmp_z(&s) == 0) { + PORT_SetError(SEC_ERROR_NEED_RANDOM); + goto cleanup; + } + + /* + ** + ** Signature is tuple (r, s) + */ + CHECK_MPI_OK( mp_to_fixlen_octets(&r, signature->data, olen) ); + CHECK_MPI_OK( mp_to_fixlen_octets(&s, signature->data + olen, olen) ); +finish: + signature->len = 2*olen; + + rv = SECSuccess; + err = MP_OKAY; +cleanup: + mp_clear(&x1); + mp_clear(&d); + mp_clear(&k); + mp_clear(&r); + mp_clear(&s); + mp_clear(&n); + + if (kGpoint.data) { + PORT_ZFree(kGpoint.data, 2*flen + 1); + } + + if (err) { + MP_TO_SEC_ERROR(err); + rv = SECFailure; + } + +#if EC_DEBUG + printf("ECDSA signing with seed %s\n", + (rv == SECSuccess) ? "succeeded" : "failed"); +#endif + + return rv; +} + +/* +** Computes the ECDSA signature on the digest using the given key +** and a random seed. +*/ +SECStatus +ECDSA_SignDigest(ECPrivateKey *key, SECItem *signature, const SECItem *digest, + int kmflag) +{ + SECStatus rv = SECFailure; + int len; + unsigned char *kBytes= NULL; + + if (!key) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* Generate random value k */ + len = key->ecParams.order.len; + kBytes = ec_GenerateRandomPrivateKey(key->ecParams.order.data, len, + kmflag); + if (kBytes == NULL) goto cleanup; + + /* Generate ECDSA signature with the specified k value */ + rv = ECDSA_SignDigestWithSeed(key, signature, digest, kBytes, len, kmflag); + +cleanup: + if (kBytes) { + PORT_ZFree(kBytes, len * 2); + } + +#if EC_DEBUG + printf("ECDSA signing %s\n", + (rv == SECSuccess) ? "succeeded" : "failed"); +#endif + + return rv; +} + +/* +** Checks the signature on the given digest using the key provided. +*/ +SECStatus +ECDSA_VerifyDigest(ECPublicKey *key, const SECItem *signature, + const SECItem *digest, int kmflag) +{ + SECStatus rv = SECFailure; + mp_int r_, s_; /* tuple (r', s') is received signature) */ + mp_int c, u1, u2, v; /* intermediate values used in verification */ + mp_int x1; + mp_int n; + mp_err err = MP_OKAY; + ECParams *ecParams = NULL; + SECItem pointC = { siBuffer, NULL, 0 }; + int slen; /* length in bytes of a half signature (r or s) */ + int flen; /* length in bytes of the field size */ + unsigned olen; /* length in bytes of the base point order */ + +#if EC_DEBUG + char mpstr[256]; + printf("ECDSA verification called\n"); +#endif + + /* Initialize MPI integers. */ + /* must happen before the first potential call to cleanup */ + MP_DIGITS(&r_) = 0; + MP_DIGITS(&s_) = 0; + MP_DIGITS(&c) = 0; + MP_DIGITS(&u1) = 0; + MP_DIGITS(&u2) = 0; + MP_DIGITS(&x1) = 0; + MP_DIGITS(&v) = 0; + MP_DIGITS(&n) = 0; + + /* Check args */ + if (!key || !signature || !digest) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto cleanup; + } + + ecParams = &(key->ecParams); + flen = (ecParams->fieldID.size + 7) >> 3; + olen = ecParams->order.len; + if (signature->len == 0 || signature->len%2 != 0 || + signature->len > 2*olen) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + goto cleanup; + } + slen = signature->len/2; + + SECITEM_AllocItem(NULL, &pointC, 2*flen + 1, kmflag); + if (pointC.data == NULL) + goto cleanup; + + CHECK_MPI_OK( mp_init(&r_, kmflag) ); + CHECK_MPI_OK( mp_init(&s_, kmflag) ); + CHECK_MPI_OK( mp_init(&c, kmflag) ); + CHECK_MPI_OK( mp_init(&u1, kmflag) ); + CHECK_MPI_OK( mp_init(&u2, kmflag) ); + CHECK_MPI_OK( mp_init(&x1, kmflag) ); + CHECK_MPI_OK( mp_init(&v, kmflag) ); + CHECK_MPI_OK( mp_init(&n, kmflag) ); + + /* + ** Convert received signature (r', s') into MPI integers. + */ + CHECK_MPI_OK( mp_read_unsigned_octets(&r_, signature->data, slen) ); + CHECK_MPI_OK( mp_read_unsigned_octets(&s_, signature->data + slen, slen) ); + + /* + ** ANSI X9.62, Section 5.4.2, Steps 1 and 2 + ** + ** Verify that 0 < r' < n and 0 < s' < n + */ + SECITEM_TO_MPINT(ecParams->order, &n); + if (mp_cmp_z(&r_) <= 0 || mp_cmp_z(&s_) <= 0 || + mp_cmp(&r_, &n) >= 0 || mp_cmp(&s_, &n) >= 0) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + goto cleanup; /* will return rv == SECFailure */ + } + + /* + ** ANSI X9.62, Section 5.4.2, Step 3 + ** + ** c = (s')**-1 mod n + */ + CHECK_MPI_OK( mp_invmod(&s_, &n, &c) ); /* c = (s')**-1 mod n */ + + /* + ** ANSI X9.62, Section 5.4.2, Step 4 + ** + ** u1 = ((HASH(M')) * c) mod n + */ + SECITEM_TO_MPINT(*digest, &u1); /* u1 = HASH(M) */ + + /* In the definition of EC signing, digests are truncated + * to the length of n in bits. + * (see SEC 1 "Elliptic Curve Digit Signature Algorithm" section 4.1.*/ + if (digest->len*8 > ecParams->fieldID.size) { /* u1 = HASH(M') */ + mpl_rsh(&u1,&u1,digest->len*8- ecParams->fieldID.size); + } + +#if EC_DEBUG + mp_todecimal(&r_, mpstr); + printf("r_: %s (dec)\n", mpstr); + mp_todecimal(&s_, mpstr); + printf("s_: %s (dec)\n", mpstr); + mp_todecimal(&c, mpstr); + printf("c : %s (dec)\n", mpstr); + mp_todecimal(&u1, mpstr); + printf("digest: %s (dec)\n", mpstr); +#endif + + CHECK_MPI_OK( mp_mulmod(&u1, &c, &n, &u1) ); /* u1 = u1 * c mod n */ + + /* + ** ANSI X9.62, Section 5.4.2, Step 4 + ** + ** u2 = ((r') * c) mod n + */ + CHECK_MPI_OK( mp_mulmod(&r_, &c, &n, &u2) ); + + /* + ** ANSI X9.62, Section 5.4.3, Step 1 + ** + ** Compute u1*G + u2*Q + ** Here, A = u1.G B = u2.Q and C = A + B + ** If the result, C, is the point at infinity, reject the signature + */ + if (ec_points_mul(ecParams, &u1, &u2, &key->publicValue, &pointC, kmflag) + != SECSuccess) { + rv = SECFailure; + goto cleanup; + } + if (ec_point_at_infinity(&pointC)) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + goto cleanup; + } + + CHECK_MPI_OK( mp_read_unsigned_octets(&x1, pointC.data + 1, flen) ); + + /* + ** ANSI X9.62, Section 5.4.4, Step 2 + ** + ** v = x1 mod n + */ + CHECK_MPI_OK( mp_mod(&x1, &n, &v) ); + +#if EC_DEBUG + mp_todecimal(&r_, mpstr); + printf("r_: %s (dec)\n", mpstr); + mp_todecimal(&v, mpstr); + printf("v : %s (dec)\n", mpstr); +#endif + + /* + ** ANSI X9.62, Section 5.4.4, Step 3 + ** + ** Verification: v == r' + */ + if (mp_cmp(&v, &r_)) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; /* Signature failed to verify. */ + } else { + rv = SECSuccess; /* Signature verified. */ + } + +#if EC_DEBUG + mp_todecimal(&u1, mpstr); + printf("u1: %s (dec)\n", mpstr); + mp_todecimal(&u2, mpstr); + printf("u2: %s (dec)\n", mpstr); + mp_tohex(&x1, mpstr); + printf("x1: %s\n", mpstr); + mp_todecimal(&v, mpstr); + printf("v : %s (dec)\n", mpstr); +#endif + +cleanup: + mp_clear(&r_); + mp_clear(&s_); + mp_clear(&c); + mp_clear(&u1); + mp_clear(&u2); + mp_clear(&x1); + mp_clear(&v); + mp_clear(&n); + + if (pointC.data) SECITEM_FreeItem(&pointC, PR_FALSE); + if (err) { + MP_TO_SEC_ERROR(err); + rv = SECFailure; + } + +#if EC_DEBUG + printf("ECDSA verification %s\n", + (rv == SECSuccess) ? "succeeded" : "failed"); +#endif + + return rv; +} + diff --git a/usr/src/common/crypto/ecc/ec.h b/usr/src/common/crypto/ecc/ec.h new file mode 100644 index 0000000000..c84a3c3344 --- /dev/null +++ b/usr/src/common/crypto/ecc/ec.h @@ -0,0 +1,60 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Elliptic Curve Cryptography library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef __ec_h_ +#define __ec_h_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define EC_DEBUG 0 +#define EC_POINT_FORM_COMPRESSED_Y0 0x02 +#define EC_POINT_FORM_COMPRESSED_Y1 0x03 +#define EC_POINT_FORM_UNCOMPRESSED 0x04 +#define EC_POINT_FORM_HYBRID_Y0 0x06 +#define EC_POINT_FORM_HYBRID_Y1 0x07 + +#define ANSI_X962_CURVE_OID_TOTAL_LEN 10 +#define SECG_CURVE_OID_TOTAL_LEN 7 + +#endif /* __ec_h_ */ diff --git a/usr/src/common/crypto/ecc/ec2.h b/usr/src/common/crypto/ecc/ec2.h new file mode 100644 index 0000000000..bf290142f2 --- /dev/null +++ b/usr/src/common/crypto/ecc/ec2.h @@ -0,0 +1,134 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for binary polynomial field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _EC2_H +#define _EC2_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecl-priv.h" + +/* Checks if point P(px, py) is at infinity. Uses affine coordinates. */ +mp_err ec_GF2m_pt_is_inf_aff(const mp_int *px, const mp_int *py); + +/* Sets P(px, py) to be the point at infinity. Uses affine coordinates. */ +mp_err ec_GF2m_pt_set_inf_aff(mp_int *px, mp_int *py); + +/* Computes R = P + Q where R is (rx, ry), P is (px, py) and Q is (qx, + * qy). Uses affine coordinates. */ +mp_err ec_GF2m_pt_add_aff(const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Computes R = P - Q. Uses affine coordinates. */ +mp_err ec_GF2m_pt_sub_aff(const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Computes R = 2P. Uses affine coordinates. */ +mp_err ec_GF2m_pt_dbl_aff(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Validates a point on a GF2m curve. */ +mp_err ec_GF2m_validate_point(const mp_int *px, const mp_int *py, const ECGroup *group); + +/* by default, this routine is unused and thus doesn't need to be compiled */ +#ifdef ECL_ENABLE_GF2M_PT_MUL_AFF +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the irreducible that + * determines the field GF2m. Uses affine coordinates. */ +mp_err ec_GF2m_pt_mul_aff(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); +#endif + +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the irreducible that + * determines the field GF2m. Uses Montgomery projective coordinates. */ +mp_err ec_GF2m_pt_mul_mont(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); + +#ifdef ECL_ENABLE_GF2M_PROJ +/* Converts a point P(px, py) from affine coordinates to projective + * coordinates R(rx, ry, rz). */ +mp_err ec_GF2m_pt_aff2proj(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group); + +/* Converts a point P(px, py, pz) from projective coordinates to affine + * coordinates R(rx, ry). */ +mp_err ec_GF2m_pt_proj2aff(const mp_int *px, const mp_int *py, + const mp_int *pz, mp_int *rx, mp_int *ry, + const ECGroup *group); + +/* Checks if point P(px, py, pz) is at infinity. Uses projective + * coordinates. */ +mp_err ec_GF2m_pt_is_inf_proj(const mp_int *px, const mp_int *py, + const mp_int *pz); + +/* Sets P(px, py, pz) to be the point at infinity. Uses projective + * coordinates. */ +mp_err ec_GF2m_pt_set_inf_proj(mp_int *px, mp_int *py, mp_int *pz); + +/* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is + * (qx, qy, qz). Uses projective coordinates. */ +mp_err ec_GF2m_pt_add_proj(const mp_int *px, const mp_int *py, + const mp_int *pz, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + mp_int *rz, const ECGroup *group); + +/* Computes R = 2P. Uses projective coordinates. */ +mp_err ec_GF2m_pt_dbl_proj(const mp_int *px, const mp_int *py, + const mp_int *pz, mp_int *rx, mp_int *ry, + mp_int *rz, const ECGroup *group); + +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GF2m. Uses projective coordinates. */ +mp_err ec_GF2m_pt_mul_proj(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); +#endif + +#endif /* _EC2_H */ diff --git a/usr/src/common/crypto/ecc/ec2_163.c b/usr/src/common/crypto/ecc/ec2_163.c new file mode 100644 index 0000000000..ed45a53f95 --- /dev/null +++ b/usr/src/common/crypto/ecc/ec2_163.c @@ -0,0 +1,269 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for binary polynomial field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang-Shantz <sheueling.chang@sun.com>, + * Stephen Fung <fungstep@hotmail.com>, and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ec2.h" +#include "mp_gf2m.h" +#include "mp_gf2m-priv.h" +#include "mpi.h" +#include "mpi-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* Fast reduction for polynomials over a 163-bit curve. Assumes reduction + * polynomial with terms {163, 7, 6, 3, 0}. */ +mp_err +ec_GF2m_163_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, z; + + if (a != r) { + MP_CHECKOK(mp_copy(a, r)); + } +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(r) < 6) { + MP_CHECKOK(s_mp_pad(r, 6)); + } + u = MP_DIGITS(r); + MP_USED(r) = 6; + + /* u[5] only has 6 significant bits */ + z = u[5]; + u[2] ^= (z << 36) ^ (z << 35) ^ (z << 32) ^ (z << 29); + z = u[4]; + u[2] ^= (z >> 28) ^ (z >> 29) ^ (z >> 32) ^ (z >> 35); + u[1] ^= (z << 36) ^ (z << 35) ^ (z << 32) ^ (z << 29); + z = u[3]; + u[1] ^= (z >> 28) ^ (z >> 29) ^ (z >> 32) ^ (z >> 35); + u[0] ^= (z << 36) ^ (z << 35) ^ (z << 32) ^ (z << 29); + z = u[2] >> 35; /* z only has 29 significant bits */ + u[0] ^= (z << 7) ^ (z << 6) ^ (z << 3) ^ z; + /* clear bits above 163 */ + u[5] = u[4] = u[3] = 0; + u[2] ^= z << 35; +#else + if (MP_USED(r) < 11) { + MP_CHECKOK(s_mp_pad(r, 11)); + } + u = MP_DIGITS(r); + MP_USED(r) = 11; + + /* u[11] only has 6 significant bits */ + z = u[10]; + u[5] ^= (z << 4) ^ (z << 3) ^ z ^ (z >> 3); + u[4] ^= (z << 29); + z = u[9]; + u[5] ^= (z >> 28) ^ (z >> 29); + u[4] ^= (z << 4) ^ (z << 3) ^ z ^ (z >> 3); + u[3] ^= (z << 29); + z = u[8]; + u[4] ^= (z >> 28) ^ (z >> 29); + u[3] ^= (z << 4) ^ (z << 3) ^ z ^ (z >> 3); + u[2] ^= (z << 29); + z = u[7]; + u[3] ^= (z >> 28) ^ (z >> 29); + u[2] ^= (z << 4) ^ (z << 3) ^ z ^ (z >> 3); + u[1] ^= (z << 29); + z = u[6]; + u[2] ^= (z >> 28) ^ (z >> 29); + u[1] ^= (z << 4) ^ (z << 3) ^ z ^ (z >> 3); + u[0] ^= (z << 29); + z = u[5] >> 3; /* z only has 29 significant bits */ + u[1] ^= (z >> 25) ^ (z >> 26); + u[0] ^= (z << 7) ^ (z << 6) ^ (z << 3) ^ z; + /* clear bits above 163 */ + u[11] = u[10] = u[9] = u[8] = u[7] = u[6] = 0; + u[5] ^= z << 3; +#endif + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* Fast squaring for polynomials over a 163-bit curve. Assumes reduction + * polynomial with terms {163, 7, 6, 3, 0}. */ +mp_err +ec_GF2m_163_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, *v; + + v = MP_DIGITS(a); + +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(a) < 3) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 6) { + MP_CHECKOK(s_mp_pad(r, 6)); + } + MP_USED(r) = 6; +#else + if (MP_USED(a) < 6) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 12) { + MP_CHECKOK(s_mp_pad(r, 12)); + } + MP_USED(r) = 12; +#endif + u = MP_DIGITS(r); + +#ifdef ECL_THIRTY_TWO_BIT + u[11] = gf2m_SQR1(v[5]); + u[10] = gf2m_SQR0(v[5]); + u[9] = gf2m_SQR1(v[4]); + u[8] = gf2m_SQR0(v[4]); + u[7] = gf2m_SQR1(v[3]); + u[6] = gf2m_SQR0(v[3]); +#endif + u[5] = gf2m_SQR1(v[2]); + u[4] = gf2m_SQR0(v[2]); + u[3] = gf2m_SQR1(v[1]); + u[2] = gf2m_SQR0(v[1]); + u[1] = gf2m_SQR1(v[0]); + u[0] = gf2m_SQR0(v[0]); + return ec_GF2m_163_mod(r, r, meth); + + CLEANUP: + return res; +} + +/* Fast multiplication for polynomials over a 163-bit curve. Assumes + * reduction polynomial with terms {163, 7, 6, 3, 0}. */ +mp_err +ec_GF2m_163_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a2 = 0, a1 = 0, a0, b2 = 0, b1 = 0, b0; + +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a5 = 0, a4 = 0, a3 = 0, b5 = 0, b4 = 0, b3 = 0; + mp_digit rm[6]; +#endif + + if (a == b) { + return ec_GF2m_163_sqr(a, r, meth); + } else { + switch (MP_USED(a)) { +#ifdef ECL_THIRTY_TWO_BIT + case 6: + a5 = MP_DIGIT(a, 5); + case 5: + a4 = MP_DIGIT(a, 4); + case 4: + a3 = MP_DIGIT(a, 3); +#endif + case 3: + a2 = MP_DIGIT(a, 2); + case 2: + a1 = MP_DIGIT(a, 1); + default: + a0 = MP_DIGIT(a, 0); + } + switch (MP_USED(b)) { +#ifdef ECL_THIRTY_TWO_BIT + case 6: + b5 = MP_DIGIT(b, 5); + case 5: + b4 = MP_DIGIT(b, 4); + case 4: + b3 = MP_DIGIT(b, 3); +#endif + case 3: + b2 = MP_DIGIT(b, 2); + case 2: + b1 = MP_DIGIT(b, 1); + default: + b0 = MP_DIGIT(b, 0); + } +#ifdef ECL_SIXTY_FOUR_BIT + MP_CHECKOK(s_mp_pad(r, 6)); + s_bmul_3x3(MP_DIGITS(r), a2, a1, a0, b2, b1, b0); + MP_USED(r) = 6; + s_mp_clamp(r); +#else + MP_CHECKOK(s_mp_pad(r, 12)); + s_bmul_3x3(MP_DIGITS(r) + 6, a5, a4, a3, b5, b4, b3); + s_bmul_3x3(MP_DIGITS(r), a2, a1, a0, b2, b1, b0); + s_bmul_3x3(rm, a5 ^ a2, a4 ^ a1, a3 ^ a0, b5 ^ b2, b4 ^ b1, + b3 ^ b0); + rm[5] ^= MP_DIGIT(r, 5) ^ MP_DIGIT(r, 11); + rm[4] ^= MP_DIGIT(r, 4) ^ MP_DIGIT(r, 10); + rm[3] ^= MP_DIGIT(r, 3) ^ MP_DIGIT(r, 9); + rm[2] ^= MP_DIGIT(r, 2) ^ MP_DIGIT(r, 8); + rm[1] ^= MP_DIGIT(r, 1) ^ MP_DIGIT(r, 7); + rm[0] ^= MP_DIGIT(r, 0) ^ MP_DIGIT(r, 6); + MP_DIGIT(r, 8) ^= rm[5]; + MP_DIGIT(r, 7) ^= rm[4]; + MP_DIGIT(r, 6) ^= rm[3]; + MP_DIGIT(r, 5) ^= rm[2]; + MP_DIGIT(r, 4) ^= rm[1]; + MP_DIGIT(r, 3) ^= rm[0]; + MP_USED(r) = 12; + s_mp_clamp(r); +#endif + return ec_GF2m_163_mod(r, r, meth); + } + + CLEANUP: + return res; +} + +/* Wire in fast field arithmetic for 163-bit curves. */ +mp_err +ec_group_set_gf2m163(ECGroup *group, ECCurveName name) +{ + group->meth->field_mod = &ec_GF2m_163_mod; + group->meth->field_mul = &ec_GF2m_163_mul; + group->meth->field_sqr = &ec_GF2m_163_sqr; + return MP_OKAY; +} diff --git a/usr/src/common/crypto/ecc/ec2_193.c b/usr/src/common/crypto/ecc/ec2_193.c new file mode 100644 index 0000000000..d0b2e57399 --- /dev/null +++ b/usr/src/common/crypto/ecc/ec2_193.c @@ -0,0 +1,286 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for binary polynomial field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang-Shantz <sheueling.chang@sun.com>, + * Stephen Fung <fungstep@hotmail.com>, and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ec2.h" +#include "mp_gf2m.h" +#include "mp_gf2m-priv.h" +#include "mpi.h" +#include "mpi-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* Fast reduction for polynomials over a 193-bit curve. Assumes reduction + * polynomial with terms {193, 15, 0}. */ +mp_err +ec_GF2m_193_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, z; + + if (a != r) { + MP_CHECKOK(mp_copy(a, r)); + } +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(r) < 7) { + MP_CHECKOK(s_mp_pad(r, 7)); + } + u = MP_DIGITS(r); + MP_USED(r) = 7; + + /* u[6] only has 2 significant bits */ + z = u[6]; + u[3] ^= (z << 14) ^ (z >> 1); + u[2] ^= (z << 63); + z = u[5]; + u[3] ^= (z >> 50); + u[2] ^= (z << 14) ^ (z >> 1); + u[1] ^= (z << 63); + z = u[4]; + u[2] ^= (z >> 50); + u[1] ^= (z << 14) ^ (z >> 1); + u[0] ^= (z << 63); + z = u[3] >> 1; /* z only has 63 significant bits */ + u[1] ^= (z >> 49); + u[0] ^= (z << 15) ^ z; + /* clear bits above 193 */ + u[6] = u[5] = u[4] = 0; + u[3] ^= z << 1; +#else + if (MP_USED(r) < 13) { + MP_CHECKOK(s_mp_pad(r, 13)); + } + u = MP_DIGITS(r); + MP_USED(r) = 13; + + /* u[12] only has 2 significant bits */ + z = u[12]; + u[6] ^= (z << 14) ^ (z >> 1); + u[5] ^= (z << 31); + z = u[11]; + u[6] ^= (z >> 18); + u[5] ^= (z << 14) ^ (z >> 1); + u[4] ^= (z << 31); + z = u[10]; + u[5] ^= (z >> 18); + u[4] ^= (z << 14) ^ (z >> 1); + u[3] ^= (z << 31); + z = u[9]; + u[4] ^= (z >> 18); + u[3] ^= (z << 14) ^ (z >> 1); + u[2] ^= (z << 31); + z = u[8]; + u[3] ^= (z >> 18); + u[2] ^= (z << 14) ^ (z >> 1); + u[1] ^= (z << 31); + z = u[7]; + u[2] ^= (z >> 18); + u[1] ^= (z << 14) ^ (z >> 1); + u[0] ^= (z << 31); + z = u[6] >> 1; /* z only has 31 significant bits */ + u[1] ^= (z >> 17); + u[0] ^= (z << 15) ^ z; + /* clear bits above 193 */ + u[12] = u[11] = u[10] = u[9] = u[8] = u[7] = 0; + u[6] ^= z << 1; +#endif + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* Fast squaring for polynomials over a 193-bit curve. Assumes reduction + * polynomial with terms {193, 15, 0}. */ +mp_err +ec_GF2m_193_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, *v; + + v = MP_DIGITS(a); + +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(a) < 4) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 7) { + MP_CHECKOK(s_mp_pad(r, 7)); + } + MP_USED(r) = 7; +#else + if (MP_USED(a) < 7) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 13) { + MP_CHECKOK(s_mp_pad(r, 13)); + } + MP_USED(r) = 13; +#endif + u = MP_DIGITS(r); + +#ifdef ECL_THIRTY_TWO_BIT + u[12] = gf2m_SQR0(v[6]); + u[11] = gf2m_SQR1(v[5]); + u[10] = gf2m_SQR0(v[5]); + u[9] = gf2m_SQR1(v[4]); + u[8] = gf2m_SQR0(v[4]); + u[7] = gf2m_SQR1(v[3]); +#endif + u[6] = gf2m_SQR0(v[3]); + u[5] = gf2m_SQR1(v[2]); + u[4] = gf2m_SQR0(v[2]); + u[3] = gf2m_SQR1(v[1]); + u[2] = gf2m_SQR0(v[1]); + u[1] = gf2m_SQR1(v[0]); + u[0] = gf2m_SQR0(v[0]); + return ec_GF2m_193_mod(r, r, meth); + + CLEANUP: + return res; +} + +/* Fast multiplication for polynomials over a 193-bit curve. Assumes + * reduction polynomial with terms {193, 15, 0}. */ +mp_err +ec_GF2m_193_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a3 = 0, a2 = 0, a1 = 0, a0, b3 = 0, b2 = 0, b1 = 0, b0; + +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a6 = 0, a5 = 0, a4 = 0, b6 = 0, b5 = 0, b4 = 0; + mp_digit rm[8]; +#endif + + if (a == b) { + return ec_GF2m_193_sqr(a, r, meth); + } else { + switch (MP_USED(a)) { +#ifdef ECL_THIRTY_TWO_BIT + case 7: + a6 = MP_DIGIT(a, 6); + case 6: + a5 = MP_DIGIT(a, 5); + case 5: + a4 = MP_DIGIT(a, 4); +#endif + case 4: + a3 = MP_DIGIT(a, 3); + case 3: + a2 = MP_DIGIT(a, 2); + case 2: + a1 = MP_DIGIT(a, 1); + default: + a0 = MP_DIGIT(a, 0); + } + switch (MP_USED(b)) { +#ifdef ECL_THIRTY_TWO_BIT + case 7: + b6 = MP_DIGIT(b, 6); + case 6: + b5 = MP_DIGIT(b, 5); + case 5: + b4 = MP_DIGIT(b, 4); +#endif + case 4: + b3 = MP_DIGIT(b, 3); + case 3: + b2 = MP_DIGIT(b, 2); + case 2: + b1 = MP_DIGIT(b, 1); + default: + b0 = MP_DIGIT(b, 0); + } +#ifdef ECL_SIXTY_FOUR_BIT + MP_CHECKOK(s_mp_pad(r, 8)); + s_bmul_4x4(MP_DIGITS(r), a3, a2, a1, a0, b3, b2, b1, b0); + MP_USED(r) = 8; + s_mp_clamp(r); +#else + MP_CHECKOK(s_mp_pad(r, 14)); + s_bmul_3x3(MP_DIGITS(r) + 8, a6, a5, a4, b6, b5, b4); + s_bmul_4x4(MP_DIGITS(r), a3, a2, a1, a0, b3, b2, b1, b0); + s_bmul_4x4(rm, a3, a6 ^ a2, a5 ^ a1, a4 ^ a0, b3, b6 ^ b2, b5 ^ b1, + b4 ^ b0); + rm[7] ^= MP_DIGIT(r, 7); + rm[6] ^= MP_DIGIT(r, 6); + rm[5] ^= MP_DIGIT(r, 5) ^ MP_DIGIT(r, 13); + rm[4] ^= MP_DIGIT(r, 4) ^ MP_DIGIT(r, 12); + rm[3] ^= MP_DIGIT(r, 3) ^ MP_DIGIT(r, 11); + rm[2] ^= MP_DIGIT(r, 2) ^ MP_DIGIT(r, 10); + rm[1] ^= MP_DIGIT(r, 1) ^ MP_DIGIT(r, 9); + rm[0] ^= MP_DIGIT(r, 0) ^ MP_DIGIT(r, 8); + MP_DIGIT(r, 11) ^= rm[7]; + MP_DIGIT(r, 10) ^= rm[6]; + MP_DIGIT(r, 9) ^= rm[5]; + MP_DIGIT(r, 8) ^= rm[4]; + MP_DIGIT(r, 7) ^= rm[3]; + MP_DIGIT(r, 6) ^= rm[2]; + MP_DIGIT(r, 5) ^= rm[1]; + MP_DIGIT(r, 4) ^= rm[0]; + MP_USED(r) = 14; + s_mp_clamp(r); +#endif + return ec_GF2m_193_mod(r, r, meth); + } + + CLEANUP: + return res; +} + +/* Wire in fast field arithmetic for 193-bit curves. */ +mp_err +ec_group_set_gf2m193(ECGroup *group, ECCurveName name) +{ + group->meth->field_mod = &ec_GF2m_193_mod; + group->meth->field_mul = &ec_GF2m_193_mul; + group->meth->field_sqr = &ec_GF2m_193_sqr; + return MP_OKAY; +} diff --git a/usr/src/common/crypto/ecc/ec2_233.c b/usr/src/common/crypto/ecc/ec2_233.c new file mode 100644 index 0000000000..f7131f8641 --- /dev/null +++ b/usr/src/common/crypto/ecc/ec2_233.c @@ -0,0 +1,309 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for binary polynomial field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang-Shantz <sheueling.chang@sun.com>, + * Stephen Fung <fungstep@hotmail.com>, and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ec2.h" +#include "mp_gf2m.h" +#include "mp_gf2m-priv.h" +#include "mpi.h" +#include "mpi-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* Fast reduction for polynomials over a 233-bit curve. Assumes reduction + * polynomial with terms {233, 74, 0}. */ +mp_err +ec_GF2m_233_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, z; + + if (a != r) { + MP_CHECKOK(mp_copy(a, r)); + } +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(r) < 8) { + MP_CHECKOK(s_mp_pad(r, 8)); + } + u = MP_DIGITS(r); + MP_USED(r) = 8; + + /* u[7] only has 18 significant bits */ + z = u[7]; + u[4] ^= (z << 33) ^ (z >> 41); + u[3] ^= (z << 23); + z = u[6]; + u[4] ^= (z >> 31); + u[3] ^= (z << 33) ^ (z >> 41); + u[2] ^= (z << 23); + z = u[5]; + u[3] ^= (z >> 31); + u[2] ^= (z << 33) ^ (z >> 41); + u[1] ^= (z << 23); + z = u[4]; + u[2] ^= (z >> 31); + u[1] ^= (z << 33) ^ (z >> 41); + u[0] ^= (z << 23); + z = u[3] >> 41; /* z only has 23 significant bits */ + u[1] ^= (z << 10); + u[0] ^= z; + /* clear bits above 233 */ + u[7] = u[6] = u[5] = u[4] = 0; + u[3] ^= z << 41; +#else + if (MP_USED(r) < 15) { + MP_CHECKOK(s_mp_pad(r, 15)); + } + u = MP_DIGITS(r); + MP_USED(r) = 15; + + /* u[14] only has 18 significant bits */ + z = u[14]; + u[9] ^= (z << 1); + u[7] ^= (z >> 9); + u[6] ^= (z << 23); + z = u[13]; + u[9] ^= (z >> 31); + u[8] ^= (z << 1); + u[6] ^= (z >> 9); + u[5] ^= (z << 23); + z = u[12]; + u[8] ^= (z >> 31); + u[7] ^= (z << 1); + u[5] ^= (z >> 9); + u[4] ^= (z << 23); + z = u[11]; + u[7] ^= (z >> 31); + u[6] ^= (z << 1); + u[4] ^= (z >> 9); + u[3] ^= (z << 23); + z = u[10]; + u[6] ^= (z >> 31); + u[5] ^= (z << 1); + u[3] ^= (z >> 9); + u[2] ^= (z << 23); + z = u[9]; + u[5] ^= (z >> 31); + u[4] ^= (z << 1); + u[2] ^= (z >> 9); + u[1] ^= (z << 23); + z = u[8]; + u[4] ^= (z >> 31); + u[3] ^= (z << 1); + u[1] ^= (z >> 9); + u[0] ^= (z << 23); + z = u[7] >> 9; /* z only has 23 significant bits */ + u[3] ^= (z >> 22); + u[2] ^= (z << 10); + u[0] ^= z; + /* clear bits above 233 */ + u[14] = u[13] = u[12] = u[11] = u[10] = u[9] = u[8] = 0; + u[7] ^= z << 9; +#endif + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* Fast squaring for polynomials over a 233-bit curve. Assumes reduction + * polynomial with terms {233, 74, 0}. */ +mp_err +ec_GF2m_233_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, *v; + + v = MP_DIGITS(a); + +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(a) < 4) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 8) { + MP_CHECKOK(s_mp_pad(r, 8)); + } + MP_USED(r) = 8; +#else + if (MP_USED(a) < 8) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 15) { + MP_CHECKOK(s_mp_pad(r, 15)); + } + MP_USED(r) = 15; +#endif + u = MP_DIGITS(r); + +#ifdef ECL_THIRTY_TWO_BIT + u[14] = gf2m_SQR0(v[7]); + u[13] = gf2m_SQR1(v[6]); + u[12] = gf2m_SQR0(v[6]); + u[11] = gf2m_SQR1(v[5]); + u[10] = gf2m_SQR0(v[5]); + u[9] = gf2m_SQR1(v[4]); + u[8] = gf2m_SQR0(v[4]); +#endif + u[7] = gf2m_SQR1(v[3]); + u[6] = gf2m_SQR0(v[3]); + u[5] = gf2m_SQR1(v[2]); + u[4] = gf2m_SQR0(v[2]); + u[3] = gf2m_SQR1(v[1]); + u[2] = gf2m_SQR0(v[1]); + u[1] = gf2m_SQR1(v[0]); + u[0] = gf2m_SQR0(v[0]); + return ec_GF2m_233_mod(r, r, meth); + + CLEANUP: + return res; +} + +/* Fast multiplication for polynomials over a 233-bit curve. Assumes + * reduction polynomial with terms {233, 74, 0}. */ +mp_err +ec_GF2m_233_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a3 = 0, a2 = 0, a1 = 0, a0, b3 = 0, b2 = 0, b1 = 0, b0; + +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a7 = 0, a6 = 0, a5 = 0, a4 = 0, b7 = 0, b6 = 0, b5 = 0, b4 = + 0; + mp_digit rm[8]; +#endif + + if (a == b) { + return ec_GF2m_233_sqr(a, r, meth); + } else { + switch (MP_USED(a)) { +#ifdef ECL_THIRTY_TWO_BIT + case 8: + a7 = MP_DIGIT(a, 7); + case 7: + a6 = MP_DIGIT(a, 6); + case 6: + a5 = MP_DIGIT(a, 5); + case 5: + a4 = MP_DIGIT(a, 4); +#endif + case 4: + a3 = MP_DIGIT(a, 3); + case 3: + a2 = MP_DIGIT(a, 2); + case 2: + a1 = MP_DIGIT(a, 1); + default: + a0 = MP_DIGIT(a, 0); + } + switch (MP_USED(b)) { +#ifdef ECL_THIRTY_TWO_BIT + case 8: + b7 = MP_DIGIT(b, 7); + case 7: + b6 = MP_DIGIT(b, 6); + case 6: + b5 = MP_DIGIT(b, 5); + case 5: + b4 = MP_DIGIT(b, 4); +#endif + case 4: + b3 = MP_DIGIT(b, 3); + case 3: + b2 = MP_DIGIT(b, 2); + case 2: + b1 = MP_DIGIT(b, 1); + default: + b0 = MP_DIGIT(b, 0); + } +#ifdef ECL_SIXTY_FOUR_BIT + MP_CHECKOK(s_mp_pad(r, 8)); + s_bmul_4x4(MP_DIGITS(r), a3, a2, a1, a0, b3, b2, b1, b0); + MP_USED(r) = 8; + s_mp_clamp(r); +#else + MP_CHECKOK(s_mp_pad(r, 16)); + s_bmul_4x4(MP_DIGITS(r) + 8, a7, a6, a5, a4, b7, b6, b5, b4); + s_bmul_4x4(MP_DIGITS(r), a3, a2, a1, a0, b3, b2, b1, b0); + s_bmul_4x4(rm, a7 ^ a3, a6 ^ a2, a5 ^ a1, a4 ^ a0, b7 ^ b3, + b6 ^ b2, b5 ^ b1, b4 ^ b0); + rm[7] ^= MP_DIGIT(r, 7) ^ MP_DIGIT(r, 15); + rm[6] ^= MP_DIGIT(r, 6) ^ MP_DIGIT(r, 14); + rm[5] ^= MP_DIGIT(r, 5) ^ MP_DIGIT(r, 13); + rm[4] ^= MP_DIGIT(r, 4) ^ MP_DIGIT(r, 12); + rm[3] ^= MP_DIGIT(r, 3) ^ MP_DIGIT(r, 11); + rm[2] ^= MP_DIGIT(r, 2) ^ MP_DIGIT(r, 10); + rm[1] ^= MP_DIGIT(r, 1) ^ MP_DIGIT(r, 9); + rm[0] ^= MP_DIGIT(r, 0) ^ MP_DIGIT(r, 8); + MP_DIGIT(r, 11) ^= rm[7]; + MP_DIGIT(r, 10) ^= rm[6]; + MP_DIGIT(r, 9) ^= rm[5]; + MP_DIGIT(r, 8) ^= rm[4]; + MP_DIGIT(r, 7) ^= rm[3]; + MP_DIGIT(r, 6) ^= rm[2]; + MP_DIGIT(r, 5) ^= rm[1]; + MP_DIGIT(r, 4) ^= rm[0]; + MP_USED(r) = 16; + s_mp_clamp(r); +#endif + return ec_GF2m_233_mod(r, r, meth); + } + + CLEANUP: + return res; +} + +/* Wire in fast field arithmetic for 233-bit curves. */ +mp_err +ec_group_set_gf2m233(ECGroup *group, ECCurveName name) +{ + group->meth->field_mod = &ec_GF2m_233_mod; + group->meth->field_mul = &ec_GF2m_233_mul; + group->meth->field_sqr = &ec_GF2m_233_sqr; + return MP_OKAY; +} diff --git a/usr/src/common/crypto/ecc/ec2_aff.c b/usr/src/common/crypto/ecc/ec2_aff.c new file mode 100644 index 0000000000..67d2130b9f --- /dev/null +++ b/usr/src/common/crypto/ecc/ec2_aff.c @@ -0,0 +1,356 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for binary polynomial field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ec2.h" +#include "mplogic.h" +#include "mp_gf2m.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* Checks if point P(px, py) is at infinity. Uses affine coordinates. */ +mp_err +ec_GF2m_pt_is_inf_aff(const mp_int *px, const mp_int *py) +{ + + if ((mp_cmp_z(px) == 0) && (mp_cmp_z(py) == 0)) { + return MP_YES; + } else { + return MP_NO; + } + +} + +/* Sets P(px, py) to be the point at infinity. Uses affine coordinates. */ +mp_err +ec_GF2m_pt_set_inf_aff(mp_int *px, mp_int *py) +{ + mp_zero(px); + mp_zero(py); + return MP_OKAY; +} + +/* Computes R = P + Q based on IEEE P1363 A.10.2. Elliptic curve points P, + * Q, and R can all be identical. Uses affine coordinates. */ +mp_err +ec_GF2m_pt_add_aff(const mp_int *px, const mp_int *py, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int lambda, tempx, tempy; + + MP_DIGITS(&lambda) = 0; + MP_DIGITS(&tempx) = 0; + MP_DIGITS(&tempy) = 0; + MP_CHECKOK(mp_init(&lambda, FLAG(px))); + MP_CHECKOK(mp_init(&tempx, FLAG(px))); + MP_CHECKOK(mp_init(&tempy, FLAG(px))); + /* if P = inf, then R = Q */ + if (ec_GF2m_pt_is_inf_aff(px, py) == 0) { + MP_CHECKOK(mp_copy(qx, rx)); + MP_CHECKOK(mp_copy(qy, ry)); + res = MP_OKAY; + goto CLEANUP; + } + /* if Q = inf, then R = P */ + if (ec_GF2m_pt_is_inf_aff(qx, qy) == 0) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + res = MP_OKAY; + goto CLEANUP; + } + /* if px != qx, then lambda = (py+qy) / (px+qx), tempx = a + lambda^2 + * + lambda + px + qx */ + if (mp_cmp(px, qx) != 0) { + MP_CHECKOK(group->meth->field_add(py, qy, &tempy, group->meth)); + MP_CHECKOK(group->meth->field_add(px, qx, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_div(&tempy, &tempx, &lambda, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&lambda, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, &lambda, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, &group->curvea, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, px, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, qx, &tempx, group->meth)); + } else { + /* if py != qy or qx = 0, then R = inf */ + if (((mp_cmp(py, qy) != 0)) || (mp_cmp_z(qx) == 0)) { + mp_zero(rx); + mp_zero(ry); + res = MP_OKAY; + goto CLEANUP; + } + /* lambda = qx + qy / qx */ + MP_CHECKOK(group->meth->field_div(qy, qx, &lambda, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&lambda, qx, &lambda, group->meth)); + /* tempx = a + lambda^2 + lambda */ + MP_CHECKOK(group->meth->field_sqr(&lambda, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, &lambda, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, &group->curvea, &tempx, group->meth)); + } + /* ry = (qx + tempx) * lambda + tempx + qy */ + MP_CHECKOK(group->meth->field_add(qx, &tempx, &tempy, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(&tempy, &lambda, &tempy, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempy, &tempx, &tempy, group->meth)); + MP_CHECKOK(group->meth->field_add(&tempy, qy, ry, group->meth)); + /* rx = tempx */ + MP_CHECKOK(mp_copy(&tempx, rx)); + + CLEANUP: + mp_clear(&lambda); + mp_clear(&tempx); + mp_clear(&tempy); + return res; +} + +/* Computes R = P - Q. Elliptic curve points P, Q, and R can all be + * identical. Uses affine coordinates. */ +mp_err +ec_GF2m_pt_sub_aff(const mp_int *px, const mp_int *py, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int nqy; + + MP_DIGITS(&nqy) = 0; + MP_CHECKOK(mp_init(&nqy, FLAG(px))); + /* nqy = qx+qy */ + MP_CHECKOK(group->meth->field_add(qx, qy, &nqy, group->meth)); + MP_CHECKOK(group->point_add(px, py, qx, &nqy, rx, ry, group)); + CLEANUP: + mp_clear(&nqy); + return res; +} + +/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses + * affine coordinates. */ +mp_err +ec_GF2m_pt_dbl_aff(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group) +{ + return group->point_add(px, py, px, py, rx, ry, group); +} + +/* by default, this routine is unused and thus doesn't need to be compiled */ +#ifdef ECL_ENABLE_GF2M_PT_MUL_AFF +/* Computes R = nP based on IEEE P1363 A.10.3. Elliptic curve points P and + * R can be identical. Uses affine coordinates. */ +mp_err +ec_GF2m_pt_mul_aff(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int k, k3, qx, qy, sx, sy; + int b1, b3, i, l; + + MP_DIGITS(&k) = 0; + MP_DIGITS(&k3) = 0; + MP_DIGITS(&qx) = 0; + MP_DIGITS(&qy) = 0; + MP_DIGITS(&sx) = 0; + MP_DIGITS(&sy) = 0; + MP_CHECKOK(mp_init(&k)); + MP_CHECKOK(mp_init(&k3)); + MP_CHECKOK(mp_init(&qx)); + MP_CHECKOK(mp_init(&qy)); + MP_CHECKOK(mp_init(&sx)); + MP_CHECKOK(mp_init(&sy)); + + /* if n = 0 then r = inf */ + if (mp_cmp_z(n) == 0) { + mp_zero(rx); + mp_zero(ry); + res = MP_OKAY; + goto CLEANUP; + } + /* Q = P, k = n */ + MP_CHECKOK(mp_copy(px, &qx)); + MP_CHECKOK(mp_copy(py, &qy)); + MP_CHECKOK(mp_copy(n, &k)); + /* if n < 0 then Q = -Q, k = -k */ + if (mp_cmp_z(n) < 0) { + MP_CHECKOK(group->meth->field_add(&qx, &qy, &qy, group->meth)); + MP_CHECKOK(mp_neg(&k, &k)); + } +#ifdef ECL_DEBUG /* basic double and add method */ + l = mpl_significant_bits(&k) - 1; + MP_CHECKOK(mp_copy(&qx, &sx)); + MP_CHECKOK(mp_copy(&qy, &sy)); + for (i = l - 1; i >= 0; i--) { + /* S = 2S */ + MP_CHECKOK(group->point_dbl(&sx, &sy, &sx, &sy, group)); + /* if k_i = 1, then S = S + Q */ + if (mpl_get_bit(&k, i) != 0) { + MP_CHECKOK(group-> + point_add(&sx, &sy, &qx, &qy, &sx, &sy, group)); + } + } +#else /* double and add/subtract method from + * standard */ + /* k3 = 3 * k */ + MP_CHECKOK(mp_set_int(&k3, 3)); + MP_CHECKOK(mp_mul(&k, &k3, &k3)); + /* S = Q */ + MP_CHECKOK(mp_copy(&qx, &sx)); + MP_CHECKOK(mp_copy(&qy, &sy)); + /* l = index of high order bit in binary representation of 3*k */ + l = mpl_significant_bits(&k3) - 1; + /* for i = l-1 downto 1 */ + for (i = l - 1; i >= 1; i--) { + /* S = 2S */ + MP_CHECKOK(group->point_dbl(&sx, &sy, &sx, &sy, group)); + b3 = MP_GET_BIT(&k3, i); + b1 = MP_GET_BIT(&k, i); + /* if k3_i = 1 and k_i = 0, then S = S + Q */ + if ((b3 == 1) && (b1 == 0)) { + MP_CHECKOK(group-> + point_add(&sx, &sy, &qx, &qy, &sx, &sy, group)); + /* if k3_i = 0 and k_i = 1, then S = S - Q */ + } else if ((b3 == 0) && (b1 == 1)) { + MP_CHECKOK(group-> + point_sub(&sx, &sy, &qx, &qy, &sx, &sy, group)); + } + } +#endif + /* output S */ + MP_CHECKOK(mp_copy(&sx, rx)); + MP_CHECKOK(mp_copy(&sy, ry)); + + CLEANUP: + mp_clear(&k); + mp_clear(&k3); + mp_clear(&qx); + mp_clear(&qy); + mp_clear(&sx); + mp_clear(&sy); + return res; +} +#endif + +/* Validates a point on a GF2m curve. */ +mp_err +ec_GF2m_validate_point(const mp_int *px, const mp_int *py, const ECGroup *group) +{ + mp_err res = MP_NO; + mp_int accl, accr, tmp, pxt, pyt; + + MP_DIGITS(&accl) = 0; + MP_DIGITS(&accr) = 0; + MP_DIGITS(&tmp) = 0; + MP_DIGITS(&pxt) = 0; + MP_DIGITS(&pyt) = 0; + MP_CHECKOK(mp_init(&accl, FLAG(px))); + MP_CHECKOK(mp_init(&accr, FLAG(px))); + MP_CHECKOK(mp_init(&tmp, FLAG(px))); + MP_CHECKOK(mp_init(&pxt, FLAG(px))); + MP_CHECKOK(mp_init(&pyt, FLAG(px))); + + /* 1: Verify that publicValue is not the point at infinity */ + if (ec_GF2m_pt_is_inf_aff(px, py) == MP_YES) { + res = MP_NO; + goto CLEANUP; + } + /* 2: Verify that the coordinates of publicValue are elements + * of the field. + */ + if ((MP_SIGN(px) == MP_NEG) || (mp_cmp(px, &group->meth->irr) >= 0) || + (MP_SIGN(py) == MP_NEG) || (mp_cmp(py, &group->meth->irr) >= 0)) { + res = MP_NO; + goto CLEANUP; + } + /* 3: Verify that publicValue is on the curve. */ + if (group->meth->field_enc) { + group->meth->field_enc(px, &pxt, group->meth); + group->meth->field_enc(py, &pyt, group->meth); + } else { + mp_copy(px, &pxt); + mp_copy(py, &pyt); + } + /* left-hand side: y^2 + x*y */ + MP_CHECKOK( group->meth->field_sqr(&pyt, &accl, group->meth) ); + MP_CHECKOK( group->meth->field_mul(&pxt, &pyt, &tmp, group->meth) ); + MP_CHECKOK( group->meth->field_add(&accl, &tmp, &accl, group->meth) ); + /* right-hand side: x^3 + a*x^2 + b */ + MP_CHECKOK( group->meth->field_sqr(&pxt, &tmp, group->meth) ); + MP_CHECKOK( group->meth->field_mul(&pxt, &tmp, &accr, group->meth) ); + MP_CHECKOK( group->meth->field_mul(&group->curvea, &tmp, &tmp, group->meth) ); + MP_CHECKOK( group->meth->field_add(&tmp, &accr, &accr, group->meth) ); + MP_CHECKOK( group->meth->field_add(&accr, &group->curveb, &accr, group->meth) ); + /* check LHS - RHS == 0 */ + MP_CHECKOK( group->meth->field_add(&accl, &accr, &accr, group->meth) ); + if (mp_cmp_z(&accr) != 0) { + res = MP_NO; + goto CLEANUP; + } + /* 4: Verify that the order of the curve times the publicValue + * is the point at infinity. + */ + MP_CHECKOK( ECPoint_mul(group, &group->order, px, py, &pxt, &pyt) ); + if (ec_GF2m_pt_is_inf_aff(&pxt, &pyt) != MP_YES) { + res = MP_NO; + goto CLEANUP; + } + + res = MP_YES; + +CLEANUP: + mp_clear(&accl); + mp_clear(&accr); + mp_clear(&tmp); + mp_clear(&pxt); + mp_clear(&pyt); + return res; +} diff --git a/usr/src/common/crypto/ecc/ec2_mont.c b/usr/src/common/crypto/ecc/ec2_mont.c new file mode 100644 index 0000000000..e908708618 --- /dev/null +++ b/usr/src/common/crypto/ecc/ec2_mont.c @@ -0,0 +1,284 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for binary polynomial field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang-Shantz <sheueling.chang@sun.com>, + * Stephen Fung <fungstep@hotmail.com>, and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ec2.h" +#include "mplogic.h" +#include "mp_gf2m.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* Compute the x-coordinate x/z for the point 2*(x/z) in Montgomery + * projective coordinates. Uses algorithm Mdouble in appendix of Lopez, J. + * and Dahab, R. "Fast multiplication on elliptic curves over GF(2^m) + * without precomputation". modified to not require precomputation of + * c=b^{2^{m-1}}. */ +static mp_err +gf2m_Mdouble(mp_int *x, mp_int *z, const ECGroup *group, int kmflag) +{ + mp_err res = MP_OKAY; + mp_int t1; + + MP_DIGITS(&t1) = 0; + MP_CHECKOK(mp_init(&t1, kmflag)); + + MP_CHECKOK(group->meth->field_sqr(x, x, group->meth)); + MP_CHECKOK(group->meth->field_sqr(z, &t1, group->meth)); + MP_CHECKOK(group->meth->field_mul(x, &t1, z, group->meth)); + MP_CHECKOK(group->meth->field_sqr(x, x, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&t1, &t1, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(&group->curveb, &t1, &t1, group->meth)); + MP_CHECKOK(group->meth->field_add(x, &t1, x, group->meth)); + + CLEANUP: + mp_clear(&t1); + return res; +} + +/* Compute the x-coordinate x1/z1 for the point (x1/z1)+(x2/x2) in + * Montgomery projective coordinates. Uses algorithm Madd in appendix of + * Lopex, J. and Dahab, R. "Fast multiplication on elliptic curves over + * GF(2^m) without precomputation". */ +static mp_err +gf2m_Madd(const mp_int *x, mp_int *x1, mp_int *z1, mp_int *x2, mp_int *z2, + const ECGroup *group, int kmflag) +{ + mp_err res = MP_OKAY; + mp_int t1, t2; + + MP_DIGITS(&t1) = 0; + MP_DIGITS(&t2) = 0; + MP_CHECKOK(mp_init(&t1, kmflag)); + MP_CHECKOK(mp_init(&t2, kmflag)); + + MP_CHECKOK(mp_copy(x, &t1)); + MP_CHECKOK(group->meth->field_mul(x1, z2, x1, group->meth)); + MP_CHECKOK(group->meth->field_mul(z1, x2, z1, group->meth)); + MP_CHECKOK(group->meth->field_mul(x1, z1, &t2, group->meth)); + MP_CHECKOK(group->meth->field_add(z1, x1, z1, group->meth)); + MP_CHECKOK(group->meth->field_sqr(z1, z1, group->meth)); + MP_CHECKOK(group->meth->field_mul(z1, &t1, x1, group->meth)); + MP_CHECKOK(group->meth->field_add(x1, &t2, x1, group->meth)); + + CLEANUP: + mp_clear(&t1); + mp_clear(&t2); + return res; +} + +/* Compute the x, y affine coordinates from the point (x1, z1) (x2, z2) + * using Montgomery point multiplication algorithm Mxy() in appendix of + * Lopex, J. and Dahab, R. "Fast multiplication on elliptic curves over + * GF(2^m) without precomputation". Returns: 0 on error 1 if return value + * should be the point at infinity 2 otherwise */ +static int +gf2m_Mxy(const mp_int *x, const mp_int *y, mp_int *x1, mp_int *z1, + mp_int *x2, mp_int *z2, const ECGroup *group) +{ + mp_err res = MP_OKAY; + int ret = 0; + mp_int t3, t4, t5; + + MP_DIGITS(&t3) = 0; + MP_DIGITS(&t4) = 0; + MP_DIGITS(&t5) = 0; + MP_CHECKOK(mp_init(&t3, FLAG(x2))); + MP_CHECKOK(mp_init(&t4, FLAG(x2))); + MP_CHECKOK(mp_init(&t5, FLAG(x2))); + + if (mp_cmp_z(z1) == 0) { + mp_zero(x2); + mp_zero(z2); + ret = 1; + goto CLEANUP; + } + + if (mp_cmp_z(z2) == 0) { + MP_CHECKOK(mp_copy(x, x2)); + MP_CHECKOK(group->meth->field_add(x, y, z2, group->meth)); + ret = 2; + goto CLEANUP; + } + + MP_CHECKOK(mp_set_int(&t5, 1)); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(&t5, &t5, group->meth)); + } + + MP_CHECKOK(group->meth->field_mul(z1, z2, &t3, group->meth)); + + MP_CHECKOK(group->meth->field_mul(z1, x, z1, group->meth)); + MP_CHECKOK(group->meth->field_add(z1, x1, z1, group->meth)); + MP_CHECKOK(group->meth->field_mul(z2, x, z2, group->meth)); + MP_CHECKOK(group->meth->field_mul(z2, x1, x1, group->meth)); + MP_CHECKOK(group->meth->field_add(z2, x2, z2, group->meth)); + + MP_CHECKOK(group->meth->field_mul(z2, z1, z2, group->meth)); + MP_CHECKOK(group->meth->field_sqr(x, &t4, group->meth)); + MP_CHECKOK(group->meth->field_add(&t4, y, &t4, group->meth)); + MP_CHECKOK(group->meth->field_mul(&t4, &t3, &t4, group->meth)); + MP_CHECKOK(group->meth->field_add(&t4, z2, &t4, group->meth)); + + MP_CHECKOK(group->meth->field_mul(&t3, x, &t3, group->meth)); + MP_CHECKOK(group->meth->field_div(&t5, &t3, &t3, group->meth)); + MP_CHECKOK(group->meth->field_mul(&t3, &t4, &t4, group->meth)); + MP_CHECKOK(group->meth->field_mul(x1, &t3, x2, group->meth)); + MP_CHECKOK(group->meth->field_add(x2, x, z2, group->meth)); + + MP_CHECKOK(group->meth->field_mul(z2, &t4, z2, group->meth)); + MP_CHECKOK(group->meth->field_add(z2, y, z2, group->meth)); + + ret = 2; + + CLEANUP: + mp_clear(&t3); + mp_clear(&t4); + mp_clear(&t5); + if (res == MP_OKAY) { + return ret; + } else { + return 0; + } +} + +/* Computes R = nP based on algorithm 2P of Lopex, J. and Dahab, R. "Fast + * multiplication on elliptic curves over GF(2^m) without + * precomputation". Elliptic curve points P and R can be identical. Uses + * Montgomery projective coordinates. */ +mp_err +ec_GF2m_pt_mul_mont(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int x1, x2, z1, z2; + int i, j; + mp_digit top_bit, mask; + + MP_DIGITS(&x1) = 0; + MP_DIGITS(&x2) = 0; + MP_DIGITS(&z1) = 0; + MP_DIGITS(&z2) = 0; + MP_CHECKOK(mp_init(&x1, FLAG(n))); + MP_CHECKOK(mp_init(&x2, FLAG(n))); + MP_CHECKOK(mp_init(&z1, FLAG(n))); + MP_CHECKOK(mp_init(&z2, FLAG(n))); + + /* if result should be point at infinity */ + if ((mp_cmp_z(n) == 0) || (ec_GF2m_pt_is_inf_aff(px, py) == MP_YES)) { + MP_CHECKOK(ec_GF2m_pt_set_inf_aff(rx, ry)); + goto CLEANUP; + } + + MP_CHECKOK(mp_copy(px, &x1)); /* x1 = px */ + MP_CHECKOK(mp_set_int(&z1, 1)); /* z1 = 1 */ + MP_CHECKOK(group->meth->field_sqr(&x1, &z2, group->meth)); /* z2 = + * x1^2 = + * px^2 */ + MP_CHECKOK(group->meth->field_sqr(&z2, &x2, group->meth)); + MP_CHECKOK(group->meth->field_add(&x2, &group->curveb, &x2, group->meth)); /* x2 + * = + * px^4 + * + + * b + */ + + /* find top-most bit and go one past it */ + i = MP_USED(n) - 1; + j = MP_DIGIT_BIT - 1; + top_bit = 1; + top_bit <<= MP_DIGIT_BIT - 1; + mask = top_bit; + while (!(MP_DIGITS(n)[i] & mask)) { + mask >>= 1; + j--; + } + mask >>= 1; + j--; + + /* if top most bit was at word break, go to next word */ + if (!mask) { + i--; + j = MP_DIGIT_BIT - 1; + mask = top_bit; + } + + for (; i >= 0; i--) { + for (; j >= 0; j--) { + if (MP_DIGITS(n)[i] & mask) { + MP_CHECKOK(gf2m_Madd(px, &x1, &z1, &x2, &z2, group, FLAG(n))); + MP_CHECKOK(gf2m_Mdouble(&x2, &z2, group, FLAG(n))); + } else { + MP_CHECKOK(gf2m_Madd(px, &x2, &z2, &x1, &z1, group, FLAG(n))); + MP_CHECKOK(gf2m_Mdouble(&x1, &z1, group, FLAG(n))); + } + mask >>= 1; + } + j = MP_DIGIT_BIT - 1; + mask = top_bit; + } + + /* convert out of "projective" coordinates */ + i = gf2m_Mxy(px, py, &x1, &z1, &x2, &z2, group); + if (i == 0) { + res = MP_BADARG; + goto CLEANUP; + } else if (i == 1) { + MP_CHECKOK(ec_GF2m_pt_set_inf_aff(rx, ry)); + } else { + MP_CHECKOK(mp_copy(&x2, rx)); + MP_CHECKOK(mp_copy(&z2, ry)); + } + + CLEANUP: + mp_clear(&x1); + mp_clear(&x2); + mp_clear(&z1); + mp_clear(&z2); + return res; +} diff --git a/usr/src/common/crypto/ecc/ec2_proj.c b/usr/src/common/crypto/ecc/ec2_proj.c new file mode 100644 index 0000000000..b07b84b19b --- /dev/null +++ b/usr/src/common/crypto/ecc/ec2_proj.c @@ -0,0 +1,379 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for binary polynomial field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang-Shantz <sheueling.chang@sun.com>, + * Stephen Fung <fungstep@hotmail.com>, and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ec2.h" +#include "mplogic.h" +#include "mp_gf2m.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif +#ifdef ECL_DEBUG +#include <assert.h> +#endif + +/* by default, these routines are unused and thus don't need to be compiled */ +#ifdef ECL_ENABLE_GF2M_PROJ +/* Converts a point P(px, py) from affine coordinates to projective + * coordinates R(rx, ry, rz). Assumes input is already field-encoded using + * field_enc, and returns output that is still field-encoded. */ +mp_err +ec_GF2m_pt_aff2proj(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + MP_CHECKOK(mp_set_int(rz, 1)); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(rz, rz, group->meth)); + } + CLEANUP: + return res; +} + +/* Converts a point P(px, py, pz) from projective coordinates to affine + * coordinates R(rx, ry). P and R can share x and y coordinates. Assumes + * input is already field-encoded using field_enc, and returns output that + * is still field-encoded. */ +mp_err +ec_GF2m_pt_proj2aff(const mp_int *px, const mp_int *py, const mp_int *pz, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int z1, z2; + + MP_DIGITS(&z1) = 0; + MP_DIGITS(&z2) = 0; + MP_CHECKOK(mp_init(&z1)); + MP_CHECKOK(mp_init(&z2)); + + /* if point at infinity, then set point at infinity and exit */ + if (ec_GF2m_pt_is_inf_proj(px, py, pz) == MP_YES) { + MP_CHECKOK(ec_GF2m_pt_set_inf_aff(rx, ry)); + goto CLEANUP; + } + + /* transform (px, py, pz) into (px / pz, py / pz^2) */ + if (mp_cmp_d(pz, 1) == 0) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + } else { + MP_CHECKOK(group->meth->field_div(NULL, pz, &z1, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&z1, &z2, group->meth)); + MP_CHECKOK(group->meth->field_mul(px, &z1, rx, group->meth)); + MP_CHECKOK(group->meth->field_mul(py, &z2, ry, group->meth)); + } + + CLEANUP: + mp_clear(&z1); + mp_clear(&z2); + return res; +} + +/* Checks if point P(px, py, pz) is at infinity. Uses projective + * coordinates. */ +mp_err +ec_GF2m_pt_is_inf_proj(const mp_int *px, const mp_int *py, + const mp_int *pz) +{ + return mp_cmp_z(pz); +} + +/* Sets P(px, py, pz) to be the point at infinity. Uses projective + * coordinates. */ +mp_err +ec_GF2m_pt_set_inf_proj(mp_int *px, mp_int *py, mp_int *pz) +{ + mp_zero(pz); + return MP_OKAY; +} + +/* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is + * (qx, qy, 1). Elliptic curve points P, Q, and R can all be identical. + * Uses mixed projective-affine coordinates. Assumes input is already + * field-encoded using field_enc, and returns output that is still + * field-encoded. Uses equation (3) from Hankerson, Hernandez, Menezes. + * Software Implementation of Elliptic Curve Cryptography Over Binary + * Fields. */ +mp_err +ec_GF2m_pt_add_proj(const mp_int *px, const mp_int *py, const mp_int *pz, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int A, B, C, D, E, F, G; + + /* If either P or Q is the point at infinity, then return the other + * point */ + if (ec_GF2m_pt_is_inf_proj(px, py, pz) == MP_YES) { + return ec_GF2m_pt_aff2proj(qx, qy, rx, ry, rz, group); + } + if (ec_GF2m_pt_is_inf_aff(qx, qy) == MP_YES) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + return mp_copy(pz, rz); + } + + MP_DIGITS(&A) = 0; + MP_DIGITS(&B) = 0; + MP_DIGITS(&C) = 0; + MP_DIGITS(&D) = 0; + MP_DIGITS(&E) = 0; + MP_DIGITS(&F) = 0; + MP_DIGITS(&G) = 0; + MP_CHECKOK(mp_init(&A)); + MP_CHECKOK(mp_init(&B)); + MP_CHECKOK(mp_init(&C)); + MP_CHECKOK(mp_init(&D)); + MP_CHECKOK(mp_init(&E)); + MP_CHECKOK(mp_init(&F)); + MP_CHECKOK(mp_init(&G)); + + /* D = pz^2 */ + MP_CHECKOK(group->meth->field_sqr(pz, &D, group->meth)); + + /* A = qy * pz^2 + py */ + MP_CHECKOK(group->meth->field_mul(qy, &D, &A, group->meth)); + MP_CHECKOK(group->meth->field_add(&A, py, &A, group->meth)); + + /* B = qx * pz + px */ + MP_CHECKOK(group->meth->field_mul(qx, pz, &B, group->meth)); + MP_CHECKOK(group->meth->field_add(&B, px, &B, group->meth)); + + /* C = pz * B */ + MP_CHECKOK(group->meth->field_mul(pz, &B, &C, group->meth)); + + /* D = B^2 * (C + a * pz^2) (using E as a temporary variable) */ + MP_CHECKOK(group->meth-> + field_mul(&group->curvea, &D, &D, group->meth)); + MP_CHECKOK(group->meth->field_add(&C, &D, &D, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&B, &E, group->meth)); + MP_CHECKOK(group->meth->field_mul(&E, &D, &D, group->meth)); + + /* rz = C^2 */ + MP_CHECKOK(group->meth->field_sqr(&C, rz, group->meth)); + + /* E = A * C */ + MP_CHECKOK(group->meth->field_mul(&A, &C, &E, group->meth)); + + /* rx = A^2 + D + E */ + MP_CHECKOK(group->meth->field_sqr(&A, rx, group->meth)); + MP_CHECKOK(group->meth->field_add(rx, &D, rx, group->meth)); + MP_CHECKOK(group->meth->field_add(rx, &E, rx, group->meth)); + + /* F = rx + qx * rz */ + MP_CHECKOK(group->meth->field_mul(qx, rz, &F, group->meth)); + MP_CHECKOK(group->meth->field_add(rx, &F, &F, group->meth)); + + /* G = rx + qy * rz */ + MP_CHECKOK(group->meth->field_mul(qy, rz, &G, group->meth)); + MP_CHECKOK(group->meth->field_add(rx, &G, &G, group->meth)); + + /* ry = E * F + rz * G (using G as a temporary variable) */ + MP_CHECKOK(group->meth->field_mul(rz, &G, &G, group->meth)); + MP_CHECKOK(group->meth->field_mul(&E, &F, ry, group->meth)); + MP_CHECKOK(group->meth->field_add(ry, &G, ry, group->meth)); + + CLEANUP: + mp_clear(&A); + mp_clear(&B); + mp_clear(&C); + mp_clear(&D); + mp_clear(&E); + mp_clear(&F); + mp_clear(&G); + return res; +} + +/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses + * projective coordinates. + * + * Assumes input is already field-encoded using field_enc, and returns + * output that is still field-encoded. + * + * Uses equation (3) from Hankerson, Hernandez, Menezes. Software + * Implementation of Elliptic Curve Cryptography Over Binary Fields. + */ +mp_err +ec_GF2m_pt_dbl_proj(const mp_int *px, const mp_int *py, const mp_int *pz, + mp_int *rx, mp_int *ry, mp_int *rz, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int t0, t1; + + if (ec_GF2m_pt_is_inf_proj(px, py, pz) == MP_YES) { + return ec_GF2m_pt_set_inf_proj(rx, ry, rz); + } + + MP_DIGITS(&t0) = 0; + MP_DIGITS(&t1) = 0; + MP_CHECKOK(mp_init(&t0)); + MP_CHECKOK(mp_init(&t1)); + + /* t0 = px^2 */ + /* t1 = pz^2 */ + MP_CHECKOK(group->meth->field_sqr(px, &t0, group->meth)); + MP_CHECKOK(group->meth->field_sqr(pz, &t1, group->meth)); + + /* rz = px^2 * pz^2 */ + MP_CHECKOK(group->meth->field_mul(&t0, &t1, rz, group->meth)); + + /* t0 = px^4 */ + /* t1 = b * pz^4 */ + MP_CHECKOK(group->meth->field_sqr(&t0, &t0, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&t1, &t1, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(&group->curveb, &t1, &t1, group->meth)); + + /* rx = px^4 + b * pz^4 */ + MP_CHECKOK(group->meth->field_add(&t0, &t1, rx, group->meth)); + + /* ry = b * pz^4 * rz + rx * (a * rz + py^2 + b * pz^4) */ + MP_CHECKOK(group->meth->field_sqr(py, ry, group->meth)); + MP_CHECKOK(group->meth->field_add(ry, &t1, ry, group->meth)); + /* t0 = a * rz */ + MP_CHECKOK(group->meth-> + field_mul(&group->curvea, rz, &t0, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, ry, ry, group->meth)); + MP_CHECKOK(group->meth->field_mul(rx, ry, ry, group->meth)); + /* t1 = b * pz^4 * rz */ + MP_CHECKOK(group->meth->field_mul(&t1, rz, &t1, group->meth)); + MP_CHECKOK(group->meth->field_add(&t1, ry, ry, group->meth)); + + CLEANUP: + mp_clear(&t0); + mp_clear(&t1); + return res; +} + +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GF2m. Elliptic curve points P and R can be + * identical. Uses mixed projective-affine coordinates. Assumes input is + * already field-encoded using field_enc, and returns output that is still + * field-encoded. Uses 4-bit window method. */ +mp_err +ec_GF2m_pt_mul_proj(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int precomp[16][2], rz; + mp_digit precomp_arr[ECL_MAX_FIELD_SIZE_DIGITS * 16 * 2], *t; + int i, ni, d; + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK((n != NULL) && (px != NULL) && (py != NULL), MP_BADARG); + + /* initialize precomputation table */ + t = precomp_arr; + for (i = 0; i < 16; i++) { + /* x co-ord */ + MP_SIGN(&precomp[i][0]) = MP_ZPOS; + MP_ALLOC(&precomp[i][0]) = ECL_MAX_FIELD_SIZE_DIGITS; + MP_USED(&precomp[i][0]) = 1; + *t = 0; + MP_DIGITS(&precomp[i][0]) = t; + t += ECL_MAX_FIELD_SIZE_DIGITS; + /* y co-ord */ + MP_SIGN(&precomp[i][1]) = MP_ZPOS; + MP_ALLOC(&precomp[i][1]) = ECL_MAX_FIELD_SIZE_DIGITS; + MP_USED(&precomp[i][1]) = 1; + *t = 0; + MP_DIGITS(&precomp[i][1]) = t; + t += ECL_MAX_FIELD_SIZE_DIGITS; + } + + /* fill precomputation table */ + mp_zero(&precomp[0][0]); + mp_zero(&precomp[0][1]); + MP_CHECKOK(mp_copy(px, &precomp[1][0])); + MP_CHECKOK(mp_copy(py, &precomp[1][1])); + for (i = 2; i < 16; i++) { + MP_CHECKOK(group-> + point_add(&precomp[1][0], &precomp[1][1], + &precomp[i - 1][0], &precomp[i - 1][1], + &precomp[i][0], &precomp[i][1], group)); + } + + d = (mpl_significant_bits(n) + 3) / 4; + + /* R = inf */ + MP_DIGITS(&rz) = 0; + MP_CHECKOK(mp_init(&rz)); + MP_CHECKOK(ec_GF2m_pt_set_inf_proj(rx, ry, &rz)); + + for (i = d - 1; i >= 0; i--) { + /* compute window ni */ + ni = MP_GET_BIT(n, 4 * i + 3); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i + 2); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i + 1); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i); + /* R = 2^4 * R */ + MP_CHECKOK(ec_GF2m_pt_dbl_proj(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GF2m_pt_dbl_proj(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GF2m_pt_dbl_proj(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GF2m_pt_dbl_proj(rx, ry, &rz, rx, ry, &rz, group)); + /* R = R + (ni * P) */ + MP_CHECKOK(ec_GF2m_pt_add_proj + (rx, ry, &rz, &precomp[ni][0], &precomp[ni][1], rx, ry, + &rz, group)); + } + + /* convert result S to affine coordinates */ + MP_CHECKOK(ec_GF2m_pt_proj2aff(rx, ry, &rz, rx, ry, group)); + + CLEANUP: + mp_clear(&rz); + return res; +} +#endif diff --git a/usr/src/common/crypto/ecc/ec2_test.c b/usr/src/common/crypto/ecc/ec2_test.c new file mode 100644 index 0000000000..e2b4bb558e --- /dev/null +++ b/usr/src/common/crypto/ecc/ec2_test.c @@ -0,0 +1,537 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for binary polynomial field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef _KERNEL +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/param.h> +#include <sys/modctl.h> +#include <sys/ddi.h> +#include <sys/crypto/spi.h> +#include <sys/sysmacros.h> +#include <sys/strsun.h> +#include <sys/md5.h> +#include <sys/sha1.h> +#include <sys/sha2.h> +#include <sys/random.h> +#include <sys/conf.h> +#include <sys/devops.h> +#include <sys/sunddi.h> +#include <sys/varargs.h> +#include <sys/kmem.h> +#include <sys/kstat.h> +#include <sys/crypto/common.h> +#else +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <assert.h> +#include <time.h> +#include <sys/time.h> +#include <sys/resource.h> +#endif /* _KERNEL */ + +#include "mpi.h" +#include "mplogic.h" +#include "mpprime.h" +#include "mp_gf2m.h" +#include "ecl.h" +#include "ecl-curve.h" +#include "ec2.h" +#include "ecc_impl.h" +#include "ec.h" + +#ifndef KM_SLEEP +#define KM_SLEEP 0 +#endif + +#ifndef _KERNEL +/* Time k repetitions of operation op. */ +#define M_TimeOperation(op, k) { \ + double dStart, dNow, dUserTime; \ + struct rusage ru; \ + int i; \ + getrusage(RUSAGE_SELF, &ru); \ + dStart = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + for (i = 0; i < k; i++) { \ + { op; } \ + }; \ + getrusage(RUSAGE_SELF, &ru); \ + dNow = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + dUserTime = dNow-dStart; \ + if (dUserTime) printf(" %-45s k: %6i, t: %6.2f sec\n", #op, k, dUserTime); \ +} +#else +#define M_TimeOperation(op, k) +#endif + +/* Test curve using generic field arithmetic. */ +#define ECTEST_GENERIC_GF2M(name_c, name) \ + printf("Testing %s using generic implementation...\n", name_c); \ + params = EC_GetNamedCurveParams(name, KM_SLEEP); \ + if (params == NULL) { \ + printf(" Error: could not construct params.\n"); \ + res = MP_NO; \ + goto CLEANUP; \ + } \ + ECGroup_free(group); \ + group = ECGroup_fromHex(params, KM_SLEEP); \ + if (group == NULL) { \ + printf(" Error: could not construct group.\n"); \ + res = MP_NO; \ + goto CLEANUP; \ + } \ + MP_CHECKOK( ectest_curve_GF2m(group, ectestPrint, ectestTime, 1, KM_SLEEP) ); \ + printf("... okay.\n"); + +/* Test curve using specific field arithmetic. */ +#define ECTEST_NAMED_GF2M(name_c, name) \ + printf("Testing %s using specific implementation...\n", name_c); \ + ECGroup_free(group); \ + group = ECGroup_fromName(name, KM_SLEEP); \ + if (group == NULL) { \ + printf(" Warning: could not construct group.\n"); \ + printf("... failed; continuing with remaining tests.\n"); \ + } else { \ + MP_CHECKOK( ectest_curve_GF2m(group, ectestPrint, ectestTime, 0, KM_SLEEP) ); \ + printf("... okay.\n"); \ + } + +/* Performs basic tests of elliptic curve cryptography over binary + * polynomial fields. If tests fail, then it prints an error message, + * aborts, and returns an error code. Otherwise, returns 0. */ +int +ectest_curve_GF2m(ECGroup *group, int ectestPrint, int ectestTime, + int generic, int kmflag) +{ + + mp_int one, order_1, gx, gy, rx, ry, n; + int size; + mp_err res; + char s[1000]; + + /* initialize values */ + MP_CHECKOK(mp_init(&one, kmflag)); + MP_CHECKOK(mp_init(&order_1, kmflag)); + MP_CHECKOK(mp_init(&gx, kmflag)); + MP_CHECKOK(mp_init(&gy, kmflag)); + MP_CHECKOK(mp_init(&rx, kmflag)); + MP_CHECKOK(mp_init(&ry, kmflag)); + MP_CHECKOK(mp_init(&n, kmflag)); + + MP_CHECKOK(mp_set_int(&one, 1)); + MP_CHECKOK(mp_sub(&group->order, &one, &order_1)); + + /* encode base point */ + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(&group->genx, &gx, group->meth)); + MP_CHECKOK(group->meth->field_dec(&group->geny, &gy, group->meth)); + } else { + MP_CHECKOK(mp_copy(&group->genx, &gx)); + MP_CHECKOK(mp_copy(&group->geny, &gy)); + } + + if (ectestPrint) { + /* output base point */ + printf(" base point P:\n"); + MP_CHECKOK(mp_toradix(&gx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&gy, s, 16)); + printf(" %s\n", s); + if (group->meth->field_enc) { + printf(" base point P (encoded):\n"); + MP_CHECKOK(mp_toradix(&group->genx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&group->geny, s, 16)); + printf(" %s\n", s); + } + } + +#ifdef ECL_ENABLE_GF2M_PT_MUL_AFF + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ec_GF2m_pt_mul_aff + (&order_1, &group->genx, &group->geny, &rx, &ry, group)); + if (ectestPrint) { + printf(" (order-1)*P (affine):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(group->meth->field_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &group->genx) != 0) + || (mp_cmp(&ry, &group->geny) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ec_GF2m_pt_mul_mont + (&order_1, &group->genx, &group->geny, &rx, &ry, group)); + if (ectestPrint) { + printf(" (order-1)*P (montgomery):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(group->meth->field_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &group->genx) != 0) + || (mp_cmp(&ry, &group->geny) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + +#ifdef ECL_ENABLE_GF2M_PROJ + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ec_GF2m_pt_mul_proj + (&order_1, &group->genx, &group->geny, &rx, &ry, group)); + if (ectestPrint) { + printf(" (order-1)*P (projective):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(group->meth->field_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &group->genx) != 0) + || (mp_cmp(&ry, &group->geny) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ECPoint_mul(group, &order_1, NULL, NULL, &rx, &ry)); + if (ectestPrint) { + printf(" (order-1)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(ec_GF2m_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ECPoint_mul(group, &order_1, &gx, &gy, &rx, &ry)); + if (ectestPrint) { + printf(" (order-1)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(ec_GF2m_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + +#ifdef ECL_ENABLE_GF2M_PT_MUL_AFF + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ec_GF2m_pt_mul_aff + (&group->order, &group->genx, &group->geny, &rx, &ry, + group)); + if (ectestPrint) { + printf(" (order)*P (affine):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GF2m_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ec_GF2m_pt_mul_mont + (&group->order, &group->genx, &group->geny, &rx, &ry, + group)); + if (ectestPrint) { + printf(" (order)*P (montgomery):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GF2m_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } + +#ifdef ECL_ENABLE_GF2M_PROJ + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ec_GF2m_pt_mul_proj + (&group->order, &group->genx, &group->geny, &rx, &ry, + group)); + if (ectestPrint) { + printf(" (order)*P (projective):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GF2m_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ECPoint_mul(group, &group->order, NULL, NULL, &rx, &ry)); + if (ectestPrint) { + printf(" (order)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GF2m_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ECPoint_mul(group, &group->order, &gx, &gy, &rx, &ry)); + if (ectestPrint) { + printf(" (order)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GF2m_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* check that (order-1)P + (order-1)P + P == (order-1)P */ + MP_CHECKOK(ECPoints_mul + (group, &order_1, &order_1, &gx, &gy, &rx, &ry)); + MP_CHECKOK(ECPoints_mul(group, &one, &one, &rx, &ry, &rx, &ry)); + if (ectestPrint) { + printf + (" (order-1)*P + (order-1)*P + P == (order-1)*P (ECPoints_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(ec_GF2m_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* test validate_point function */ + if (ECPoint_validate(group, &gx, &gy) != MP_YES) { + printf(" Error: validate point on base point failed.\n"); + res = MP_NO; + goto CLEANUP; + } + MP_CHECKOK(mp_add_d(&gy, 1, &ry)); + if (ECPoint_validate(group, &gx, &ry) != MP_NO) { + printf(" Error: validate point on invalid point passed.\n"); + res = MP_NO; + goto CLEANUP; + } + + if (ectestTime) { + /* compute random scalar */ + size = mpl_significant_bits(&group->meth->irr); + if (size < MP_OKAY) { + goto CLEANUP; + } + MP_CHECKOK(mpp_random_size(&n, (size + ECL_BITS - 1) / ECL_BITS)); + MP_CHECKOK(group->meth->field_mod(&n, &n, group->meth)); + /* timed test */ + if (generic) { +#ifdef ECL_ENABLE_GF2M_PT_MUL_AFF + M_TimeOperation(MP_CHECKOK + (ec_GF2m_pt_mul_aff + (&n, &group->genx, &group->geny, &rx, &ry, + group)), 100); +#endif + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, NULL, NULL, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoints_mul + (group, &n, &n, &gx, &gy, &rx, &ry)), 100); + } else { + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, NULL, NULL, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, &gx, &gy, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoints_mul + (group, &n, &n, &gx, &gy, &rx, &ry)), 100); + } + } + + CLEANUP: + mp_clear(&one); + mp_clear(&order_1); + mp_clear(&gx); + mp_clear(&gy); + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&n); + if (res != MP_OKAY) { +#ifdef _KERNEL + printf(" Error: exiting with error value 0x%x\n", res); +#else + printf(" Error: exiting with error value %i\n", res); +#endif + } + return res; +} + +/* Performs tests of elliptic curve cryptography over binary polynomial + * fields. If tests fail, then it prints an error message, aborts, and + * returns an error code. Otherwise, returns 0. */ +int +ec2_test() +{ + int ectestTime = 0; + int ectestPrint = 0; + int i; + ECGroup *group = NULL; + ECCurveParams *params = NULL; + mp_err res; + + /* generic arithmetic tests */ + ECTEST_GENERIC_GF2M("SECT-131R1", ECCurve_SECG_CHAR2_131R1); + + /* specific arithmetic tests */ + ECTEST_NAMED_GF2M("NIST-K163", ECCurve_NIST_K163); + ECTEST_NAMED_GF2M("NIST-B163", ECCurve_NIST_B163); + ECTEST_NAMED_GF2M("NIST-K233", ECCurve_NIST_K233); + ECTEST_NAMED_GF2M("NIST-B233", ECCurve_NIST_B233); + ECTEST_NAMED_GF2M("NIST-K283", ECCurve_NIST_K283); + ECTEST_NAMED_GF2M("NIST-B283", ECCurve_NIST_B283); + ECTEST_NAMED_GF2M("NIST-K409", ECCurve_NIST_K409); + ECTEST_NAMED_GF2M("NIST-B409", ECCurve_NIST_B409); + ECTEST_NAMED_GF2M("NIST-K571", ECCurve_NIST_K571); + ECTEST_NAMED_GF2M("NIST-B571", ECCurve_NIST_B571); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB163V1", ECCurve_X9_62_CHAR2_PNB163V1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB163V2", ECCurve_X9_62_CHAR2_PNB163V2); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB163V3", ECCurve_X9_62_CHAR2_PNB163V3); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB176V1", ECCurve_X9_62_CHAR2_PNB176V1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB191V1", ECCurve_X9_62_CHAR2_TNB191V1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB191V2", ECCurve_X9_62_CHAR2_TNB191V2); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB191V3", ECCurve_X9_62_CHAR2_TNB191V3); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB208W1", ECCurve_X9_62_CHAR2_PNB208W1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB239V1", ECCurve_X9_62_CHAR2_TNB239V1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB239V2", ECCurve_X9_62_CHAR2_TNB239V2); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB239V3", ECCurve_X9_62_CHAR2_TNB239V3); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB272W1", ECCurve_X9_62_CHAR2_PNB272W1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB304W1", ECCurve_X9_62_CHAR2_PNB304W1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB359V1", ECCurve_X9_62_CHAR2_TNB359V1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB368W1", ECCurve_X9_62_CHAR2_PNB368W1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB431R1", ECCurve_X9_62_CHAR2_TNB431R1); + ECTEST_NAMED_GF2M("SECT-113R1", ECCurve_SECG_CHAR2_113R1); + ECTEST_NAMED_GF2M("SECT-113R2", ECCurve_SECG_CHAR2_113R2); + ECTEST_NAMED_GF2M("SECT-131R1", ECCurve_SECG_CHAR2_131R1); + ECTEST_NAMED_GF2M("SECT-131R2", ECCurve_SECG_CHAR2_131R2); + ECTEST_NAMED_GF2M("SECT-163K1", ECCurve_SECG_CHAR2_163K1); + ECTEST_NAMED_GF2M("SECT-163R1", ECCurve_SECG_CHAR2_163R1); + ECTEST_NAMED_GF2M("SECT-163R2", ECCurve_SECG_CHAR2_163R2); + ECTEST_NAMED_GF2M("SECT-193R1", ECCurve_SECG_CHAR2_193R1); + ECTEST_NAMED_GF2M("SECT-193R2", ECCurve_SECG_CHAR2_193R2); + ECTEST_NAMED_GF2M("SECT-233K1", ECCurve_SECG_CHAR2_233K1); + ECTEST_NAMED_GF2M("SECT-233R1", ECCurve_SECG_CHAR2_233R1); + ECTEST_NAMED_GF2M("SECT-239K1", ECCurve_SECG_CHAR2_239K1); + ECTEST_NAMED_GF2M("SECT-283K1", ECCurve_SECG_CHAR2_283K1); + ECTEST_NAMED_GF2M("SECT-283R1", ECCurve_SECG_CHAR2_283R1); + ECTEST_NAMED_GF2M("SECT-409K1", ECCurve_SECG_CHAR2_409K1); + ECTEST_NAMED_GF2M("SECT-409R1", ECCurve_SECG_CHAR2_409R1); + ECTEST_NAMED_GF2M("SECT-571K1", ECCurve_SECG_CHAR2_571K1); + ECTEST_NAMED_GF2M("SECT-571R1", ECCurve_SECG_CHAR2_571R1); + ECTEST_NAMED_GF2M("WTLS-1 (113)", ECCurve_WTLS_1); + ECTEST_NAMED_GF2M("WTLS-3 (163)", ECCurve_WTLS_3); + ECTEST_NAMED_GF2M("WTLS-4 (113)", ECCurve_WTLS_4); + ECTEST_NAMED_GF2M("WTLS-5 (163)", ECCurve_WTLS_5); + ECTEST_NAMED_GF2M("WTLS-10 (233)", ECCurve_WTLS_10); + ECTEST_NAMED_GF2M("WTLS-11 (233)", ECCurve_WTLS_11); + + CLEANUP: + EC_FreeCurveParams(params); + ECGroup_free(group); + if (res != MP_OKAY) { +#ifdef _KERNEL + printf("Error: exiting with error value 0x%x\n", res); +#else + printf("Error: exiting with error value %i\n", res); +#endif + } + return res; +} diff --git a/usr/src/common/crypto/ecc/ec_naf.c b/usr/src/common/crypto/ecc/ec_naf.c new file mode 100644 index 0000000000..f0fc6731b0 --- /dev/null +++ b/usr/src/common/crypto/ecc/ec_naf.c @@ -0,0 +1,111 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stephen Fung <fungstep@hotmail.com>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecl-priv.h" + +/* Returns 2^e as an integer. This is meant to be used for small powers of + * two. */ +int +ec_twoTo(int e) +{ + int a = 1; + int i; + + for (i = 0; i < e; i++) { + a *= 2; + } + return a; +} + +/* Computes the windowed non-adjacent-form (NAF) of a scalar. Out should + * be an array of signed char's to output to, bitsize should be the number + * of bits of out, in is the original scalar, and w is the window size. + * NAF is discussed in the paper: D. Hankerson, J. Hernandez and A. + * Menezes, "Software implementation of elliptic curve cryptography over + * binary fields", Proc. CHES 2000. */ +mp_err +ec_compute_wNAF(signed char *out, int bitsize, const mp_int *in, int w) +{ + mp_int k; + mp_err res = MP_OKAY; + int i, twowm1, mask; + + twowm1 = ec_twoTo(w - 1); + mask = 2 * twowm1 - 1; + + MP_DIGITS(&k) = 0; + MP_CHECKOK(mp_init_copy(&k, in)); + + i = 0; + /* Compute wNAF form */ + while (mp_cmp_z(&k) > 0) { + if (mp_isodd(&k)) { + out[i] = MP_DIGIT(&k, 0) & mask; + if (out[i] >= twowm1) + out[i] -= 2 * twowm1; + + /* Subtract off out[i]. Note mp_sub_d only works with + * unsigned digits */ + if (out[i] >= 0) { + mp_sub_d(&k, out[i], &k); + } else { + mp_add_d(&k, -(out[i]), &k); + } + } else { + out[i] = 0; + } + mp_div_2(&k, &k); + i++; + } + /* Zero out the remaining elements of the out array. */ + for (; i < bitsize + 1; i++) { + out[i] = 0; + } + CLEANUP: + mp_clear(&k); + return res; + +} diff --git a/usr/src/common/crypto/ecc/ecc_impl.h b/usr/src/common/crypto/ecc/ecc_impl.h new file mode 100644 index 0000000000..506267c4a8 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecc_impl.h @@ -0,0 +1,238 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta <vipul.gupta@sun.com> and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _ECC_IMPL_H +#define _ECC_IMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include "ecl-exp.h" +#ifndef _KERNEL +#include <security/cryptoki.h> +#include <security/pkcs11t.h> +#endif /* _KERNEL */ + +#define EC_MAX_DIGEST_LEN 1024 /* max digest that can be signed */ +#define EC_MAX_POINT_LEN 145 /* max len of DER encoded Q */ +#define EC_MAX_VALUE_LEN 72 /* max len of ANSI X9.62 private value d */ +#define EC_MAX_SIG_LEN 144 /* max signature len for supported curves */ +#define EC_MIN_KEY_LEN 112 /* min key length in bits */ +#define EC_MAX_KEY_LEN 571 /* max key length in bits */ +#define EC_MAX_OID_LEN 10 /* max length of OID buffer */ + +/* + * Various structures and definitions from NSS are here. + */ + +#ifdef _KERNEL +#define PORT_ArenaAlloc(a, n, f) kmem_alloc((n), (f)) +#define PORT_ArenaZAlloc(a, n, f) kmem_zalloc((n), (f)) +#define PORT_ArenaGrow(a, b, c, d) NULL +#define PORT_ZAlloc(n, f) kmem_zalloc((n), (f)) +#define PORT_Alloc(n, f) kmem_alloc((n), (f)) +#else +#define PORT_ArenaAlloc(a, n, f) malloc((n)) +#define PORT_ArenaZAlloc(a, n, f) calloc(1, (n)) +#define PORT_ArenaGrow(a, b, c, d) NULL +#define PORT_ZAlloc(n, f) calloc(1, (n)) +#define PORT_Alloc(n, f) malloc((n)) +#endif + +#define PORT_NewArena(b) (char *)12345 +#define PORT_ArenaMark(a) NULL +#define PORT_ArenaUnmark(a, b) +#define PORT_ArenaRelease(a, m) +#define PORT_FreeArena(a, b) +#define PORT_Strlen(s) strlen((s)) +#define PORT_SetError(e) + +#define PRBool boolean_t +#define PR_TRUE B_TRUE +#define PR_FALSE B_FALSE + +#ifdef _KERNEL +#define PORT_Assert ASSERT +#define PORT_Memcpy(t, f, l) bcopy((f), (t), (l)) +#else +#define PORT_Assert assert +#define PORT_Memcpy(t, f, l) memcpy((t), (f), (l)) +#endif + +#define CHECK_OK(func) if (func == NULL) goto cleanup +#define CHECK_SEC_OK(func) if (SECSuccess != (rv = func)) goto cleanup + +typedef enum { + siBuffer = 0, + siClearDataBuffer = 1, + siCipherDataBuffer = 2, + siDERCertBuffer = 3, + siEncodedCertBuffer = 4, + siDERNameBuffer = 5, + siEncodedNameBuffer = 6, + siAsciiNameString = 7, + siAsciiString = 8, + siDEROID = 9, + siUnsignedInteger = 10, + siUTCTime = 11, + siGeneralizedTime = 12 +} SECItemType; + +typedef struct SECItemStr SECItem; + +struct SECItemStr { + SECItemType type; + unsigned char *data; + unsigned int len; +}; + +typedef SECItem SECKEYECParams; + +typedef enum { ec_params_explicit, + ec_params_named +} ECParamsType; + +typedef enum { ec_field_GFp = 1, + ec_field_GF2m +} ECFieldType; + +struct ECFieldIDStr { + int size; /* field size in bits */ + ECFieldType type; + union { + SECItem prime; /* prime p for (GFp) */ + SECItem poly; /* irreducible binary polynomial for (GF2m) */ + } u; + int k1; /* first coefficient of pentanomial or + * the only coefficient of trinomial + */ + int k2; /* two remaining coefficients of pentanomial */ + int k3; +}; +typedef struct ECFieldIDStr ECFieldID; + +struct ECCurveStr { + SECItem a; /* contains octet stream encoding of + * field element (X9.62 section 4.3.3) + */ + SECItem b; + SECItem seed; +}; +typedef struct ECCurveStr ECCurve; + +typedef void PRArenaPool; + +struct ECParamsStr { + PRArenaPool * arena; + ECParamsType type; + ECFieldID fieldID; + ECCurve curve; + SECItem base; + SECItem order; + int cofactor; + SECItem DEREncoding; + ECCurveName name; + SECItem curveOID; +}; +typedef struct ECParamsStr ECParams; + +struct ECPublicKeyStr { + ECParams ecParams; + SECItem publicValue; /* elliptic curve point encoded as + * octet stream. + */ +}; +typedef struct ECPublicKeyStr ECPublicKey; + +struct ECPrivateKeyStr { + ECParams ecParams; + SECItem publicValue; /* encoded ec point */ + SECItem privateValue; /* private big integer */ + SECItem version; /* As per SEC 1, Appendix C, Section C.4 */ +}; +typedef struct ECPrivateKeyStr ECPrivateKey; + +typedef enum _SECStatus { + SECBufferTooSmall = -3, + SECWouldBlock = -2, + SECFailure = -1, + SECSuccess = 0 +} SECStatus; + +#ifdef _KERNEL +#define RNG_GenerateGlobalRandomBytes(p,l) ecc_knzero_random_generator((p), (l)) +#else +#define RNG_GenerateGlobalRandomBytes(p,l) soft_nzero_random_generator((p), (l)) +#endif +#define CHECK_MPI_OK(func) if (MP_OKAY > (err = func)) goto cleanup +#define MP_TO_SEC_ERROR(err) + +#define SECITEM_TO_MPINT(it, mp) \ + CHECK_MPI_OK(mp_read_unsigned_octets((mp), (it).data, (it).len)) + +extern int ecc_knzero_random_generator(uint8_t *, size_t); +extern ulong_t soft_nzero_random_generator(uint8_t *, ulong_t); + +extern SECStatus EC_DecodeParams(const SECItem *, ECParams **, int); +extern SECItem * SECITEM_AllocItem(PRArenaPool *, SECItem *, unsigned int, int); +extern SECStatus SECITEM_CopyItem(PRArenaPool *, SECItem *, const SECItem *, + int); +extern void SECITEM_FreeItem(SECItem *, boolean_t); +extern SECStatus EC_NewKey(ECParams *ecParams, ECPrivateKey **privKey, int); +extern SECStatus ECDSA_SignDigest(ECPrivateKey *, SECItem *, const SECItem *, + int); +extern SECStatus ECDSA_VerifyDigest(ECPublicKey *, const SECItem *, + const SECItem *, int); +extern SECStatus ECDH_Derive(SECItem *, ECParams *, SECItem *, boolean_t, + SECItem *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _ECC_IMPL_H */ diff --git a/usr/src/common/crypto/ecc/ecdecode.c b/usr/src/common/crypto/ecc/ecdecode.c new file mode 100644 index 0000000000..fd42ffa98e --- /dev/null +++ b/usr/src/common/crypto/ecc/ecdecode.c @@ -0,0 +1,610 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Elliptic Curve Cryptography library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta <vipul.gupta@sun.com> and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/param.h> +#ifdef _KERNEL +#include <sys/kmem.h> +#else +#include <string.h> +#endif +#include "ec.h" +#include "ecl-curve.h" +#include "ecc_impl.h" + +#define MAX_ECKEY_LEN 72 +#define SEC_ASN1_OBJECT_ID 0x06 + +/* + * Initializes a SECItem from a hexadecimal string + * + * Warning: This function ignores leading 00's, so any leading 00's + * in the hexadecimal string must be optional. + */ +static SECItem * +hexString2SECItem(PRArenaPool *arena, SECItem *item, const char *str, + int kmflag) +{ + int i = 0; + int byteval = 0; + int tmp = strlen(str); + + if ((tmp % 2) != 0) return NULL; + + /* skip leading 00's unless the hex string is "00" */ + while ((tmp > 2) && (str[0] == '0') && (str[1] == '0')) { + str += 2; + tmp -= 2; + } + + item->data = (unsigned char *) PORT_ArenaAlloc(arena, tmp/2, kmflag); + if (item->data == NULL) return NULL; + item->len = tmp/2; + + while (str[i]) { + if ((str[i] >= '0') && (str[i] <= '9')) + tmp = str[i] - '0'; + else if ((str[i] >= 'a') && (str[i] <= 'f')) + tmp = str[i] - 'a' + 10; + else if ((str[i] >= 'A') && (str[i] <= 'F')) + tmp = str[i] - 'A' + 10; + else + return NULL; + + byteval = byteval * 16 + tmp; + if ((i % 2) != 0) { + item->data[i/2] = byteval; + byteval = 0; + } + i++; + } + + return item; +} + +static SECStatus +gf_populate_params(ECCurveName name, ECFieldType field_type, ECParams *params, + int kmflag) +{ + SECStatus rv = SECFailure; + const ECCurveParams *curveParams; + /* 2 ['0'+'4'] + MAX_ECKEY_LEN * 2 [x,y] * 2 [hex string] + 1 ['\0'] */ + char genenc[3 + 2 * 2 * MAX_ECKEY_LEN]; + + if ((name < ECCurve_noName) || (name > ECCurve_pastLastCurve)) goto cleanup; + params->name = name; + curveParams = ecCurve_map[params->name]; + CHECK_OK(curveParams); + params->fieldID.size = curveParams->size; + params->fieldID.type = field_type; + if (field_type == ec_field_GFp) { + CHECK_OK(hexString2SECItem(NULL, ¶ms->fieldID.u.prime, + curveParams->irr, kmflag)); + } else { + CHECK_OK(hexString2SECItem(NULL, ¶ms->fieldID.u.poly, + curveParams->irr, kmflag)); + } + CHECK_OK(hexString2SECItem(NULL, ¶ms->curve.a, + curveParams->curvea, kmflag)); + CHECK_OK(hexString2SECItem(NULL, ¶ms->curve.b, + curveParams->curveb, kmflag)); + genenc[0] = '0'; + genenc[1] = '4'; + genenc[2] = '\0'; + strcat(genenc, curveParams->genx); + strcat(genenc, curveParams->geny); + CHECK_OK(hexString2SECItem(NULL, ¶ms->base, genenc, kmflag)); + CHECK_OK(hexString2SECItem(NULL, ¶ms->order, + curveParams->order, kmflag)); + params->cofactor = curveParams->cofactor; + + rv = SECSuccess; + +cleanup: + return rv; +} + +ECCurveName SECOID_FindOIDTag(const SECItem *); + +SECStatus +EC_FillParams(PRArenaPool *arena, const SECItem *encodedParams, + ECParams *params, int kmflag) +{ + SECStatus rv = SECFailure; + ECCurveName tag; + SECItem oid = { siBuffer, NULL, 0}; + +#if EC_DEBUG + int i; + + printf("Encoded params in EC_DecodeParams: "); + for (i = 0; i < encodedParams->len; i++) { + printf("%02x:", encodedParams->data[i]); + } + printf("\n"); +#endif + + if ((encodedParams->len != ANSI_X962_CURVE_OID_TOTAL_LEN) && + (encodedParams->len != SECG_CURVE_OID_TOTAL_LEN)) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); + return SECFailure; + }; + + oid.len = encodedParams->len - 2; + oid.data = encodedParams->data + 2; + if ((encodedParams->data[0] != SEC_ASN1_OBJECT_ID) || + ((tag = SECOID_FindOIDTag(&oid)) == ECCurve_noName)) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); + return SECFailure; + } + + params->arena = arena; + params->cofactor = 0; + params->type = ec_params_named; + params->name = ECCurve_noName; + + /* For named curves, fill out curveOID */ + params->curveOID.len = oid.len; + params->curveOID.data = (unsigned char *) PORT_ArenaAlloc(NULL, oid.len, + kmflag); + if (params->curveOID.data == NULL) goto cleanup; + memcpy(params->curveOID.data, oid.data, oid.len); + +#if EC_DEBUG + printf("Curve: %s\n", SECOID_FindOIDTagDescription(tag)); +#endif + + switch (tag) { + + /* Binary curves */ + + case ECCurve_X9_62_CHAR2_PNB163V1: + /* Populate params for c2pnb163v1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB163V1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_PNB163V2: + /* Populate params for c2pnb163v2 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB163V2, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_PNB163V3: + /* Populate params for c2pnb163v3 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB163V3, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_PNB176V1: + /* Populate params for c2pnb176v1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB176V1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_TNB191V1: + /* Populate params for c2tnb191v1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB191V1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_TNB191V2: + /* Populate params for c2tnb191v2 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB191V2, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_TNB191V3: + /* Populate params for c2tnb191v3 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB191V3, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_PNB208W1: + /* Populate params for c2pnb208w1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB208W1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_TNB239V1: + /* Populate params for c2tnb239v1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB239V1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_TNB239V2: + /* Populate params for c2tnb239v2 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB239V2, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_TNB239V3: + /* Populate params for c2tnb239v3 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB239V3, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_PNB272W1: + /* Populate params for c2pnb272w1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB272W1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_PNB304W1: + /* Populate params for c2pnb304w1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB304W1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_TNB359V1: + /* Populate params for c2tnb359v1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB359V1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_PNB368W1: + /* Populate params for c2pnb368w1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB368W1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_X9_62_CHAR2_TNB431R1: + /* Populate params for c2tnb431r1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB431R1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_113R1: + /* Populate params for sect113r1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_113R1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_113R2: + /* Populate params for sect113r2 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_113R2, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_131R1: + /* Populate params for sect131r1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_131R1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_131R2: + /* Populate params for sect131r2 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_131R2, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_163K1: + /* Populate params for sect163k1 + * (the NIST K-163 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_163K1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_163R1: + /* Populate params for sect163r1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_163R1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_163R2: + /* Populate params for sect163r2 + * (the NIST B-163 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_163R2, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_193R1: + /* Populate params for sect193r1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_193R1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_193R2: + /* Populate params for sect193r2 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_193R2, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_233K1: + /* Populate params for sect233k1 + * (the NIST K-233 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_233K1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_233R1: + /* Populate params for sect233r1 + * (the NIST B-233 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_233R1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_239K1: + /* Populate params for sect239k1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_239K1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_283K1: + /* Populate params for sect283k1 + * (the NIST K-283 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_283K1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_283R1: + /* Populate params for sect283r1 + * (the NIST B-283 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_283R1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_409K1: + /* Populate params for sect409k1 + * (the NIST K-409 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_409K1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_409R1: + /* Populate params for sect409r1 + * (the NIST B-409 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_409R1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_571K1: + /* Populate params for sect571k1 + * (the NIST K-571 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_571K1, ec_field_GF2m, + params, kmflag) ); + break; + + case ECCurve_SECG_CHAR2_571R1: + /* Populate params for sect571r1 + * (the NIST B-571 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_571R1, ec_field_GF2m, + params, kmflag) ); + break; + + /* Prime curves */ + + case ECCurve_X9_62_PRIME_192V1: + /* Populate params for prime192v1 aka secp192r1 + * (the NIST P-192 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_192V1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_X9_62_PRIME_192V2: + /* Populate params for prime192v2 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_192V2, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_X9_62_PRIME_192V3: + /* Populate params for prime192v3 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_192V3, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_X9_62_PRIME_239V1: + /* Populate params for prime239v1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_239V1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_X9_62_PRIME_239V2: + /* Populate params for prime239v2 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_239V2, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_X9_62_PRIME_239V3: + /* Populate params for prime239v3 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_239V3, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_X9_62_PRIME_256V1: + /* Populate params for prime256v1 aka secp256r1 + * (the NIST P-256 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_256V1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_112R1: + /* Populate params for secp112r1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_112R1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_112R2: + /* Populate params for secp112r2 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_112R2, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_128R1: + /* Populate params for secp128r1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_128R1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_128R2: + /* Populate params for secp128r2 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_128R2, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_160K1: + /* Populate params for secp160k1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_160K1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_160R1: + /* Populate params for secp160r1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_160R1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_160R2: + /* Populate params for secp160r1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_160R2, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_192K1: + /* Populate params for secp192k1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_192K1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_224K1: + /* Populate params for secp224k1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_224K1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_224R1: + /* Populate params for secp224r1 + * (the NIST P-224 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_224R1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_256K1: + /* Populate params for secp256k1 */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_256K1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_384R1: + /* Populate params for secp384r1 + * (the NIST P-384 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_384R1, ec_field_GFp, + params, kmflag) ); + break; + + case ECCurve_SECG_PRIME_521R1: + /* Populate params for secp521r1 + * (the NIST P-521 curve) + */ + CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_521R1, ec_field_GFp, + params, kmflag) ); + break; + + default: + break; + }; + +cleanup: + if (!params->cofactor) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); +#if EC_DEBUG + printf("Unrecognized curve, returning NULL params\n"); +#endif + } + + return rv; +} + +SECStatus +EC_DecodeParams(const SECItem *encodedParams, ECParams **ecparams, int kmflag) +{ + PRArenaPool *arena; + ECParams *params; + SECStatus rv = SECFailure; + + /* Initialize an arena for the ECParams structure */ + if (!(arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE))) + return SECFailure; + + params = (ECParams *)PORT_ArenaZAlloc(NULL, sizeof(ECParams), kmflag); + if (!params) { + PORT_FreeArena(NULL, B_TRUE); + return SECFailure; + } + + /* Copy the encoded params */ + SECITEM_AllocItem(arena, &(params->DEREncoding), encodedParams->len, + kmflag); + memcpy(params->DEREncoding.data, encodedParams->data, encodedParams->len); + + /* Fill out the rest of the ECParams structure based on + * the encoded params + */ + rv = EC_FillParams(NULL, encodedParams, params, kmflag); + if (rv == SECFailure) { + PORT_FreeArena(NULL, B_TRUE); + return SECFailure; + } else { + *ecparams = params;; + return SECSuccess; + } +} diff --git a/usr/src/common/crypto/ecc/ecl-curve.h b/usr/src/common/crypto/ecc/ecl-curve.h new file mode 100644 index 0000000000..58c4fe5b98 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecl-curve.h @@ -0,0 +1,698 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _ECL_CURVE_H +#define _ECL_CURVE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecl-exp.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* NIST prime curves */ +static const ECCurveParams ecCurve_NIST_P192 = { + "NIST-P192", ECField_GFp, 192, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", + "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", + "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", 1 +}; + +static const ECCurveParams ecCurve_NIST_P224 = { + "NIST-P224", ECField_GFp, 224, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", + "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", 1 +}; + +static const ECCurveParams ecCurve_NIST_P256 = { + "NIST-P256", ECField_GFp, 256, + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 1 +}; + +static const ECCurveParams ecCurve_NIST_P384 = { + "NIST-P384", ECField_GFp, 384, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", + "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", + 1 +}; + +static const ECCurveParams ecCurve_NIST_P521 = { + "NIST-P521", ECField_GFp, 521, + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", + "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", + 1 +}; + +/* NIST binary curves */ +static const ECCurveParams ecCurve_NIST_K163 = { + "NIST-K163", ECField_GF2m, 163, + "0800000000000000000000000000000000000000C9", + "000000000000000000000000000000000000000001", + "000000000000000000000000000000000000000001", + "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8", + "0289070FB05D38FF58321F2E800536D538CCDAA3D9", + "04000000000000000000020108A2E0CC0D99F8A5EF", 2 +}; + +static const ECCurveParams ecCurve_NIST_B163 = { + "NIST-B163", ECField_GF2m, 163, + "0800000000000000000000000000000000000000C9", + "000000000000000000000000000000000000000001", + "020A601907B8C953CA1481EB10512F78744A3205FD", + "03F0EBA16286A2D57EA0991168D4994637E8343E36", + "00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1", + "040000000000000000000292FE77E70C12A4234C33", 2 +}; + +static const ECCurveParams ecCurve_NIST_K233 = { + "NIST-K233", ECField_GF2m, 233, + "020000000000000000000000000000000000000004000000000000000001", + "000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000001", + "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126", + "01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3", + "008000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF", 4 +}; + +static const ECCurveParams ecCurve_NIST_B233 = { + "NIST-B233", ECField_GF2m, 233, + "020000000000000000000000000000000000000004000000000000000001", + "000000000000000000000000000000000000000000000000000000000001", + "0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD", + "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B", + "01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052", + "01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7", 2 +}; + +static const ECCurveParams ecCurve_NIST_K283 = { + "NIST-K283", ECField_GF2m, 283, + "0800000000000000000000000000000000000000000000000000000000000000000010A1", + "000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000001", + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836", + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259", + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61", 4 +}; + +static const ECCurveParams ecCurve_NIST_B283 = { + "NIST-B283", ECField_GF2m, 283, + "0800000000000000000000000000000000000000000000000000000000000000000010A1", + "000000000000000000000000000000000000000000000000000000000000000000000001", + "027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5", + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053", + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4", + "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307", 2 +}; + +static const ECCurveParams ecCurve_NIST_K409 = { + "NIST-K409", ECField_GF2m, 409, + "02000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000001", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746", + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B", + "007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF", 4 +}; + +static const ECCurveParams ecCurve_NIST_B409 = { + "NIST-B409", ECField_GF2m, 409, + "02000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000001", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F", + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7", + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706", + "010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173", 2 +}; + +static const ECCurveParams ecCurve_NIST_K571 = { + "NIST-K571", ECField_GF2m, 571, + "080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000425", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972", + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3", + "020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001", 4 +}; + +static const ECCurveParams ecCurve_NIST_B571 = { + "NIST-B571", ECField_GF2m, 571, + "080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000425", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A", + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19", + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B", + "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47", 2 +}; + +/* ANSI X9.62 prime curves */ +static const ECCurveParams ecCurve_X9_62_PRIME_192V2 = { + "X9.62 P-192V2", ECField_GFp, 192, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", + "CC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953", + "EEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A", + "6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15", + "FFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31", 1 +}; + +static const ECCurveParams ecCurve_X9_62_PRIME_192V3 = { + "X9.62 P-192V3", ECField_GFp, 192, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", + "22123DC2395A05CAA7423DAECCC94760A7D462256BD56916", + "7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896", + "38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0", + "FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13", 1 +}; + +static const ECCurveParams ecCurve_X9_62_PRIME_239V1 = { + "X9.62 P-239V1", ECField_GFp, 239, + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", + "6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A", + "0FFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF", + "7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B", 1 +}; + +static const ECCurveParams ecCurve_X9_62_PRIME_239V2 = { + "X9.62 P-239V2", ECField_GFp, 239, + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", + "617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C", + "38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7", + "5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA", + "7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063", 1 +}; + +static const ECCurveParams ecCurve_X9_62_PRIME_239V3 = { + "X9.62 P-239V3", ECField_GFp, 239, + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", + "255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E", + "6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A", + "1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551", 1 +}; + +/* ANSI X9.62 binary curves */ +static const ECCurveParams ecCurve_X9_62_CHAR2_PNB163V1 = { + "X9.62 C2-PNB163V1", ECField_GF2m, 163, + "080000000000000000000000000000000000000107", + "072546B5435234A422E0789675F432C89435DE5242", + "00C9517D06D5240D3CFF38C74B20B6CD4D6F9DD4D9", + "07AF69989546103D79329FCC3D74880F33BBE803CB", + "01EC23211B5966ADEA1D3F87F7EA5848AEF0B7CA9F", + "0400000000000000000001E60FC8821CC74DAEAFC1", 2 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_PNB163V2 = { + "X9.62 C2-PNB163V2", ECField_GF2m, 163, + "080000000000000000000000000000000000000107", + "0108B39E77C4B108BED981ED0E890E117C511CF072", + "0667ACEB38AF4E488C407433FFAE4F1C811638DF20", + "0024266E4EB5106D0A964D92C4860E2671DB9B6CC5", + "079F684DDF6684C5CD258B3890021B2386DFD19FC5", + "03FFFFFFFFFFFFFFFFFFFDF64DE1151ADBB78F10A7", 2 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_PNB163V3 = { + "X9.62 C2-PNB163V3", ECField_GF2m, 163, + "080000000000000000000000000000000000000107", + "07A526C63D3E25A256A007699F5447E32AE456B50E", + "03F7061798EB99E238FD6F1BF95B48FEEB4854252B", + "02F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB", + "05B935590C155E17EA48EB3FF3718B893DF59A05D0", + "03FFFFFFFFFFFFFFFFFFFE1AEE140F110AFF961309", 2 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_PNB176V1 = { + "X9.62 C2-PNB176V1", ECField_GF2m, 176, + "0100000000000000000000000000000000080000000007", + "E4E6DB2995065C407D9D39B8D0967B96704BA8E9C90B", + "5DDA470ABE6414DE8EC133AE28E9BBD7FCEC0AE0FFF2", + "8D16C2866798B600F9F08BB4A8E860F3298CE04A5798", + "6FA4539C2DADDDD6BAB5167D61B436E1D92BB16A562C", + "00010092537397ECA4F6145799D62B0A19CE06FE26AD", 0xFF6E +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_TNB191V1 = { + "X9.62 C2-TNB191V1", ECField_GF2m, 191, + "800000000000000000000000000000000000000000000201", + "2866537B676752636A68F56554E12640276B649EF7526267", + "2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC", + "36B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D", + "765BE73433B3F95E332932E70EA245CA2418EA0EF98018FB", + "40000000000000000000000004A20E90C39067C893BBB9A5", 2 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_TNB191V2 = { + "X9.62 C2-TNB191V2", ECField_GF2m, 191, + "800000000000000000000000000000000000000000000201", + "401028774D7777C7B7666D1366EA432071274F89FF01E718", + "0620048D28BCBD03B6249C99182B7C8CD19700C362C46A01", + "3809B2B7CC1B28CC5A87926AAD83FD28789E81E2C9E3BF10", + "17434386626D14F3DBF01760D9213A3E1CF37AEC437D668A", + "20000000000000000000000050508CB89F652824E06B8173", 4 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_TNB191V3 = { + "X9.62 C2-TNB191V3", ECField_GF2m, 191, + "800000000000000000000000000000000000000000000201", + "6C01074756099122221056911C77D77E77A777E7E7E77FCB", + "71FE1AF926CF847989EFEF8DB459F66394D90F32AD3F15E8", + "375D4CE24FDE434489DE8746E71786015009E66E38A926DD", + "545A39176196575D985999366E6AD34CE0A77CD7127B06BE", + "155555555555555555555555610C0B196812BFB6288A3EA3", 6 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_PNB208W1 = { + "X9.62 C2-PNB208W1", ECField_GF2m, 208, + "010000000000000000000000000000000800000000000000000007", + "0000000000000000000000000000000000000000000000000000", + "C8619ED45A62E6212E1160349E2BFA844439FAFC2A3FD1638F9E", + "89FDFBE4ABE193DF9559ECF07AC0CE78554E2784EB8C1ED1A57A", + "0F55B51A06E78E9AC38A035FF520D8B01781BEB1A6BB08617DE3", + "000101BAF95C9723C57B6C21DA2EFF2D5ED588BDD5717E212F9D", 0xFE48 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_TNB239V1 = { + "X9.62 C2-TNB239V1", ECField_GF2m, 239, + "800000000000000000000000000000000000000000000000001000000001", + "32010857077C5431123A46B808906756F543423E8D27877578125778AC76", + "790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", + "57927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D", + "61D8EE5077C33FECF6F1A16B268DE469C3C7744EA9A971649FC7A9616305", + "2000000000000000000000000000000F4D42FFE1492A4993F1CAD666E447", 4 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_TNB239V2 = { + "X9.62 C2-TNB239V2", ECField_GF2m, 239, + "800000000000000000000000000000000000000000000000001000000001", + "4230017757A767FAE42398569B746325D45313AF0766266479B75654E65F", + "5037EA654196CFF0CD82B2C14A2FCF2E3FF8775285B545722F03EACDB74B", + "28F9D04E900069C8DC47A08534FE76D2B900B7D7EF31F5709F200C4CA205", + "5667334C45AFF3B5A03BAD9DD75E2C71A99362567D5453F7FA6E227EC833", + "1555555555555555555555555555553C6F2885259C31E3FCDF154624522D", 6 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_TNB239V3 = { + "X9.62 C2-TNB239V3", ECField_GF2m, 239, + "800000000000000000000000000000000000000000000000001000000001", + "01238774666A67766D6676F778E676B66999176666E687666D8766C66A9F", + "6A941977BA9F6A435199ACFC51067ED587F519C5ECB541B8E44111DE1D40", + "70F6E9D04D289C4E89913CE3530BFDE903977D42B146D539BF1BDE4E9C92", + "2E5A0EAF6E5E1305B9004DCE5C0ED7FE59A35608F33837C816D80B79F461", + "0CCCCCCCCCCCCCCCCCCCCCCCCCCCCCAC4912D2D9DF903EF9888B8A0E4CFF", 0xA +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_PNB272W1 = { + "X9.62 C2-PNB272W1", ECField_GF2m, 272, + "010000000000000000000000000000000000000000000000000000010000000000000B", + "91A091F03B5FBA4AB2CCF49C4EDD220FB028712D42BE752B2C40094DBACDB586FB20", + "7167EFC92BB2E3CE7C8AAAFF34E12A9C557003D7C73A6FAF003F99F6CC8482E540F7", + "6108BABB2CEEBCF787058A056CBE0CFE622D7723A289E08A07AE13EF0D10D171DD8D", + "10C7695716851EEF6BA7F6872E6142FBD241B830FF5EFCACECCAB05E02005DDE9D23", + "000100FAF51354E0E39E4892DF6E319C72C8161603FA45AA7B998A167B8F1E629521", + 0xFF06 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_PNB304W1 = { + "X9.62 C2-PNB304W1", ECField_GF2m, 304, + "010000000000000000000000000000000000000000000000000000000000000000000000000807", + "FD0D693149A118F651E6DCE6802085377E5F882D1B510B44160074C1288078365A0396C8E681", + "BDDB97E555A50A908E43B01C798EA5DAA6788F1EA2794EFCF57166B8C14039601E55827340BE", + "197B07845E9BE2D96ADB0F5F3C7F2CFFBD7A3EB8B6FEC35C7FD67F26DDF6285A644F740A2614", + "E19FBEB76E0DA171517ECF401B50289BF014103288527A9B416A105E80260B549FDC1B92C03B", + "000101D556572AABAC800101D556572AABAC8001022D5C91DD173F8FB561DA6899164443051D", 0xFE2E +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_TNB359V1 = { + "X9.62 C2-TNB359V1", ECField_GF2m, 359, + "800000000000000000000000000000000000000000000000000000000000000000000000100000000000000001", + "5667676A654B20754F356EA92017D946567C46675556F19556A04616B567D223A5E05656FB549016A96656A557", + "2472E2D0197C49363F1FE7F5B6DB075D52B6947D135D8CA445805D39BC345626089687742B6329E70680231988", + "3C258EF3047767E7EDE0F1FDAA79DAEE3841366A132E163ACED4ED2401DF9C6BDCDE98E8E707C07A2239B1B097", + "53D7E08529547048121E9C95F3791DD804963948F34FAE7BF44EA82365DC7868FE57E4AE2DE211305A407104BD", + "01AF286BCA1AF286BCA1AF286BCA1AF286BCA1AF286BC9FB8F6B85C556892C20A7EB964FE7719E74F490758D3B", 0x4C +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_PNB368W1 = { + "X9.62 C2-PNB368W1", ECField_GF2m, 368, + "0100000000000000000000000000000000000000000000000000000000000000000000002000000000000000000007", + "E0D2EE25095206F5E2A4F9ED229F1F256E79A0E2B455970D8D0D865BD94778C576D62F0AB7519CCD2A1A906AE30D", + "FC1217D4320A90452C760A58EDCD30C8DD069B3C34453837A34ED50CB54917E1C2112D84D164F444F8F74786046A", + "1085E2755381DCCCE3C1557AFA10C2F0C0C2825646C5B34A394CBCFA8BC16B22E7E789E927BE216F02E1FB136A5F", + "7B3EB1BDDCBA62D5D8B2059B525797FC73822C59059C623A45FF3843CEE8F87CD1855ADAA81E2A0750B80FDA2310", + "00010090512DA9AF72B08349D98A5DD4C7B0532ECA51CE03E2D10F3B7AC579BD87E909AE40A6F131E9CFCE5BD967", 0xFF70 +}; + +static const ECCurveParams ecCurve_X9_62_CHAR2_TNB431R1 = { + "X9.62 C2-TNB431R1", ECField_GF2m, 431, + "800000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000001", + "1A827EF00DD6FC0E234CAF046C6A5D8A85395B236CC4AD2CF32A0CADBDC9DDF620B0EB9906D0957F6C6FEACD615468DF104DE296CD8F", + "10D9B4A3D9047D8B154359ABFB1B7F5485B04CEB868237DDC9DEDA982A679A5A919B626D4E50A8DD731B107A9962381FB5D807BF2618", + "120FC05D3C67A99DE161D2F4092622FECA701BE4F50F4758714E8A87BBF2A658EF8C21E7C5EFE965361F6C2999C0C247B0DBD70CE6B7", + "20D0AF8903A96F8D5FA2C255745D3C451B302C9346D9B7E485E7BCE41F6B591F3E8F6ADDCBB0BC4C2F947A7DE1A89B625D6A598B3760", + "0340340340340340340340340340340340340340340340340340340323C313FAB50589703B5EC68D3587FEC60D161CC149C1AD4A91", 0x2760 +}; + +/* SEC2 prime curves */ +static const ECCurveParams ecCurve_SECG_PRIME_112R1 = { + "SECP-112R1", ECField_GFp, 112, + "DB7C2ABF62E35E668076BEAD208B", + "DB7C2ABF62E35E668076BEAD2088", + "659EF8BA043916EEDE8911702B22", + "09487239995A5EE76B55F9C2F098", + "A89CE5AF8724C0A23E0E0FF77500", + "DB7C2ABF62E35E7628DFAC6561C5", 1 +}; + +static const ECCurveParams ecCurve_SECG_PRIME_112R2 = { + "SECP-112R2", ECField_GFp, 112, + "DB7C2ABF62E35E668076BEAD208B", + "6127C24C05F38A0AAAF65C0EF02C", + "51DEF1815DB5ED74FCC34C85D709", + "4BA30AB5E892B4E1649DD0928643", + "adcd46f5882e3747def36e956e97", + "36DF0AAFD8B8D7597CA10520D04B", 4 +}; + +static const ECCurveParams ecCurve_SECG_PRIME_128R1 = { + "SECP-128R1", ECField_GFp, 128, + "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", + "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC", + "E87579C11079F43DD824993C2CEE5ED3", + "161FF7528B899B2D0C28607CA52C5B86", + "CF5AC8395BAFEB13C02DA292DDED7A83", + "FFFFFFFE0000000075A30D1B9038A115", 1 +}; + +static const ECCurveParams ecCurve_SECG_PRIME_128R2 = { + "SECP-128R2", ECField_GFp, 128, + "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", + "D6031998D1B3BBFEBF59CC9BBFF9AEE1", + "5EEEFCA380D02919DC2C6558BB6D8A5D", + "7B6AA5D85E572983E6FB32A7CDEBC140", + "27B6916A894D3AEE7106FE805FC34B44", + "3FFFFFFF7FFFFFFFBE0024720613B5A3", 4 +}; + +static const ECCurveParams ecCurve_SECG_PRIME_160K1 = { + "SECP-160K1", ECField_GFp, 160, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", + "0000000000000000000000000000000000000000", + "0000000000000000000000000000000000000007", + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", + "938CF935318FDCED6BC28286531733C3F03C4FEE", + "0100000000000000000001B8FA16DFAB9ACA16B6B3", 1 +}; + +static const ECCurveParams ecCurve_SECG_PRIME_160R1 = { + "SECP-160R1", ECField_GFp, 160, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC", + "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45", + "4A96B5688EF573284664698968C38BB913CBFC82", + "23A628553168947D59DCC912042351377AC5FB32", + "0100000000000000000001F4C8F927AED3CA752257", 1 +}; + +static const ECCurveParams ecCurve_SECG_PRIME_160R2 = { + "SECP-160R2", ECField_GFp, 160, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70", + "B4E134D3FB59EB8BAB57274904664D5AF50388BA", + "52DCB034293A117E1F4FF11B30F7199D3144CE6D", + "FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E", + "0100000000000000000000351EE786A818F3A1A16B", 1 +}; + +static const ECCurveParams ecCurve_SECG_PRIME_192K1 = { + "SECP-192K1", ECField_GFp, 192, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", + "000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000003", + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", + "FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", 1 +}; + +static const ECCurveParams ecCurve_SECG_PRIME_224K1 = { + "SECP-224K1", ECField_GFp, 224, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", + "00000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000005", + "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", + "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", + "010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", 1 +}; + +static const ECCurveParams ecCurve_SECG_PRIME_256K1 = { + "SECP-256K1", ECField_GFp, 256, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000007", + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 1 +}; + +/* SEC2 binary curves */ +static const ECCurveParams ecCurve_SECG_CHAR2_113R1 = { + "SECT-113R1", ECField_GF2m, 113, + "020000000000000000000000000201", + "003088250CA6E7C7FE649CE85820F7", + "00E8BEE4D3E2260744188BE0E9C723", + "009D73616F35F4AB1407D73562C10F", + "00A52830277958EE84D1315ED31886", + "0100000000000000D9CCEC8A39E56F", 2 +}; + +static const ECCurveParams ecCurve_SECG_CHAR2_113R2 = { + "SECT-113R2", ECField_GF2m, 113, + "020000000000000000000000000201", + "00689918DBEC7E5A0DD6DFC0AA55C7", + "0095E9A9EC9B297BD4BF36E059184F", + "01A57A6A7B26CA5EF52FCDB8164797", + "00B3ADC94ED1FE674C06E695BABA1D", + "010000000000000108789B2496AF93", 2 +}; + +static const ECCurveParams ecCurve_SECG_CHAR2_131R1 = { + "SECT-131R1", ECField_GF2m, 131, + "080000000000000000000000000000010D", + "07A11B09A76B562144418FF3FF8C2570B8", + "0217C05610884B63B9C6C7291678F9D341", + "0081BAF91FDF9833C40F9C181343638399", + "078C6E7EA38C001F73C8134B1B4EF9E150", + "0400000000000000023123953A9464B54D", 2 +}; + +static const ECCurveParams ecCurve_SECG_CHAR2_131R2 = { + "SECT-131R2", ECField_GF2m, 131, + "080000000000000000000000000000010D", + "03E5A88919D7CAFCBF415F07C2176573B2", + "04B8266A46C55657AC734CE38F018F2192", + "0356DCD8F2F95031AD652D23951BB366A8", + "0648F06D867940A5366D9E265DE9EB240F", + "0400000000000000016954A233049BA98F", 2 +}; + +static const ECCurveParams ecCurve_SECG_CHAR2_163R1 = { + "SECT-163R1", ECField_GF2m, 163, + "0800000000000000000000000000000000000000C9", + "07B6882CAAEFA84F9554FF8428BD88E246D2782AE2", + "0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9", + "0369979697AB43897789566789567F787A7876A654", + "00435EDB42EFAFB2989D51FEFCE3C80988F41FF883", + "03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B", 2 +}; + +static const ECCurveParams ecCurve_SECG_CHAR2_193R1 = { + "SECT-193R1", ECField_GF2m, 193, + "02000000000000000000000000000000000000000000008001", + "0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01", + "00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814", + "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1", + "0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05", + "01000000000000000000000000C7F34A778F443ACC920EBA49", 2 +}; + +static const ECCurveParams ecCurve_SECG_CHAR2_193R2 = { + "SECT-193R2", ECField_GF2m, 193, + "02000000000000000000000000000000000000000000008001", + "0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B", + "00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE", + "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F", + "01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C", + "010000000000000000000000015AAB561B005413CCD4EE99D5", 2 +}; + +static const ECCurveParams ecCurve_SECG_CHAR2_239K1 = { + "SECT-239K1", ECField_GF2m, 239, + "800000000000000000004000000000000000000000000000000000000001", + "000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000001", + "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC", + "76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA", + "2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5", 4 +}; + +/* WTLS curves */ +static const ECCurveParams ecCurve_WTLS_1 = { + "WTLS-1", ECField_GF2m, 113, + "020000000000000000000000000201", + "000000000000000000000000000001", + "000000000000000000000000000001", + "01667979A40BA497E5D5C270780617", + "00F44B4AF1ECC2630E08785CEBCC15", + "00FFFFFFFFFFFFFFFDBF91AF6DEA73", 2 +}; + +static const ECCurveParams ecCurve_WTLS_8 = { + "WTLS-8", ECField_GFp, 112, + "FFFFFFFFFFFFFFFFFFFFFFFFFDE7", + "0000000000000000000000000000", + "0000000000000000000000000003", + "0000000000000000000000000001", + "0000000000000000000000000002", + "0100000000000001ECEA551AD837E9", 1 +}; + +static const ECCurveParams ecCurve_WTLS_9 = { + "WTLS-9", ECField_GFp, 160, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC808F", + "0000000000000000000000000000000000000000", + "0000000000000000000000000000000000000003", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", + "0100000000000000000001CDC98AE0E2DE574ABF33", 1 +}; + +/* mapping between ECCurveName enum and pointers to ECCurveParams */ +static const ECCurveParams *ecCurve_map[] = { + NULL, /* ECCurve_noName */ + &ecCurve_NIST_P192, /* ECCurve_NIST_P192 */ + &ecCurve_NIST_P224, /* ECCurve_NIST_P224 */ + &ecCurve_NIST_P256, /* ECCurve_NIST_P256 */ + &ecCurve_NIST_P384, /* ECCurve_NIST_P384 */ + &ecCurve_NIST_P521, /* ECCurve_NIST_P521 */ + &ecCurve_NIST_K163, /* ECCurve_NIST_K163 */ + &ecCurve_NIST_B163, /* ECCurve_NIST_B163 */ + &ecCurve_NIST_K233, /* ECCurve_NIST_K233 */ + &ecCurve_NIST_B233, /* ECCurve_NIST_B233 */ + &ecCurve_NIST_K283, /* ECCurve_NIST_K283 */ + &ecCurve_NIST_B283, /* ECCurve_NIST_B283 */ + &ecCurve_NIST_K409, /* ECCurve_NIST_K409 */ + &ecCurve_NIST_B409, /* ECCurve_NIST_B409 */ + &ecCurve_NIST_K571, /* ECCurve_NIST_K571 */ + &ecCurve_NIST_B571, /* ECCurve_NIST_B571 */ + &ecCurve_X9_62_PRIME_192V2, /* ECCurve_X9_62_PRIME_192V2 */ + &ecCurve_X9_62_PRIME_192V3, /* ECCurve_X9_62_PRIME_192V3 */ + &ecCurve_X9_62_PRIME_239V1, /* ECCurve_X9_62_PRIME_239V1 */ + &ecCurve_X9_62_PRIME_239V2, /* ECCurve_X9_62_PRIME_239V2 */ + &ecCurve_X9_62_PRIME_239V3, /* ECCurve_X9_62_PRIME_239V3 */ + &ecCurve_X9_62_CHAR2_PNB163V1, /* ECCurve_X9_62_CHAR2_PNB163V1 */ + &ecCurve_X9_62_CHAR2_PNB163V2, /* ECCurve_X9_62_CHAR2_PNB163V2 */ + &ecCurve_X9_62_CHAR2_PNB163V3, /* ECCurve_X9_62_CHAR2_PNB163V3 */ + &ecCurve_X9_62_CHAR2_PNB176V1, /* ECCurve_X9_62_CHAR2_PNB176V1 */ + &ecCurve_X9_62_CHAR2_TNB191V1, /* ECCurve_X9_62_CHAR2_TNB191V1 */ + &ecCurve_X9_62_CHAR2_TNB191V2, /* ECCurve_X9_62_CHAR2_TNB191V2 */ + &ecCurve_X9_62_CHAR2_TNB191V3, /* ECCurve_X9_62_CHAR2_TNB191V3 */ + &ecCurve_X9_62_CHAR2_PNB208W1, /* ECCurve_X9_62_CHAR2_PNB208W1 */ + &ecCurve_X9_62_CHAR2_TNB239V1, /* ECCurve_X9_62_CHAR2_TNB239V1 */ + &ecCurve_X9_62_CHAR2_TNB239V2, /* ECCurve_X9_62_CHAR2_TNB239V2 */ + &ecCurve_X9_62_CHAR2_TNB239V3, /* ECCurve_X9_62_CHAR2_TNB239V3 */ + &ecCurve_X9_62_CHAR2_PNB272W1, /* ECCurve_X9_62_CHAR2_PNB272W1 */ + &ecCurve_X9_62_CHAR2_PNB304W1, /* ECCurve_X9_62_CHAR2_PNB304W1 */ + &ecCurve_X9_62_CHAR2_TNB359V1, /* ECCurve_X9_62_CHAR2_TNB359V1 */ + &ecCurve_X9_62_CHAR2_PNB368W1, /* ECCurve_X9_62_CHAR2_PNB368W1 */ + &ecCurve_X9_62_CHAR2_TNB431R1, /* ECCurve_X9_62_CHAR2_TNB431R1 */ + &ecCurve_SECG_PRIME_112R1, /* ECCurve_SECG_PRIME_112R1 */ + &ecCurve_SECG_PRIME_112R2, /* ECCurve_SECG_PRIME_112R2 */ + &ecCurve_SECG_PRIME_128R1, /* ECCurve_SECG_PRIME_128R1 */ + &ecCurve_SECG_PRIME_128R2, /* ECCurve_SECG_PRIME_128R2 */ + &ecCurve_SECG_PRIME_160K1, /* ECCurve_SECG_PRIME_160K1 */ + &ecCurve_SECG_PRIME_160R1, /* ECCurve_SECG_PRIME_160R1 */ + &ecCurve_SECG_PRIME_160R2, /* ECCurve_SECG_PRIME_160R2 */ + &ecCurve_SECG_PRIME_192K1, /* ECCurve_SECG_PRIME_192K1 */ + &ecCurve_SECG_PRIME_224K1, /* ECCurve_SECG_PRIME_224K1 */ + &ecCurve_SECG_PRIME_256K1, /* ECCurve_SECG_PRIME_256K1 */ + &ecCurve_SECG_CHAR2_113R1, /* ECCurve_SECG_CHAR2_113R1 */ + &ecCurve_SECG_CHAR2_113R2, /* ECCurve_SECG_CHAR2_113R2 */ + &ecCurve_SECG_CHAR2_131R1, /* ECCurve_SECG_CHAR2_131R1 */ + &ecCurve_SECG_CHAR2_131R2, /* ECCurve_SECG_CHAR2_131R2 */ + &ecCurve_SECG_CHAR2_163R1, /* ECCurve_SECG_CHAR2_163R1 */ + &ecCurve_SECG_CHAR2_193R1, /* ECCurve_SECG_CHAR2_193R1 */ + &ecCurve_SECG_CHAR2_193R2, /* ECCurve_SECG_CHAR2_193R2 */ + &ecCurve_SECG_CHAR2_239K1, /* ECCurve_SECG_CHAR2_239K1 */ + &ecCurve_WTLS_1, /* ECCurve_WTLS_1 */ + &ecCurve_WTLS_8, /* ECCurve_WTLS_8 */ + &ecCurve_WTLS_9, /* ECCurve_WTLS_9 */ + NULL /* ECCurve_pastLastCurve */ +}; + +#endif /* _ECL_CURVE_H */ diff --git a/usr/src/common/crypto/ecc/ecl-exp.h b/usr/src/common/crypto/ecc/ecl-exp.h new file mode 100644 index 0000000000..aaf266fec2 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecl-exp.h @@ -0,0 +1,204 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _ECL_EXP_H +#define _ECL_EXP_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Curve field type */ +typedef enum { + ECField_GFp, + ECField_GF2m +} ECField; + +/* Hexadecimal encoding of curve parameters */ +struct ECCurveParamsStr { + char *text; + ECField field; + unsigned int size; + char *irr; + char *curvea; + char *curveb; + char *genx; + char *geny; + char *order; + int cofactor; +}; +typedef struct ECCurveParamsStr ECCurveParams; + +/* Named curve parameters */ +typedef enum { + + ECCurve_noName = 0, + + /* NIST prime curves */ + ECCurve_NIST_P192, + ECCurve_NIST_P224, + ECCurve_NIST_P256, + ECCurve_NIST_P384, + ECCurve_NIST_P521, + + /* NIST binary curves */ + ECCurve_NIST_K163, + ECCurve_NIST_B163, + ECCurve_NIST_K233, + ECCurve_NIST_B233, + ECCurve_NIST_K283, + ECCurve_NIST_B283, + ECCurve_NIST_K409, + ECCurve_NIST_B409, + ECCurve_NIST_K571, + ECCurve_NIST_B571, + + /* ANSI X9.62 prime curves */ + /* ECCurve_X9_62_PRIME_192V1 == ECCurve_NIST_P192 */ + ECCurve_X9_62_PRIME_192V2, + ECCurve_X9_62_PRIME_192V3, + ECCurve_X9_62_PRIME_239V1, + ECCurve_X9_62_PRIME_239V2, + ECCurve_X9_62_PRIME_239V3, + /* ECCurve_X9_62_PRIME_256V1 == ECCurve_NIST_P256 */ + + /* ANSI X9.62 binary curves */ + ECCurve_X9_62_CHAR2_PNB163V1, + ECCurve_X9_62_CHAR2_PNB163V2, + ECCurve_X9_62_CHAR2_PNB163V3, + ECCurve_X9_62_CHAR2_PNB176V1, + ECCurve_X9_62_CHAR2_TNB191V1, + ECCurve_X9_62_CHAR2_TNB191V2, + ECCurve_X9_62_CHAR2_TNB191V3, + ECCurve_X9_62_CHAR2_PNB208W1, + ECCurve_X9_62_CHAR2_TNB239V1, + ECCurve_X9_62_CHAR2_TNB239V2, + ECCurve_X9_62_CHAR2_TNB239V3, + ECCurve_X9_62_CHAR2_PNB272W1, + ECCurve_X9_62_CHAR2_PNB304W1, + ECCurve_X9_62_CHAR2_TNB359V1, + ECCurve_X9_62_CHAR2_PNB368W1, + ECCurve_X9_62_CHAR2_TNB431R1, + + /* SEC2 prime curves */ + ECCurve_SECG_PRIME_112R1, + ECCurve_SECG_PRIME_112R2, + ECCurve_SECG_PRIME_128R1, + ECCurve_SECG_PRIME_128R2, + ECCurve_SECG_PRIME_160K1, + ECCurve_SECG_PRIME_160R1, + ECCurve_SECG_PRIME_160R2, + ECCurve_SECG_PRIME_192K1, + /* ECCurve_SECG_PRIME_192R1 == ECCurve_NIST_P192 */ + ECCurve_SECG_PRIME_224K1, + /* ECCurve_SECG_PRIME_224R1 == ECCurve_NIST_P224 */ + ECCurve_SECG_PRIME_256K1, + /* ECCurve_SECG_PRIME_256R1 == ECCurve_NIST_P256 */ + /* ECCurve_SECG_PRIME_384R1 == ECCurve_NIST_P384 */ + /* ECCurve_SECG_PRIME_521R1 == ECCurve_NIST_P521 */ + + /* SEC2 binary curves */ + ECCurve_SECG_CHAR2_113R1, + ECCurve_SECG_CHAR2_113R2, + ECCurve_SECG_CHAR2_131R1, + ECCurve_SECG_CHAR2_131R2, + /* ECCurve_SECG_CHAR2_163K1 == ECCurve_NIST_K163 */ + ECCurve_SECG_CHAR2_163R1, + /* ECCurve_SECG_CHAR2_163R2 == ECCurve_NIST_B163 */ + ECCurve_SECG_CHAR2_193R1, + ECCurve_SECG_CHAR2_193R2, + /* ECCurve_SECG_CHAR2_233K1 == ECCurve_NIST_K233 */ + /* ECCurve_SECG_CHAR2_233R1 == ECCurve_NIST_B233 */ + ECCurve_SECG_CHAR2_239K1, + /* ECCurve_SECG_CHAR2_283K1 == ECCurve_NIST_K283 */ + /* ECCurve_SECG_CHAR2_283R1 == ECCurve_NIST_B283 */ + /* ECCurve_SECG_CHAR2_409K1 == ECCurve_NIST_K409 */ + /* ECCurve_SECG_CHAR2_409R1 == ECCurve_NIST_B409 */ + /* ECCurve_SECG_CHAR2_571K1 == ECCurve_NIST_K571 */ + /* ECCurve_SECG_CHAR2_571R1 == ECCurve_NIST_B571 */ + + /* WTLS curves */ + ECCurve_WTLS_1, + /* there is no WTLS 2 curve */ + /* ECCurve_WTLS_3 == ECCurve_NIST_K163 */ + /* ECCurve_WTLS_4 == ECCurve_SECG_CHAR2_113R1 */ + /* ECCurve_WTLS_5 == ECCurve_X9_62_CHAR2_PNB163V1 */ + /* ECCurve_WTLS_6 == ECCurve_SECG_PRIME_112R1 */ + /* ECCurve_WTLS_7 == ECCurve_SECG_PRIME_160R1 */ + ECCurve_WTLS_8, + ECCurve_WTLS_9, + /* ECCurve_WTLS_10 == ECCurve_NIST_K233 */ + /* ECCurve_WTLS_11 == ECCurve_NIST_B233 */ + /* ECCurve_WTLS_12 == ECCurve_NIST_P224 */ + + ECCurve_pastLastCurve +} ECCurveName; + +/* Aliased named curves */ + +#define ECCurve_X9_62_PRIME_192V1 ECCurve_NIST_P192 +#define ECCurve_X9_62_PRIME_256V1 ECCurve_NIST_P256 +#define ECCurve_SECG_PRIME_192R1 ECCurve_NIST_P192 +#define ECCurve_SECG_PRIME_224R1 ECCurve_NIST_P224 +#define ECCurve_SECG_PRIME_256R1 ECCurve_NIST_P256 +#define ECCurve_SECG_PRIME_384R1 ECCurve_NIST_P384 +#define ECCurve_SECG_PRIME_521R1 ECCurve_NIST_P521 +#define ECCurve_SECG_CHAR2_163K1 ECCurve_NIST_K163 +#define ECCurve_SECG_CHAR2_163R2 ECCurve_NIST_B163 +#define ECCurve_SECG_CHAR2_233K1 ECCurve_NIST_K233 +#define ECCurve_SECG_CHAR2_233R1 ECCurve_NIST_B233 +#define ECCurve_SECG_CHAR2_283K1 ECCurve_NIST_K283 +#define ECCurve_SECG_CHAR2_283R1 ECCurve_NIST_B283 +#define ECCurve_SECG_CHAR2_409K1 ECCurve_NIST_K409 +#define ECCurve_SECG_CHAR2_409R1 ECCurve_NIST_B409 +#define ECCurve_SECG_CHAR2_571K1 ECCurve_NIST_K571 +#define ECCurve_SECG_CHAR2_571R1 ECCurve_NIST_B571 +#define ECCurve_WTLS_3 ECCurve_NIST_K163 +#define ECCurve_WTLS_4 ECCurve_SECG_CHAR2_113R1 +#define ECCurve_WTLS_5 ECCurve_X9_62_CHAR2_PNB163V1 +#define ECCurve_WTLS_6 ECCurve_SECG_PRIME_112R1 +#define ECCurve_WTLS_7 ECCurve_SECG_PRIME_160R1 +#define ECCurve_WTLS_10 ECCurve_NIST_K233 +#define ECCurve_WTLS_11 ECCurve_NIST_B233 +#define ECCurve_WTLS_12 ECCurve_NIST_P224 + +#endif /* _ECL_EXP_H */ diff --git a/usr/src/common/crypto/ecc/ecl-priv.h b/usr/src/common/crypto/ecc/ecl-priv.h new file mode 100644 index 0000000000..83c4f336f0 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecl-priv.h @@ -0,0 +1,292 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stephen Fung <fungstep@hotmail.com> and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _ECL_PRIV_H +#define _ECL_PRIV_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecl.h" +#include "mpi.h" +#include "mplogic.h" + +/* MAX_FIELD_SIZE_DIGITS is the maximum size of field element supported */ +/* the following needs to go away... */ +#if defined(MP_USE_LONG_LONG_DIGIT) || defined(MP_USE_LONG_DIGIT) +#define ECL_SIXTY_FOUR_BIT +#else +#define ECL_THIRTY_TWO_BIT +#endif + +#define ECL_CURVE_DIGITS(curve_size_in_bits) \ + (((curve_size_in_bits)+(sizeof(mp_digit)*8-1))/(sizeof(mp_digit)*8)) +#define ECL_BITS (sizeof(mp_digit)*8) +#define ECL_MAX_FIELD_SIZE_DIGITS (80/sizeof(mp_digit)) + +/* Gets the i'th bit in the binary representation of a. If i >= length(a), + * then return 0. (The above behaviour differs from mpl_get_bit, which + * causes an error if i >= length(a).) */ +#define MP_GET_BIT(a, i) \ + ((i) >= mpl_significant_bits((a))) ? 0 : mpl_get_bit((a), (i)) + +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) +#define MP_ADD_CARRY(a1, a2, s, cin, cout) \ + { mp_word w; \ + w = ((mp_word)(cin)) + (a1) + (a2); \ + s = ACCUM(w); \ + cout = CARRYOUT(w); } + +#define MP_SUB_BORROW(a1, a2, s, bin, bout) \ + { mp_word w; \ + w = ((mp_word)(a1)) - (a2) - (bin); \ + s = ACCUM(w); \ + bout = (w >> MP_DIGIT_BIT) & 1; } + +#else +/* NOTE, + * cin and cout could be the same variable. + * bin and bout could be the same variable. + * a1 or a2 and s could be the same variable. + * don't trash those outputs until their respective inputs have + * been read. */ +#define MP_ADD_CARRY(a1, a2, s, cin, cout) \ + { mp_digit tmp,sum; \ + tmp = (a1); \ + sum = tmp + (a2); \ + tmp = (sum < tmp); /* detect overflow */ \ + s = sum += (cin); \ + cout = tmp + (sum < (cin)); } + +#define MP_SUB_BORROW(a1, a2, s, bin, bout) \ + { mp_digit tmp; \ + tmp = (a1); \ + s = tmp - (a2); \ + tmp = (s > tmp); /* detect borrow */ \ + if ((bin) && !s--) tmp++; \ + bout = tmp; } +#endif + + +struct GFMethodStr; +typedef struct GFMethodStr GFMethod; +struct GFMethodStr { + /* Indicates whether the structure was constructed from dynamic memory + * or statically created. */ + int constructed; + /* Irreducible that defines the field. For prime fields, this is the + * prime p. For binary polynomial fields, this is the bitstring + * representation of the irreducible polynomial. */ + mp_int irr; + /* For prime fields, the value irr_arr[0] is the number of bits in the + * field. For binary polynomial fields, the irreducible polynomial + * f(t) is represented as an array of unsigned int[], where f(t) is + * of the form: f(t) = t^p[0] + t^p[1] + ... + t^p[4] where m = p[0] + * > p[1] > ... > p[4] = 0. */ + unsigned int irr_arr[5]; + /* Field arithmetic methods. All methods (except field_enc and + * field_dec) are assumed to take field-encoded parameters and return + * field-encoded values. All methods (except field_enc and field_dec) + * are required to be implemented. */ + mp_err (*field_add) (const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + mp_err (*field_neg) (const mp_int *a, mp_int *r, const GFMethod *meth); + mp_err (*field_sub) (const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + mp_err (*field_mod) (const mp_int *a, mp_int *r, const GFMethod *meth); + mp_err (*field_mul) (const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + mp_err (*field_sqr) (const mp_int *a, mp_int *r, const GFMethod *meth); + mp_err (*field_div) (const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + mp_err (*field_enc) (const mp_int *a, mp_int *r, const GFMethod *meth); + mp_err (*field_dec) (const mp_int *a, mp_int *r, const GFMethod *meth); + /* Extra storage for implementation-specific data. Any memory + * allocated to these extra fields will be cleared by extra_free. */ + void *extra1; + void *extra2; + void (*extra_free) (GFMethod *meth); +}; + +/* Construct generic GFMethods. */ +GFMethod *GFMethod_consGFp(const mp_int *irr); +GFMethod *GFMethod_consGFp_mont(const mp_int *irr); +GFMethod *GFMethod_consGF2m(const mp_int *irr, + const unsigned int irr_arr[5]); +/* Free the memory allocated (if any) to a GFMethod object. */ +void GFMethod_free(GFMethod *meth); + +struct ECGroupStr { + /* Indicates whether the structure was constructed from dynamic memory + * or statically created. */ + int constructed; + /* Field definition and arithmetic. */ + GFMethod *meth; + /* Textual representation of curve name, if any. */ + char *text; +#ifdef _KERNEL + int text_len; +#endif + /* Curve parameters, field-encoded. */ + mp_int curvea, curveb; + /* x and y coordinates of the base point, field-encoded. */ + mp_int genx, geny; + /* Order and cofactor of the base point. */ + mp_int order; + int cofactor; + /* Point arithmetic methods. All methods are assumed to take + * field-encoded parameters and return field-encoded values. All + * methods (except base_point_mul and points_mul) are required to be + * implemented. */ + mp_err (*point_add) (const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + mp_err (*point_sub) (const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + mp_err (*point_dbl) (const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); + mp_err (*point_mul) (const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); + mp_err (*base_point_mul) (const mp_int *n, mp_int *rx, mp_int *ry, + const ECGroup *group); + mp_err (*points_mul) (const mp_int *k1, const mp_int *k2, + const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); + mp_err (*validate_point) (const mp_int *px, const mp_int *py, const ECGroup *group); + /* Extra storage for implementation-specific data. Any memory + * allocated to these extra fields will be cleared by extra_free. */ + void *extra1; + void *extra2; + void (*extra_free) (ECGroup *group); +}; + +/* Wrapper functions for generic prime field arithmetic. */ +mp_err ec_GFp_add(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_neg(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GFp_sub(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + +/* fixed length in-line adds. Count is in words */ +mp_err ec_GFp_add_3(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_add_4(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_add_5(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_add_6(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sub_3(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sub_4(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sub_5(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sub_6(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + +mp_err ec_GFp_mod(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GFp_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sqr(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GFp_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +/* Wrapper functions for generic binary polynomial field arithmetic. */ +mp_err ec_GF2m_add(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GF2m_neg(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GF2m_mod(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GF2m_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GF2m_sqr(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GF2m_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + +/* Montgomery prime field arithmetic. */ +mp_err ec_GFp_mul_mont(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sqr_mont(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GFp_div_mont(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_enc_mont(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GFp_dec_mont(const mp_int *a, mp_int *r, const GFMethod *meth); +void ec_GFp_extra_free_mont(GFMethod *meth); + +/* point multiplication */ +mp_err ec_pts_mul_basic(const mp_int *k1, const mp_int *k2, + const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); +mp_err ec_pts_mul_simul_w2(const mp_int *k1, const mp_int *k2, + const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Computes the windowed non-adjacent-form (NAF) of a scalar. Out should + * be an array of signed char's to output to, bitsize should be the number + * of bits of out, in is the original scalar, and w is the window size. + * NAF is discussed in the paper: D. Hankerson, J. Hernandez and A. + * Menezes, "Software implementation of elliptic curve cryptography over + * binary fields", Proc. CHES 2000. */ +mp_err ec_compute_wNAF(signed char *out, int bitsize, const mp_int *in, + int w); + +/* Optimized field arithmetic */ +mp_err ec_group_set_gfp192(ECGroup *group, ECCurveName); +mp_err ec_group_set_gfp224(ECGroup *group, ECCurveName); +mp_err ec_group_set_gfp256(ECGroup *group, ECCurveName); +mp_err ec_group_set_gfp384(ECGroup *group, ECCurveName); +mp_err ec_group_set_gfp521(ECGroup *group, ECCurveName); +mp_err ec_group_set_gf2m163(ECGroup *group, ECCurveName name); +mp_err ec_group_set_gf2m193(ECGroup *group, ECCurveName name); +mp_err ec_group_set_gf2m233(ECGroup *group, ECCurveName name); + +/* Optimized floating-point arithmetic */ +#ifdef ECL_USE_FP +mp_err ec_group_set_secp160r1_fp(ECGroup *group); +mp_err ec_group_set_nistp192_fp(ECGroup *group); +mp_err ec_group_set_nistp224_fp(ECGroup *group); +#endif + +#endif /* _ECL_PRIV_H */ diff --git a/usr/src/common/crypto/ecc/ecl.c b/usr/src/common/crypto/ecc/ecl.c new file mode 100644 index 0000000000..2efc404d1b --- /dev/null +++ b/usr/src/common/crypto/ecc/ecl.c @@ -0,0 +1,463 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mpi.h" +#include "mplogic.h" +#include "ecl.h" +#include "ecl-priv.h" +#include "ec2.h" +#include "ecp.h" +#ifndef _KERNEL +#include <stdlib.h> +#include <string.h> +#endif + +/* Allocate memory for a new ECGroup object. */ +ECGroup * +ECGroup_new(int kmflag) +{ + mp_err res = MP_OKAY; + ECGroup *group; +#ifdef _KERNEL + group = (ECGroup *) kmem_alloc(sizeof(ECGroup), kmflag); +#else + group = (ECGroup *) malloc(sizeof(ECGroup)); +#endif + if (group == NULL) + return NULL; + group->constructed = MP_YES; + group->meth = NULL; + group->text = NULL; + MP_DIGITS(&group->curvea) = 0; + MP_DIGITS(&group->curveb) = 0; + MP_DIGITS(&group->genx) = 0; + MP_DIGITS(&group->geny) = 0; + MP_DIGITS(&group->order) = 0; + group->base_point_mul = NULL; + group->points_mul = NULL; + group->validate_point = NULL; + group->extra1 = NULL; + group->extra2 = NULL; + group->extra_free = NULL; + MP_CHECKOK(mp_init(&group->curvea, kmflag)); + MP_CHECKOK(mp_init(&group->curveb, kmflag)); + MP_CHECKOK(mp_init(&group->genx, kmflag)); + MP_CHECKOK(mp_init(&group->geny, kmflag)); + MP_CHECKOK(mp_init(&group->order, kmflag)); + + CLEANUP: + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} + +/* Construct a generic ECGroup for elliptic curves over prime fields. */ +ECGroup * +ECGroup_consGFp(const mp_int *irr, const mp_int *curvea, + const mp_int *curveb, const mp_int *genx, + const mp_int *geny, const mp_int *order, int cofactor) +{ + mp_err res = MP_OKAY; + ECGroup *group = NULL; + + group = ECGroup_new(FLAG(irr)); + if (group == NULL) + return NULL; + + group->meth = GFMethod_consGFp(irr); + if (group->meth == NULL) { + res = MP_MEM; + goto CLEANUP; + } + MP_CHECKOK(mp_copy(curvea, &group->curvea)); + MP_CHECKOK(mp_copy(curveb, &group->curveb)); + MP_CHECKOK(mp_copy(genx, &group->genx)); + MP_CHECKOK(mp_copy(geny, &group->geny)); + MP_CHECKOK(mp_copy(order, &group->order)); + group->cofactor = cofactor; + group->point_add = &ec_GFp_pt_add_aff; + group->point_sub = &ec_GFp_pt_sub_aff; + group->point_dbl = &ec_GFp_pt_dbl_aff; + group->point_mul = &ec_GFp_pt_mul_jm_wNAF; + group->base_point_mul = NULL; + group->points_mul = &ec_GFp_pts_mul_jac; + group->validate_point = &ec_GFp_validate_point; + + CLEANUP: + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} + +/* Construct a generic ECGroup for elliptic curves over prime fields with + * field arithmetic implemented in Montgomery coordinates. */ +ECGroup * +ECGroup_consGFp_mont(const mp_int *irr, const mp_int *curvea, + const mp_int *curveb, const mp_int *genx, + const mp_int *geny, const mp_int *order, int cofactor) +{ + mp_err res = MP_OKAY; + ECGroup *group = NULL; + + group = ECGroup_new(FLAG(irr)); + if (group == NULL) + return NULL; + + group->meth = GFMethod_consGFp_mont(irr); + if (group->meth == NULL) { + res = MP_MEM; + goto CLEANUP; + } + MP_CHECKOK(group->meth-> + field_enc(curvea, &group->curvea, group->meth)); + MP_CHECKOK(group->meth-> + field_enc(curveb, &group->curveb, group->meth)); + MP_CHECKOK(group->meth->field_enc(genx, &group->genx, group->meth)); + MP_CHECKOK(group->meth->field_enc(geny, &group->geny, group->meth)); + MP_CHECKOK(mp_copy(order, &group->order)); + group->cofactor = cofactor; + group->point_add = &ec_GFp_pt_add_aff; + group->point_sub = &ec_GFp_pt_sub_aff; + group->point_dbl = &ec_GFp_pt_dbl_aff; + group->point_mul = &ec_GFp_pt_mul_jm_wNAF; + group->base_point_mul = NULL; + group->points_mul = &ec_GFp_pts_mul_jac; + group->validate_point = &ec_GFp_validate_point; + + CLEANUP: + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} + +#ifdef NSS_ECC_MORE_THAN_SUITE_B +/* Construct a generic ECGroup for elliptic curves over binary polynomial + * fields. */ +ECGroup * +ECGroup_consGF2m(const mp_int *irr, const unsigned int irr_arr[5], + const mp_int *curvea, const mp_int *curveb, + const mp_int *genx, const mp_int *geny, + const mp_int *order, int cofactor) +{ + mp_err res = MP_OKAY; + ECGroup *group = NULL; + + group = ECGroup_new(FLAG(irr)); + if (group == NULL) + return NULL; + + group->meth = GFMethod_consGF2m(irr, irr_arr); + if (group->meth == NULL) { + res = MP_MEM; + goto CLEANUP; + } + MP_CHECKOK(mp_copy(curvea, &group->curvea)); + MP_CHECKOK(mp_copy(curveb, &group->curveb)); + MP_CHECKOK(mp_copy(genx, &group->genx)); + MP_CHECKOK(mp_copy(geny, &group->geny)); + MP_CHECKOK(mp_copy(order, &group->order)); + group->cofactor = cofactor; + group->point_add = &ec_GF2m_pt_add_aff; + group->point_sub = &ec_GF2m_pt_sub_aff; + group->point_dbl = &ec_GF2m_pt_dbl_aff; + group->point_mul = &ec_GF2m_pt_mul_mont; + group->base_point_mul = NULL; + group->points_mul = &ec_pts_mul_basic; + group->validate_point = &ec_GF2m_validate_point; + + CLEANUP: + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} +#endif + +/* Construct ECGroup from hex parameters and name, if any. Called by + * ECGroup_fromHex and ECGroup_fromName. */ +ECGroup * +ecgroup_fromNameAndHex(const ECCurveName name, + const ECCurveParams * params, int kmflag) +{ + mp_int irr, curvea, curveb, genx, geny, order; + int bits; + ECGroup *group = NULL; + mp_err res = MP_OKAY; + + /* initialize values */ + MP_DIGITS(&irr) = 0; + MP_DIGITS(&curvea) = 0; + MP_DIGITS(&curveb) = 0; + MP_DIGITS(&genx) = 0; + MP_DIGITS(&geny) = 0; + MP_DIGITS(&order) = 0; + MP_CHECKOK(mp_init(&irr, kmflag)); + MP_CHECKOK(mp_init(&curvea, kmflag)); + MP_CHECKOK(mp_init(&curveb, kmflag)); + MP_CHECKOK(mp_init(&genx, kmflag)); + MP_CHECKOK(mp_init(&geny, kmflag)); + MP_CHECKOK(mp_init(&order, kmflag)); + MP_CHECKOK(mp_read_radix(&irr, params->irr, 16)); + MP_CHECKOK(mp_read_radix(&curvea, params->curvea, 16)); + MP_CHECKOK(mp_read_radix(&curveb, params->curveb, 16)); + MP_CHECKOK(mp_read_radix(&genx, params->genx, 16)); + MP_CHECKOK(mp_read_radix(&geny, params->geny, 16)); + MP_CHECKOK(mp_read_radix(&order, params->order, 16)); + + /* determine number of bits */ + bits = mpl_significant_bits(&irr) - 1; + if (bits < MP_OKAY) { + res = bits; + goto CLEANUP; + } + + /* determine which optimizations (if any) to use */ + if (params->field == ECField_GFp) { +#ifdef NSS_ECC_MORE_THAN_SUITE_B + switch (name) { +#ifdef ECL_USE_FP + case ECCurve_SECG_PRIME_160R1: + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_secp160r1_fp(group)); + break; +#endif + case ECCurve_SECG_PRIME_192R1: +#ifdef ECL_USE_FP + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_nistp192_fp(group)); +#else + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_gfp192(group, name)); +#endif + break; + case ECCurve_SECG_PRIME_224R1: +#ifdef ECL_USE_FP + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_nistp224_fp(group)); +#else + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_gfp224(group, name)); +#endif + break; + case ECCurve_SECG_PRIME_256R1: + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_gfp256(group, name)); + break; + case ECCurve_SECG_PRIME_521R1: + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_gfp521(group, name)); + break; + default: + /* use generic arithmetic */ +#endif + group = + ECGroup_consGFp_mont(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } +#ifdef NSS_ECC_MORE_THAN_SUITE_B + } + } else if (params->field == ECField_GF2m) { + group = ECGroup_consGF2m(&irr, NULL, &curvea, &curveb, &genx, &geny, &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + if ((name == ECCurve_NIST_K163) || + (name == ECCurve_NIST_B163) || + (name == ECCurve_SECG_CHAR2_163R1)) { + MP_CHECKOK(ec_group_set_gf2m163(group, name)); + } else if ((name == ECCurve_SECG_CHAR2_193R1) || + (name == ECCurve_SECG_CHAR2_193R2)) { + MP_CHECKOK(ec_group_set_gf2m193(group, name)); + } else if ((name == ECCurve_NIST_K233) || + (name == ECCurve_NIST_B233)) { + MP_CHECKOK(ec_group_set_gf2m233(group, name)); + } +#endif + } else { + res = MP_UNDEF; + goto CLEANUP; + } + + /* set name, if any */ + if ((group != NULL) && (params->text != NULL)) { +#ifdef _KERNEL + int n = strlen(params->text) + 1; + + group->text = kmem_alloc(n, kmflag); + if (group->text == NULL) { + res = MP_MEM; + goto CLEANUP; + } + bcopy(params->text, group->text, n); + group->text_len = n; +#else + group->text = strdup(params->text); + if (group->text == NULL) { + res = MP_MEM; + } +#endif + } + + CLEANUP: + mp_clear(&irr); + mp_clear(&curvea); + mp_clear(&curveb); + mp_clear(&genx); + mp_clear(&geny); + mp_clear(&order); + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} + +/* Construct ECGroup from hexadecimal representations of parameters. */ +ECGroup * +ECGroup_fromHex(const ECCurveParams * params, int kmflag) +{ + return ecgroup_fromNameAndHex(ECCurve_noName, params, kmflag); +} + +/* Construct ECGroup from named parameters. */ +ECGroup * +ECGroup_fromName(const ECCurveName name, int kmflag) +{ + ECGroup *group = NULL; + ECCurveParams *params = NULL; + mp_err res = MP_OKAY; + + params = EC_GetNamedCurveParams(name, kmflag); + if (params == NULL) { + res = MP_UNDEF; + goto CLEANUP; + } + + /* construct actual group */ + group = ecgroup_fromNameAndHex(name, params, kmflag); + if (group == NULL) { + res = MP_UNDEF; + goto CLEANUP; + } + + CLEANUP: + EC_FreeCurveParams(params); + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} + +/* Validates an EC public key as described in Section 5.2.2 of X9.62. */ +mp_err ECPoint_validate(const ECGroup *group, const mp_int *px, const + mp_int *py) +{ + /* 1: Verify that publicValue is not the point at infinity */ + /* 2: Verify that the coordinates of publicValue are elements + * of the field. + */ + /* 3: Verify that publicValue is on the curve. */ + /* 4: Verify that the order of the curve times the publicValue + * is the point at infinity. + */ + return group->validate_point(px, py, group); +} + +/* Free the memory allocated (if any) to an ECGroup object. */ +void +ECGroup_free(ECGroup *group) +{ + if (group == NULL) + return; + GFMethod_free(group->meth); + if (group->constructed == MP_NO) + return; + mp_clear(&group->curvea); + mp_clear(&group->curveb); + mp_clear(&group->genx); + mp_clear(&group->geny); + mp_clear(&group->order); + if (group->text != NULL) +#ifdef _KERNEL + kmem_free(group->text, group->text_len); +#else + free(group->text); +#endif + if (group->extra_free != NULL) + group->extra_free(group); +#ifdef _KERNEL + kmem_free(group, sizeof (ECGroup)); +#else + free(group); +#endif +} diff --git a/usr/src/common/crypto/ecc/ecl.h b/usr/src/common/crypto/ecc/ecl.h new file mode 100644 index 0000000000..cc73bacd4c --- /dev/null +++ b/usr/src/common/crypto/ecc/ecl.h @@ -0,0 +1,99 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _ECL_H +#define _ECL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Although this is not an exported header file, code which uses elliptic + * curve point operations will need to include it. */ + +#include "ecl-exp.h" +#include "mpi.h" + +struct ECGroupStr; +typedef struct ECGroupStr ECGroup; + +/* Construct ECGroup from hexadecimal representations of parameters. */ +ECGroup *ECGroup_fromHex(const ECCurveParams * params, int kmflag); + +/* Construct ECGroup from named parameters. */ +ECGroup *ECGroup_fromName(const ECCurveName name, int kmflag); + +/* Free an allocated ECGroup. */ +void ECGroup_free(ECGroup *group); + +/* Construct ECCurveParams from an ECCurveName */ +ECCurveParams *EC_GetNamedCurveParams(const ECCurveName name, int kmflag); + +/* Duplicates an ECCurveParams */ +ECCurveParams *ECCurveParams_dup(const ECCurveParams * params, int kmflag); + +/* Free an allocated ECCurveParams */ +void EC_FreeCurveParams(ECCurveParams * params); + +/* Elliptic curve scalar-point multiplication. Computes Q(x, y) = k * P(x, + * y). If x, y = NULL, then P is assumed to be the generator (base point) + * of the group of points on the elliptic curve. Input and output values + * are assumed to be NOT field-encoded. */ +mp_err ECPoint_mul(const ECGroup *group, const mp_int *k, const mp_int *px, + const mp_int *py, mp_int *qx, mp_int *qy); + +/* Elliptic curve scalar-point multiplication. Computes Q(x, y) = k1 * G + + * k2 * P(x, y), where G is the generator (base point) of the group of + * points on the elliptic curve. Input and output values are assumed to + * be NOT field-encoded. */ +mp_err ECPoints_mul(const ECGroup *group, const mp_int *k1, + const mp_int *k2, const mp_int *px, const mp_int *py, + mp_int *qx, mp_int *qy); + +/* Validates an EC public key as described in Section 5.2.2 of X9.62. + * Returns MP_YES if the public key is valid, MP_NO if the public key + * is invalid, or an error code if the validation could not be + * performed. */ +mp_err ECPoint_validate(const ECGroup *group, const mp_int *px, const + mp_int *py); + +#endif /* _ECL_H */ diff --git a/usr/src/common/crypto/ecc/ecl_curve.c b/usr/src/common/crypto/ecc/ecl_curve.c new file mode 100644 index 0000000000..1880f355d6 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecl_curve.c @@ -0,0 +1,204 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecl.h" +#include "ecl-curve.h" +#include "ecl-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#include <string.h> +#endif + +#define CHECK(func) if ((func) == NULL) { res = 0; goto CLEANUP; } + +/* Duplicates an ECCurveParams */ +ECCurveParams * +ECCurveParams_dup(const ECCurveParams * params, int kmflag) +{ + int res = 1; + ECCurveParams *ret = NULL; + +#ifdef _KERNEL + ret = (ECCurveParams *) kmem_zalloc(sizeof(ECCurveParams), kmflag); +#else + CHECK(ret = (ECCurveParams *) calloc(1, sizeof(ECCurveParams))); +#endif + if (params->text != NULL) { +#ifdef _KERNEL + ret->text = kmem_alloc(strlen(params->text) + 1, kmflag); + bcopy(params->text, ret->text, strlen(params->text) + 1); +#else + CHECK(ret->text = strdup(params->text)); +#endif + } + ret->field = params->field; + ret->size = params->size; + if (params->irr != NULL) { +#ifdef _KERNEL + ret->irr = kmem_alloc(strlen(params->irr) + 1, kmflag); + bcopy(params->irr, ret->irr, strlen(params->irr) + 1); +#else + CHECK(ret->irr = strdup(params->irr)); +#endif + } + if (params->curvea != NULL) { +#ifdef _KERNEL + ret->curvea = kmem_alloc(strlen(params->curvea) + 1, kmflag); + bcopy(params->curvea, ret->curvea, strlen(params->curvea) + 1); +#else + CHECK(ret->curvea = strdup(params->curvea)); +#endif + } + if (params->curveb != NULL) { +#ifdef _KERNEL + ret->curveb = kmem_alloc(strlen(params->curveb) + 1, kmflag); + bcopy(params->curveb, ret->curveb, strlen(params->curveb) + 1); +#else + CHECK(ret->curveb = strdup(params->curveb)); +#endif + } + if (params->genx != NULL) { +#ifdef _KERNEL + ret->genx = kmem_alloc(strlen(params->genx) + 1, kmflag); + bcopy(params->genx, ret->genx, strlen(params->genx) + 1); +#else + CHECK(ret->genx = strdup(params->genx)); +#endif + } + if (params->geny != NULL) { +#ifdef _KERNEL + ret->geny = kmem_alloc(strlen(params->geny) + 1, kmflag); + bcopy(params->geny, ret->geny, strlen(params->geny) + 1); +#else + CHECK(ret->geny = strdup(params->geny)); +#endif + } + if (params->order != NULL) { +#ifdef _KERNEL + ret->order = kmem_alloc(strlen(params->order) + 1, kmflag); + bcopy(params->order, ret->order, strlen(params->order) + 1); +#else + CHECK(ret->order = strdup(params->order)); +#endif + } + ret->cofactor = params->cofactor; + + CLEANUP: + if (res != 1) { + EC_FreeCurveParams(ret); + return NULL; + } + return ret; +} + +#undef CHECK + +/* Construct ECCurveParams from an ECCurveName */ +ECCurveParams * +EC_GetNamedCurveParams(const ECCurveName name, int kmflag) +{ + if ((name <= ECCurve_noName) || (ECCurve_pastLastCurve <= name) || + (ecCurve_map[name] == NULL)) { + return NULL; + } else { + return ECCurveParams_dup(ecCurve_map[name], kmflag); + } +} + +/* Free the memory allocated (if any) to an ECCurveParams object. */ +void +EC_FreeCurveParams(ECCurveParams * params) +{ + if (params == NULL) + return; + if (params->text != NULL) +#ifdef _KERNEL + kmem_free(params->text, strlen(params->text) + 1); +#else + free(params->text); +#endif + if (params->irr != NULL) +#ifdef _KERNEL + kmem_free(params->irr, strlen(params->irr) + 1); +#else + free(params->irr); +#endif + if (params->curvea != NULL) +#ifdef _KERNEL + kmem_free(params->curvea, strlen(params->curvea) + 1); +#else + free(params->curvea); +#endif + if (params->curveb != NULL) +#ifdef _KERNEL + kmem_free(params->curveb, strlen(params->curveb) + 1); +#else + free(params->curveb); +#endif + if (params->genx != NULL) +#ifdef _KERNEL + kmem_free(params->genx, strlen(params->genx) + 1); +#else + free(params->genx); +#endif + if (params->geny != NULL) +#ifdef _KERNEL + kmem_free(params->geny, strlen(params->geny) + 1); +#else + free(params->geny); +#endif + if (params->order != NULL) +#ifdef _KERNEL + kmem_free(params->order, strlen(params->order) + 1); +#else + free(params->order); +#endif +#ifdef _KERNEL + kmem_free(params, sizeof(ECCurveParams)); +#else + free(params); +#endif +} diff --git a/usr/src/common/crypto/ecc/ecl_gf.c b/usr/src/common/crypto/ecc/ecl_gf.c new file mode 100644 index 0000000000..073cf73f77 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecl_gf.c @@ -0,0 +1,1050 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stephen Fung <fungstep@hotmail.com> and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mpi.h" +#include "mp_gf2m.h" +#include "ecl-priv.h" +#include "mpi-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* Allocate memory for a new GFMethod object. */ +GFMethod * +GFMethod_new(int kmflag) +{ + mp_err res = MP_OKAY; + GFMethod *meth; +#ifdef _KERNEL + meth = (GFMethod *) kmem_alloc(sizeof(GFMethod), kmflag); +#else + meth = (GFMethod *) malloc(sizeof(GFMethod)); + if (meth == NULL) + return NULL; +#endif + meth->constructed = MP_YES; + MP_DIGITS(&meth->irr) = 0; + meth->extra_free = NULL; + MP_CHECKOK(mp_init(&meth->irr, kmflag)); + + CLEANUP: + if (res != MP_OKAY) { + GFMethod_free(meth); + return NULL; + } + return meth; +} + +/* Construct a generic GFMethod for arithmetic over prime fields with + * irreducible irr. */ +GFMethod * +GFMethod_consGFp(const mp_int *irr) +{ + mp_err res = MP_OKAY; + GFMethod *meth = NULL; + + meth = GFMethod_new(FLAG(irr)); + if (meth == NULL) + return NULL; + + MP_CHECKOK(mp_copy(irr, &meth->irr)); + meth->irr_arr[0] = mpl_significant_bits(irr); + meth->irr_arr[1] = meth->irr_arr[2] = meth->irr_arr[3] = + meth->irr_arr[4] = 0; + switch(MP_USED(&meth->irr)) { + /* maybe we need 1 and 2 words here as well?*/ + case 3: + meth->field_add = &ec_GFp_add_3; + meth->field_sub = &ec_GFp_sub_3; + break; + case 4: + meth->field_add = &ec_GFp_add_4; + meth->field_sub = &ec_GFp_sub_4; + break; + case 5: + meth->field_add = &ec_GFp_add_5; + meth->field_sub = &ec_GFp_sub_5; + break; + case 6: + meth->field_add = &ec_GFp_add_6; + meth->field_sub = &ec_GFp_sub_6; + break; + default: + meth->field_add = &ec_GFp_add; + meth->field_sub = &ec_GFp_sub; + } + meth->field_neg = &ec_GFp_neg; + meth->field_mod = &ec_GFp_mod; + meth->field_mul = &ec_GFp_mul; + meth->field_sqr = &ec_GFp_sqr; + meth->field_div = &ec_GFp_div; + meth->field_enc = NULL; + meth->field_dec = NULL; + meth->extra1 = NULL; + meth->extra2 = NULL; + meth->extra_free = NULL; + + CLEANUP: + if (res != MP_OKAY) { + GFMethod_free(meth); + return NULL; + } + return meth; +} + +/* Construct a generic GFMethod for arithmetic over binary polynomial + * fields with irreducible irr that has array representation irr_arr (see + * ecl-priv.h for description of the representation). If irr_arr is NULL, + * then it is constructed from the bitstring representation. */ +GFMethod * +GFMethod_consGF2m(const mp_int *irr, const unsigned int irr_arr[5]) +{ + mp_err res = MP_OKAY; + int ret; + GFMethod *meth = NULL; + + meth = GFMethod_new(FLAG(irr)); + if (meth == NULL) + return NULL; + + MP_CHECKOK(mp_copy(irr, &meth->irr)); + if (irr_arr != NULL) { + /* Irreducible polynomials are either trinomials or pentanomials. */ + meth->irr_arr[0] = irr_arr[0]; + meth->irr_arr[1] = irr_arr[1]; + meth->irr_arr[2] = irr_arr[2]; + if (irr_arr[2] > 0) { + meth->irr_arr[3] = irr_arr[3]; + meth->irr_arr[4] = irr_arr[4]; + } else { + meth->irr_arr[3] = meth->irr_arr[4] = 0; + } + } else { + ret = mp_bpoly2arr(irr, meth->irr_arr, 5); + /* Irreducible polynomials are either trinomials or pentanomials. */ + if ((ret != 5) && (ret != 3)) { + res = MP_UNDEF; + goto CLEANUP; + } + } + meth->field_add = &ec_GF2m_add; + meth->field_neg = &ec_GF2m_neg; + meth->field_sub = &ec_GF2m_add; + meth->field_mod = &ec_GF2m_mod; + meth->field_mul = &ec_GF2m_mul; + meth->field_sqr = &ec_GF2m_sqr; + meth->field_div = &ec_GF2m_div; + meth->field_enc = NULL; + meth->field_dec = NULL; + meth->extra1 = NULL; + meth->extra2 = NULL; + meth->extra_free = NULL; + + CLEANUP: + if (res != MP_OKAY) { + GFMethod_free(meth); + return NULL; + } + return meth; +} + +/* Free the memory allocated (if any) to a GFMethod object. */ +void +GFMethod_free(GFMethod *meth) +{ + if (meth == NULL) + return; + if (meth->constructed == MP_NO) + return; + mp_clear(&meth->irr); + if (meth->extra_free != NULL) + meth->extra_free(meth); +#ifdef _KERNEL + kmem_free(meth, sizeof(GFMethod)); +#else + free(meth); +#endif +} + +/* Wrapper functions for generic prime field arithmetic. */ + +/* Add two field elements. Assumes that 0 <= a, b < meth->irr */ +mp_err +ec_GFp_add(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + /* PRE: 0 <= a, b < p = meth->irr POST: 0 <= r < p, r = a + b (mod p) */ + mp_err res; + + if ((res = mp_add(a, b, r)) != MP_OKAY) { + return res; + } + if (mp_cmp(r, &meth->irr) >= 0) { + return mp_sub(r, &meth->irr, r); + } + return res; +} + +/* Negates a field element. Assumes that 0 <= a < meth->irr */ +mp_err +ec_GFp_neg(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + /* PRE: 0 <= a < p = meth->irr POST: 0 <= r < p, r = -a (mod p) */ + + if (mp_cmp_z(a) == 0) { + mp_zero(r); + return MP_OKAY; + } + return mp_sub(&meth->irr, a, r); +} + +/* Subtracts two field elements. Assumes that 0 <= a, b < meth->irr */ +mp_err +ec_GFp_sub(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + /* PRE: 0 <= a, b < p = meth->irr POST: 0 <= r < p, r = a - b (mod p) */ + res = mp_sub(a, b, r); + if (res == MP_RANGE) { + MP_CHECKOK(mp_sub(b, a, r)); + if (mp_cmp_z(r) < 0) { + MP_CHECKOK(mp_add(r, &meth->irr, r)); + } + MP_CHECKOK(ec_GFp_neg(r, r, meth)); + } + if (mp_cmp_z(r) < 0) { + MP_CHECKOK(mp_add(r, &meth->irr, r)); + } + CLEANUP: + return res; +} +/* + * Inline adds for small curve lengths. + */ +/* 3 words */ +mp_err +ec_GFp_add_3(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a0 = 0, a1 = 0, a2 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0; + mp_digit carry; + + switch(MP_USED(a)) { + case 3: + a2 = MP_DIGIT(a,2); + case 2: + a1 = MP_DIGIT(a,1); + case 1: + a0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 3: + r2 = MP_DIGIT(b,2); + case 2: + r1 = MP_DIGIT(b,1); + case 1: + r0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + MP_ADD_CARRY(a0, r0, r0, 0, carry); + MP_ADD_CARRY(a1, r1, r1, carry, carry); + MP_ADD_CARRY(a2, r2, r2, carry, carry); +#else + __asm__ ( + "xorq %3,%3 \n\t" + "addq %4,%0 \n\t" + "adcq %5,%1 \n\t" + "adcq %6,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(carry) + : "r" (a0), "r" (a1), "r" (a2), + "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + + MP_CHECKOK(s_mp_pad(r, 3)); + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 3; + + /* Do quick 'subract' if we've gone over + * (add the 2's complement of the curve field) */ + a2 = MP_DIGIT(&meth->irr,2); + if (carry || r2 > a2 || + ((r2 == a2) && mp_cmp(r,&meth->irr) != MP_LT)) { + a1 = MP_DIGIT(&meth->irr,1); + a0 = MP_DIGIT(&meth->irr,0); +#ifndef MPI_AMD64_ADD + MP_SUB_BORROW(r0, a0, r0, 0, carry); + MP_SUB_BORROW(r1, a1, r1, carry, carry); + MP_SUB_BORROW(r2, a2, r2, carry, carry); +#else + __asm__ ( + "subq %3,%0 \n\t" + "sbbq %4,%1 \n\t" + "sbbq %5,%2 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2) + : "r" (a0), "r" (a1), "r" (a2), + "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + } + + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 4 words */ +mp_err +ec_GFp_add_4(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a0 = 0, a1 = 0, a2 = 0, a3 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0; + mp_digit carry; + + switch(MP_USED(a)) { + case 4: + a3 = MP_DIGIT(a,3); + case 3: + a2 = MP_DIGIT(a,2); + case 2: + a1 = MP_DIGIT(a,1); + case 1: + a0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 4: + r3 = MP_DIGIT(b,3); + case 3: + r2 = MP_DIGIT(b,2); + case 2: + r1 = MP_DIGIT(b,1); + case 1: + r0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + MP_ADD_CARRY(a0, r0, r0, 0, carry); + MP_ADD_CARRY(a1, r1, r1, carry, carry); + MP_ADD_CARRY(a2, r2, r2, carry, carry); + MP_ADD_CARRY(a3, r3, r3, carry, carry); +#else + __asm__ ( + "xorq %4,%4 \n\t" + "addq %5,%0 \n\t" + "adcq %6,%1 \n\t" + "adcq %7,%2 \n\t" + "adcq %8,%3 \n\t" + "adcq $0,%4 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3), "=r"(carry) + : "r" (a0), "r" (a1), "r" (a2), "r" (a3), + "0" (r0), "1" (r1), "2" (r2), "3" (r3) + : "%cc" ); +#endif + + MP_CHECKOK(s_mp_pad(r, 4)); + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 4; + + /* Do quick 'subract' if we've gone over + * (add the 2's complement of the curve field) */ + a3 = MP_DIGIT(&meth->irr,3); + if (carry || r3 > a3 || + ((r3 == a3) && mp_cmp(r,&meth->irr) != MP_LT)) { + a2 = MP_DIGIT(&meth->irr,2); + a1 = MP_DIGIT(&meth->irr,1); + a0 = MP_DIGIT(&meth->irr,0); +#ifndef MPI_AMD64_ADD + MP_SUB_BORROW(r0, a0, r0, 0, carry); + MP_SUB_BORROW(r1, a1, r1, carry, carry); + MP_SUB_BORROW(r2, a2, r2, carry, carry); + MP_SUB_BORROW(r3, a3, r3, carry, carry); +#else + __asm__ ( + "subq %4,%0 \n\t" + "sbbq %5,%1 \n\t" + "sbbq %6,%2 \n\t" + "sbbq %7,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3) + : "r" (a0), "r" (a1), "r" (a2), "r" (a3), + "0" (r0), "1" (r1), "2" (r2), "3" (r3) + : "%cc" ); +#endif + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + } + + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 5 words */ +mp_err +ec_GFp_add_5(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0; + mp_digit carry; + + switch(MP_USED(a)) { + case 5: + a4 = MP_DIGIT(a,4); + case 4: + a3 = MP_DIGIT(a,3); + case 3: + a2 = MP_DIGIT(a,2); + case 2: + a1 = MP_DIGIT(a,1); + case 1: + a0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 5: + r4 = MP_DIGIT(b,4); + case 4: + r3 = MP_DIGIT(b,3); + case 3: + r2 = MP_DIGIT(b,2); + case 2: + r1 = MP_DIGIT(b,1); + case 1: + r0 = MP_DIGIT(b,0); + } + + MP_ADD_CARRY(a0, r0, r0, 0, carry); + MP_ADD_CARRY(a1, r1, r1, carry, carry); + MP_ADD_CARRY(a2, r2, r2, carry, carry); + MP_ADD_CARRY(a3, r3, r3, carry, carry); + MP_ADD_CARRY(a4, r4, r4, carry, carry); + + MP_CHECKOK(s_mp_pad(r, 5)); + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 5; + + /* Do quick 'subract' if we've gone over + * (add the 2's complement of the curve field) */ + a4 = MP_DIGIT(&meth->irr,4); + if (carry || r4 > a4 || + ((r4 == a4) && mp_cmp(r,&meth->irr) != MP_LT)) { + a3 = MP_DIGIT(&meth->irr,3); + a2 = MP_DIGIT(&meth->irr,2); + a1 = MP_DIGIT(&meth->irr,1); + a0 = MP_DIGIT(&meth->irr,0); + MP_SUB_BORROW(r0, a0, r0, 0, carry); + MP_SUB_BORROW(r1, a1, r1, carry, carry); + MP_SUB_BORROW(r2, a2, r2, carry, carry); + MP_SUB_BORROW(r3, a3, r3, carry, carry); + MP_SUB_BORROW(r4, a4, r4, carry, carry); + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + } + + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 6 words */ +mp_err +ec_GFp_add_6(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0; + mp_digit carry; + + switch(MP_USED(a)) { + case 6: + a5 = MP_DIGIT(a,5); + case 5: + a4 = MP_DIGIT(a,4); + case 4: + a3 = MP_DIGIT(a,3); + case 3: + a2 = MP_DIGIT(a,2); + case 2: + a1 = MP_DIGIT(a,1); + case 1: + a0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 6: + r5 = MP_DIGIT(b,5); + case 5: + r4 = MP_DIGIT(b,4); + case 4: + r3 = MP_DIGIT(b,3); + case 3: + r2 = MP_DIGIT(b,2); + case 2: + r1 = MP_DIGIT(b,1); + case 1: + r0 = MP_DIGIT(b,0); + } + + MP_ADD_CARRY(a0, r0, r0, 0, carry); + MP_ADD_CARRY(a1, r1, r1, carry, carry); + MP_ADD_CARRY(a2, r2, r2, carry, carry); + MP_ADD_CARRY(a3, r3, r3, carry, carry); + MP_ADD_CARRY(a4, r4, r4, carry, carry); + MP_ADD_CARRY(a5, r5, r5, carry, carry); + + MP_CHECKOK(s_mp_pad(r, 6)); + MP_DIGIT(r, 5) = r5; + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 6; + + /* Do quick 'subract' if we've gone over + * (add the 2's complement of the curve field) */ + a5 = MP_DIGIT(&meth->irr,5); + if (carry || r5 > a5 || + ((r5 == a5) && mp_cmp(r,&meth->irr) != MP_LT)) { + a4 = MP_DIGIT(&meth->irr,4); + a3 = MP_DIGIT(&meth->irr,3); + a2 = MP_DIGIT(&meth->irr,2); + a1 = MP_DIGIT(&meth->irr,1); + a0 = MP_DIGIT(&meth->irr,0); + MP_SUB_BORROW(r0, a0, r0, 0, carry); + MP_SUB_BORROW(r1, a1, r1, carry, carry); + MP_SUB_BORROW(r2, a2, r2, carry, carry); + MP_SUB_BORROW(r3, a3, r3, carry, carry); + MP_SUB_BORROW(r4, a4, r4, carry, carry); + MP_SUB_BORROW(r5, a5, r5, carry, carry); + MP_DIGIT(r, 5) = r5; + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + } + + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* + * The following subraction functions do in-line subractions based + * on our curve size. + * + * ... 3 words + */ +mp_err +ec_GFp_sub_3(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit b0 = 0, b1 = 0, b2 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0; + mp_digit borrow; + + switch(MP_USED(a)) { + case 3: + r2 = MP_DIGIT(a,2); + case 2: + r1 = MP_DIGIT(a,1); + case 1: + r0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 3: + b2 = MP_DIGIT(b,2); + case 2: + b1 = MP_DIGIT(b,1); + case 1: + b0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + MP_SUB_BORROW(r0, b0, r0, 0, borrow); + MP_SUB_BORROW(r1, b1, r1, borrow, borrow); + MP_SUB_BORROW(r2, b2, r2, borrow, borrow); +#else + __asm__ ( + "xorq %3,%3 \n\t" + "subq %4,%0 \n\t" + "sbbq %5,%1 \n\t" + "sbbq %6,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r" (borrow) + : "r" (b0), "r" (b1), "r" (b2), + "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + + /* Do quick 'add' if we've gone under 0 + * (subtract the 2's complement of the curve field) */ + if (borrow) { + b2 = MP_DIGIT(&meth->irr,2); + b1 = MP_DIGIT(&meth->irr,1); + b0 = MP_DIGIT(&meth->irr,0); +#ifndef MPI_AMD64_ADD + MP_ADD_CARRY(b0, r0, r0, 0, borrow); + MP_ADD_CARRY(b1, r1, r1, borrow, borrow); + MP_ADD_CARRY(b2, r2, r2, borrow, borrow); +#else + __asm__ ( + "addq %3,%0 \n\t" + "adcq %4,%1 \n\t" + "adcq %5,%2 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2) + : "r" (b0), "r" (b1), "r" (b2), + "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + } + +#ifdef MPI_AMD64_ADD + /* compiler fakeout? */ + if ((r2 == b0) && (r1 == b0) && (r0 == b0)) { + MP_CHECKOK(s_mp_pad(r, 4)); + } +#endif + MP_CHECKOK(s_mp_pad(r, 3)); + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 3; + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 4 words */ +mp_err +ec_GFp_sub_4(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit b0 = 0, b1 = 0, b2 = 0, b3 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0; + mp_digit borrow; + + switch(MP_USED(a)) { + case 4: + r3 = MP_DIGIT(a,3); + case 3: + r2 = MP_DIGIT(a,2); + case 2: + r1 = MP_DIGIT(a,1); + case 1: + r0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 4: + b3 = MP_DIGIT(b,3); + case 3: + b2 = MP_DIGIT(b,2); + case 2: + b1 = MP_DIGIT(b,1); + case 1: + b0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + MP_SUB_BORROW(r0, b0, r0, 0, borrow); + MP_SUB_BORROW(r1, b1, r1, borrow, borrow); + MP_SUB_BORROW(r2, b2, r2, borrow, borrow); + MP_SUB_BORROW(r3, b3, r3, borrow, borrow); +#else + __asm__ ( + "xorq %4,%4 \n\t" + "subq %5,%0 \n\t" + "sbbq %6,%1 \n\t" + "sbbq %7,%2 \n\t" + "sbbq %8,%3 \n\t" + "adcq $0,%4 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3), "=r" (borrow) + : "r" (b0), "r" (b1), "r" (b2), "r" (b3), + "0" (r0), "1" (r1), "2" (r2), "3" (r3) + : "%cc" ); +#endif + + /* Do quick 'add' if we've gone under 0 + * (subtract the 2's complement of the curve field) */ + if (borrow) { + b3 = MP_DIGIT(&meth->irr,3); + b2 = MP_DIGIT(&meth->irr,2); + b1 = MP_DIGIT(&meth->irr,1); + b0 = MP_DIGIT(&meth->irr,0); +#ifndef MPI_AMD64_ADD + MP_ADD_CARRY(b0, r0, r0, 0, borrow); + MP_ADD_CARRY(b1, r1, r1, borrow, borrow); + MP_ADD_CARRY(b2, r2, r2, borrow, borrow); + MP_ADD_CARRY(b3, r3, r3, borrow, borrow); +#else + __asm__ ( + "addq %4,%0 \n\t" + "adcq %5,%1 \n\t" + "adcq %6,%2 \n\t" + "adcq %7,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3) + : "r" (b0), "r" (b1), "r" (b2), "r" (b3), + "0" (r0), "1" (r1), "2" (r2), "3" (r3) + : "%cc" ); +#endif + } +#ifdef MPI_AMD64_ADD + /* compiler fakeout? */ + if ((r3 == b0) && (r1 == b0) && (r0 == b0)) { + MP_CHECKOK(s_mp_pad(r, 4)); + } +#endif + MP_CHECKOK(s_mp_pad(r, 4)); + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 4; + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 5 words */ +mp_err +ec_GFp_sub_5(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit b0 = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0; + mp_digit borrow; + + switch(MP_USED(a)) { + case 5: + r4 = MP_DIGIT(a,4); + case 4: + r3 = MP_DIGIT(a,3); + case 3: + r2 = MP_DIGIT(a,2); + case 2: + r1 = MP_DIGIT(a,1); + case 1: + r0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 5: + b4 = MP_DIGIT(b,4); + case 4: + b3 = MP_DIGIT(b,3); + case 3: + b2 = MP_DIGIT(b,2); + case 2: + b1 = MP_DIGIT(b,1); + case 1: + b0 = MP_DIGIT(b,0); + } + + MP_SUB_BORROW(r0, b0, r0, 0, borrow); + MP_SUB_BORROW(r1, b1, r1, borrow, borrow); + MP_SUB_BORROW(r2, b2, r2, borrow, borrow); + MP_SUB_BORROW(r3, b3, r3, borrow, borrow); + MP_SUB_BORROW(r4, b4, r4, borrow, borrow); + + /* Do quick 'add' if we've gone under 0 + * (subtract the 2's complement of the curve field) */ + if (borrow) { + b4 = MP_DIGIT(&meth->irr,4); + b3 = MP_DIGIT(&meth->irr,3); + b2 = MP_DIGIT(&meth->irr,2); + b1 = MP_DIGIT(&meth->irr,1); + b0 = MP_DIGIT(&meth->irr,0); + MP_ADD_CARRY(b0, r0, r0, 0, borrow); + MP_ADD_CARRY(b1, r1, r1, borrow, borrow); + MP_ADD_CARRY(b2, r2, r2, borrow, borrow); + MP_ADD_CARRY(b3, r3, r3, borrow, borrow); + } + MP_CHECKOK(s_mp_pad(r, 5)); + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 5; + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 6 words */ +mp_err +ec_GFp_sub_6(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit b0 = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, b5 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0; + mp_digit borrow; + + switch(MP_USED(a)) { + case 6: + r5 = MP_DIGIT(a,5); + case 5: + r4 = MP_DIGIT(a,4); + case 4: + r3 = MP_DIGIT(a,3); + case 3: + r2 = MP_DIGIT(a,2); + case 2: + r1 = MP_DIGIT(a,1); + case 1: + r0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 6: + b5 = MP_DIGIT(b,5); + case 5: + b4 = MP_DIGIT(b,4); + case 4: + b3 = MP_DIGIT(b,3); + case 3: + b2 = MP_DIGIT(b,2); + case 2: + b1 = MP_DIGIT(b,1); + case 1: + b0 = MP_DIGIT(b,0); + } + + MP_SUB_BORROW(r0, b0, r0, 0, borrow); + MP_SUB_BORROW(r1, b1, r1, borrow, borrow); + MP_SUB_BORROW(r2, b2, r2, borrow, borrow); + MP_SUB_BORROW(r3, b3, r3, borrow, borrow); + MP_SUB_BORROW(r4, b4, r4, borrow, borrow); + MP_SUB_BORROW(r5, b5, r5, borrow, borrow); + + /* Do quick 'add' if we've gone under 0 + * (subtract the 2's complement of the curve field) */ + if (borrow) { + b5 = MP_DIGIT(&meth->irr,5); + b4 = MP_DIGIT(&meth->irr,4); + b3 = MP_DIGIT(&meth->irr,3); + b2 = MP_DIGIT(&meth->irr,2); + b1 = MP_DIGIT(&meth->irr,1); + b0 = MP_DIGIT(&meth->irr,0); + MP_ADD_CARRY(b0, r0, r0, 0, borrow); + MP_ADD_CARRY(b1, r1, r1, borrow, borrow); + MP_ADD_CARRY(b2, r2, r2, borrow, borrow); + MP_ADD_CARRY(b3, r3, r3, borrow, borrow); + MP_ADD_CARRY(b4, r4, r4, borrow, borrow); + } + + MP_CHECKOK(s_mp_pad(r, 6)); + MP_DIGIT(r, 5) = r5; + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 6; + s_mp_clamp(r); + + CLEANUP: + return res; +} + + +/* Reduces an integer to a field element. */ +mp_err +ec_GFp_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + return mp_mod(a, &meth->irr, r); +} + +/* Multiplies two field elements. */ +mp_err +ec_GFp_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + return mp_mulmod(a, b, &meth->irr, r); +} + +/* Squares a field element. */ +mp_err +ec_GFp_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + return mp_sqrmod(a, &meth->irr, r); +} + +/* Divides two field elements. If a is NULL, then returns the inverse of + * b. */ +mp_err +ec_GFp_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_int t; + + /* If a is NULL, then return the inverse of b, otherwise return a/b. */ + if (a == NULL) { + return mp_invmod(b, &meth->irr, r); + } else { + /* MPI doesn't support divmod, so we implement it using invmod and + * mulmod. */ + MP_CHECKOK(mp_init(&t, FLAG(b))); + MP_CHECKOK(mp_invmod(b, &meth->irr, &t)); + MP_CHECKOK(mp_mulmod(a, &t, &meth->irr, r)); + CLEANUP: + mp_clear(&t); + return res; + } +} + +/* Wrapper functions for generic binary polynomial field arithmetic. */ + +/* Adds two field elements. */ +mp_err +ec_GF2m_add(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + return mp_badd(a, b, r); +} + +/* Negates a field element. Note that for binary polynomial fields, the + * negation of a field element is the field element itself. */ +mp_err +ec_GF2m_neg(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + if (a == r) { + return MP_OKAY; + } else { + return mp_copy(a, r); + } +} + +/* Reduces a binary polynomial to a field element. */ +mp_err +ec_GF2m_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + return mp_bmod(a, meth->irr_arr, r); +} + +/* Multiplies two field elements. */ +mp_err +ec_GF2m_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + return mp_bmulmod(a, b, meth->irr_arr, r); +} + +/* Squares a field element. */ +mp_err +ec_GF2m_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + return mp_bsqrmod(a, meth->irr_arr, r); +} + +/* Divides two field elements. If a is NULL, then returns the inverse of + * b. */ +mp_err +ec_GF2m_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_int t; + + /* If a is NULL, then return the inverse of b, otherwise return a/b. */ + if (a == NULL) { + /* The GF(2^m) portion of MPI doesn't support invmod, so we + * compute 1/b. */ + MP_CHECKOK(mp_init(&t, FLAG(b))); + MP_CHECKOK(mp_set_int(&t, 1)); + MP_CHECKOK(mp_bdivmod(&t, b, &meth->irr, meth->irr_arr, r)); + CLEANUP: + mp_clear(&t); + return res; + } else { + return mp_bdivmod(a, b, &meth->irr, meth->irr_arr, r); + } +} diff --git a/usr/src/common/crypto/ecc/ecl_mult.c b/usr/src/common/crypto/ecc/ecl_mult.c new file mode 100644 index 0000000000..c5c6535525 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecl_mult.c @@ -0,0 +1,366 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mpi.h" +#include "mplogic.h" +#include "ecl.h" +#include "ecl-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k * P(x, + * y). If x, y = NULL, then P is assumed to be the generator (base point) + * of the group of points on the elliptic curve. Input and output values + * are assumed to be NOT field-encoded. */ +mp_err +ECPoint_mul(const ECGroup *group, const mp_int *k, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry) +{ + mp_err res = MP_OKAY; + mp_int kt; + + ARGCHK((k != NULL) && (group != NULL), MP_BADARG); + MP_DIGITS(&kt) = 0; + + /* want scalar to be less than or equal to group order */ + if (mp_cmp(k, &group->order) > 0) { + MP_CHECKOK(mp_init(&kt, FLAG(k))); + MP_CHECKOK(mp_mod(k, &group->order, &kt)); + } else { + MP_SIGN(&kt) = MP_ZPOS; + MP_USED(&kt) = MP_USED(k); + MP_ALLOC(&kt) = MP_ALLOC(k); + MP_DIGITS(&kt) = MP_DIGITS(k); + } + + if ((px == NULL) || (py == NULL)) { + if (group->base_point_mul) { + MP_CHECKOK(group->base_point_mul(&kt, rx, ry, group)); + } else { + MP_CHECKOK(group-> + point_mul(&kt, &group->genx, &group->geny, rx, ry, + group)); + } + } else { + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(px, rx, group->meth)); + MP_CHECKOK(group->meth->field_enc(py, ry, group->meth)); + MP_CHECKOK(group->point_mul(&kt, rx, ry, rx, ry, group)); + } else { + MP_CHECKOK(group->point_mul(&kt, px, py, rx, ry, group)); + } + } + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_dec(ry, ry, group->meth)); + } + + CLEANUP: + if (MP_DIGITS(&kt) != MP_DIGITS(k)) { + mp_clear(&kt); + } + return res; +} + +/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k1 * G + + * k2 * P(x, y), where G is the generator (base point) of the group of + * points on the elliptic curve. Allows k1 = NULL or { k2, P } = NULL. + * Input and output values are assumed to be NOT field-encoded. */ +mp_err +ec_pts_mul_basic(const mp_int *k1, const mp_int *k2, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int sx, sy; + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK(!((k1 == NULL) + && ((k2 == NULL) || (px == NULL) + || (py == NULL))), MP_BADARG); + + /* if some arguments are not defined used ECPoint_mul */ + if (k1 == NULL) { + return ECPoint_mul(group, k2, px, py, rx, ry); + } else if ((k2 == NULL) || (px == NULL) || (py == NULL)) { + return ECPoint_mul(group, k1, NULL, NULL, rx, ry); + } + + MP_DIGITS(&sx) = 0; + MP_DIGITS(&sy) = 0; + MP_CHECKOK(mp_init(&sx, FLAG(k1))); + MP_CHECKOK(mp_init(&sy, FLAG(k1))); + + MP_CHECKOK(ECPoint_mul(group, k1, NULL, NULL, &sx, &sy)); + MP_CHECKOK(ECPoint_mul(group, k2, px, py, rx, ry)); + + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(&sx, &sx, group->meth)); + MP_CHECKOK(group->meth->field_enc(&sy, &sy, group->meth)); + MP_CHECKOK(group->meth->field_enc(rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_enc(ry, ry, group->meth)); + } + + MP_CHECKOK(group->point_add(&sx, &sy, rx, ry, rx, ry, group)); + + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_dec(ry, ry, group->meth)); + } + + CLEANUP: + mp_clear(&sx); + mp_clear(&sy); + return res; +} + +/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k1 * G + + * k2 * P(x, y), where G is the generator (base point) of the group of + * points on the elliptic curve. Allows k1 = NULL or { k2, P } = NULL. + * Input and output values are assumed to be NOT field-encoded. Uses + * algorithm 15 (simultaneous multiple point multiplication) from Brown, + * Hankerson, Lopez, Menezes. Software Implementation of the NIST + * Elliptic Curves over Prime Fields. */ +mp_err +ec_pts_mul_simul_w2(const mp_int *k1, const mp_int *k2, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int precomp[4][4][2]; + const mp_int *a, *b; + int i, j; + int ai, bi, d; + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK(!((k1 == NULL) + && ((k2 == NULL) || (px == NULL) + || (py == NULL))), MP_BADARG); + + /* if some arguments are not defined used ECPoint_mul */ + if (k1 == NULL) { + return ECPoint_mul(group, k2, px, py, rx, ry); + } else if ((k2 == NULL) || (px == NULL) || (py == NULL)) { + return ECPoint_mul(group, k1, NULL, NULL, rx, ry); + } + + /* initialize precomputation table */ + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + MP_DIGITS(&precomp[i][j][0]) = 0; + MP_DIGITS(&precomp[i][j][1]) = 0; + } + } + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + MP_CHECKOK( mp_init_size(&precomp[i][j][0], + ECL_MAX_FIELD_SIZE_DIGITS, FLAG(k1)) ); + MP_CHECKOK( mp_init_size(&precomp[i][j][1], + ECL_MAX_FIELD_SIZE_DIGITS, FLAG(k1)) ); + } + } + + /* fill precomputation table */ + /* assign {k1, k2} = {a, b} such that len(a) >= len(b) */ + if (mpl_significant_bits(k1) < mpl_significant_bits(k2)) { + a = k2; + b = k1; + if (group->meth->field_enc) { + MP_CHECKOK(group->meth-> + field_enc(px, &precomp[1][0][0], group->meth)); + MP_CHECKOK(group->meth-> + field_enc(py, &precomp[1][0][1], group->meth)); + } else { + MP_CHECKOK(mp_copy(px, &precomp[1][0][0])); + MP_CHECKOK(mp_copy(py, &precomp[1][0][1])); + } + MP_CHECKOK(mp_copy(&group->genx, &precomp[0][1][0])); + MP_CHECKOK(mp_copy(&group->geny, &precomp[0][1][1])); + } else { + a = k1; + b = k2; + MP_CHECKOK(mp_copy(&group->genx, &precomp[1][0][0])); + MP_CHECKOK(mp_copy(&group->geny, &precomp[1][0][1])); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth-> + field_enc(px, &precomp[0][1][0], group->meth)); + MP_CHECKOK(group->meth-> + field_enc(py, &precomp[0][1][1], group->meth)); + } else { + MP_CHECKOK(mp_copy(px, &precomp[0][1][0])); + MP_CHECKOK(mp_copy(py, &precomp[0][1][1])); + } + } + /* precompute [*][0][*] */ + mp_zero(&precomp[0][0][0]); + mp_zero(&precomp[0][0][1]); + MP_CHECKOK(group-> + point_dbl(&precomp[1][0][0], &precomp[1][0][1], + &precomp[2][0][0], &precomp[2][0][1], group)); + MP_CHECKOK(group-> + point_add(&precomp[1][0][0], &precomp[1][0][1], + &precomp[2][0][0], &precomp[2][0][1], + &precomp[3][0][0], &precomp[3][0][1], group)); + /* precompute [*][1][*] */ + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][1][0], &precomp[0][1][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][1][0], &precomp[i][1][1], group)); + } + /* precompute [*][2][*] */ + MP_CHECKOK(group-> + point_dbl(&precomp[0][1][0], &precomp[0][1][1], + &precomp[0][2][0], &precomp[0][2][1], group)); + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][2][0], &precomp[0][2][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][2][0], &precomp[i][2][1], group)); + } + /* precompute [*][3][*] */ + MP_CHECKOK(group-> + point_add(&precomp[0][1][0], &precomp[0][1][1], + &precomp[0][2][0], &precomp[0][2][1], + &precomp[0][3][0], &precomp[0][3][1], group)); + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][3][0], &precomp[0][3][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][3][0], &precomp[i][3][1], group)); + } + + d = (mpl_significant_bits(a) + 1) / 2; + + /* R = inf */ + mp_zero(rx); + mp_zero(ry); + + for (i = d - 1; i >= 0; i--) { + ai = MP_GET_BIT(a, 2 * i + 1); + ai <<= 1; + ai |= MP_GET_BIT(a, 2 * i); + bi = MP_GET_BIT(b, 2 * i + 1); + bi <<= 1; + bi |= MP_GET_BIT(b, 2 * i); + /* R = 2^2 * R */ + MP_CHECKOK(group->point_dbl(rx, ry, rx, ry, group)); + MP_CHECKOK(group->point_dbl(rx, ry, rx, ry, group)); + /* R = R + (ai * A + bi * B) */ + MP_CHECKOK(group-> + point_add(rx, ry, &precomp[ai][bi][0], + &precomp[ai][bi][1], rx, ry, group)); + } + + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_dec(ry, ry, group->meth)); + } + + CLEANUP: + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + mp_clear(&precomp[i][j][0]); + mp_clear(&precomp[i][j][1]); + } + } + return res; +} + +/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k1 * G + + * k2 * P(x, y), where G is the generator (base point) of the group of + * points on the elliptic curve. Allows k1 = NULL or { k2, P } = NULL. + * Input and output values are assumed to be NOT field-encoded. */ +mp_err +ECPoints_mul(const ECGroup *group, const mp_int *k1, const mp_int *k2, + const mp_int *px, const mp_int *py, mp_int *rx, mp_int *ry) +{ + mp_err res = MP_OKAY; + mp_int k1t, k2t; + const mp_int *k1p, *k2p; + + MP_DIGITS(&k1t) = 0; + MP_DIGITS(&k2t) = 0; + + ARGCHK(group != NULL, MP_BADARG); + + /* want scalar to be less than or equal to group order */ + if (k1 != NULL) { + if (mp_cmp(k1, &group->order) >= 0) { + MP_CHECKOK(mp_init(&k1t, FLAG(k1))); + MP_CHECKOK(mp_mod(k1, &group->order, &k1t)); + k1p = &k1t; + } else { + k1p = k1; + } + } else { + k1p = k1; + } + if (k2 != NULL) { + if (mp_cmp(k2, &group->order) >= 0) { + MP_CHECKOK(mp_init(&k2t, FLAG(k2))); + MP_CHECKOK(mp_mod(k2, &group->order, &k2t)); + k2p = &k2t; + } else { + k2p = k2; + } + } else { + k2p = k2; + } + + /* if points_mul is defined, then use it */ + if (group->points_mul) { + res = group->points_mul(k1p, k2p, px, py, rx, ry, group); + } else { + res = ec_pts_mul_simul_w2(k1p, k2p, px, py, rx, ry, group); + } + + CLEANUP: + mp_clear(&k1t); + mp_clear(&k2t); + return res; +} diff --git a/usr/src/common/crypto/ecc/ecp.h b/usr/src/common/crypto/ecc/ecp.h new file mode 100644 index 0000000000..e03849cb6e --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp.h @@ -0,0 +1,148 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for prime field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _ECP_H +#define _ECP_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecl-priv.h" + +/* Checks if point P(px, py) is at infinity. Uses affine coordinates. */ +mp_err ec_GFp_pt_is_inf_aff(const mp_int *px, const mp_int *py); + +/* Sets P(px, py) to be the point at infinity. Uses affine coordinates. */ +mp_err ec_GFp_pt_set_inf_aff(mp_int *px, mp_int *py); + +/* Computes R = P + Q where R is (rx, ry), P is (px, py) and Q is (qx, + * qy). Uses affine coordinates. */ +mp_err ec_GFp_pt_add_aff(const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Computes R = P - Q. Uses affine coordinates. */ +mp_err ec_GFp_pt_sub_aff(const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Computes R = 2P. Uses affine coordinates. */ +mp_err ec_GFp_pt_dbl_aff(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Validates a point on a GFp curve. */ +mp_err ec_GFp_validate_point(const mp_int *px, const mp_int *py, const ECGroup *group); + +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GFp. Uses affine coordinates. */ +mp_err ec_GFp_pt_mul_aff(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); +#endif + +/* Converts a point P(px, py) from affine coordinates to Jacobian + * projective coordinates R(rx, ry, rz). */ +mp_err ec_GFp_pt_aff2jac(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group); + +/* Converts a point P(px, py, pz) from Jacobian projective coordinates to + * affine coordinates R(rx, ry). */ +mp_err ec_GFp_pt_jac2aff(const mp_int *px, const mp_int *py, + const mp_int *pz, mp_int *rx, mp_int *ry, + const ECGroup *group); + +/* Checks if point P(px, py, pz) is at infinity. Uses Jacobian + * coordinates. */ +mp_err ec_GFp_pt_is_inf_jac(const mp_int *px, const mp_int *py, + const mp_int *pz); + +/* Sets P(px, py, pz) to be the point at infinity. Uses Jacobian + * coordinates. */ +mp_err ec_GFp_pt_set_inf_jac(mp_int *px, mp_int *py, mp_int *pz); + +/* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is + * (qx, qy, qz). Uses Jacobian coordinates. */ +mp_err ec_GFp_pt_add_jac_aff(const mp_int *px, const mp_int *py, + const mp_int *pz, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + mp_int *rz, const ECGroup *group); + +/* Computes R = 2P. Uses Jacobian coordinates. */ +mp_err ec_GFp_pt_dbl_jac(const mp_int *px, const mp_int *py, + const mp_int *pz, mp_int *rx, mp_int *ry, + mp_int *rz, const ECGroup *group); + +#ifdef ECL_ENABLE_GFP_PT_MUL_JAC +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GFp. Uses Jacobian coordinates. */ +mp_err ec_GFp_pt_mul_jac(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); +#endif + +/* Computes R(x, y) = k1 * G + k2 * P(x, y), where G is the generator + * (base point) of the group of points on the elliptic curve. Allows k1 = + * NULL or { k2, P } = NULL. Implemented using mixed Jacobian-affine + * coordinates. Input and output values are assumed to be NOT + * field-encoded and are in affine form. */ +mp_err + ec_GFp_pts_mul_jac(const mp_int *k1, const mp_int *k2, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); + +/* Computes R = nP where R is (rx, ry) and P is the base point. Elliptic + * curve points P and R can be identical. Uses mixed Modified-Jacobian + * co-ordinates for doubling and Chudnovsky Jacobian coordinates for + * additions. Assumes input is already field-encoded using field_enc, and + * returns output that is still field-encoded. Uses 5-bit window NAF + * method (algorithm 11) for scalar-point multiplication from Brown, + * Hankerson, Lopez, Menezes. Software Implementation of the NIST Elliptic + * Curves Over Prime Fields. */ +mp_err + ec_GFp_pt_mul_jm_wNAF(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group); + +#endif /* _ECP_H */ diff --git a/usr/src/common/crypto/ecc/ecp_192.c b/usr/src/common/crypto/ecc/ecp_192.c new file mode 100644 index 0000000000..41df2037b8 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp_192.c @@ -0,0 +1,526 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for prime field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecp.h" +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +#define ECP192_DIGITS ECL_CURVE_DIGITS(192) + +/* Fast modular reduction for p192 = 2^192 - 2^64 - 1. a can be r. Uses + * algorithm 7 from Brown, Hankerson, Lopez, Menezes. Software + * Implementation of the NIST Elliptic Curves over Prime Fields. */ +mp_err +ec_GFp_nistp192_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_size a_used = MP_USED(a); + mp_digit r3; +#ifndef MPI_AMD64_ADD + mp_digit carry; +#endif +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a5a = 0, a5b = 0, a4a = 0, a4b = 0, a3a = 0, a3b = 0; + mp_digit r0a, r0b, r1a, r1b, r2a, r2b; +#else + mp_digit a5 = 0, a4 = 0, a3 = 0; + mp_digit r0, r1, r2; +#endif + + /* reduction not needed if a is not larger than field size */ + if (a_used < ECP192_DIGITS) { + if (a == r) { + return MP_OKAY; + } + return mp_copy(a, r); + } + + /* for polynomials larger than twice the field size, use regular + * reduction */ + if (a_used > ECP192_DIGITS*2) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { + /* copy out upper words of a */ + +#ifdef ECL_THIRTY_TWO_BIT + + /* in all the math below, + * nXb is most signifiant, nXa is least significant */ + switch (a_used) { + case 12: + a5b = MP_DIGIT(a, 11); + case 11: + a5a = MP_DIGIT(a, 10); + case 10: + a4b = MP_DIGIT(a, 9); + case 9: + a4a = MP_DIGIT(a, 8); + case 8: + a3b = MP_DIGIT(a, 7); + case 7: + a3a = MP_DIGIT(a, 6); + } + + + r2b= MP_DIGIT(a, 5); + r2a= MP_DIGIT(a, 4); + r1b = MP_DIGIT(a, 3); + r1a = MP_DIGIT(a, 2); + r0b = MP_DIGIT(a, 1); + r0a = MP_DIGIT(a, 0); + + /* implement r = (a2,a1,a0)+(a5,a5,a5)+(a4,a4,0)+(0,a3,a3) */ + MP_ADD_CARRY(r0a, a3a, r0a, 0, carry); + MP_ADD_CARRY(r0b, a3b, r0b, carry, carry); + MP_ADD_CARRY(r1a, a3a, r1a, carry, carry); + MP_ADD_CARRY(r1b, a3b, r1b, carry, carry); + MP_ADD_CARRY(r2a, a4a, r2a, carry, carry); + MP_ADD_CARRY(r2b, a4b, r2b, carry, carry); + r3 = carry; carry = 0; + MP_ADD_CARRY(r0a, a5a, r0a, 0, carry); + MP_ADD_CARRY(r0b, a5b, r0b, carry, carry); + MP_ADD_CARRY(r1a, a5a, r1a, carry, carry); + MP_ADD_CARRY(r1b, a5b, r1b, carry, carry); + MP_ADD_CARRY(r2a, a5a, r2a, carry, carry); + MP_ADD_CARRY(r2b, a5b, r2b, carry, carry); + r3 += carry; + MP_ADD_CARRY(r1a, a4a, r1a, 0, carry); + MP_ADD_CARRY(r1b, a4b, r1b, carry, carry); + MP_ADD_CARRY(r2a, 0, r2a, carry, carry); + MP_ADD_CARRY(r2b, 0, r2b, carry, carry); + r3 += carry; + + /* reduce out the carry */ + while (r3) { + MP_ADD_CARRY(r0a, r3, r0a, 0, carry); + MP_ADD_CARRY(r0b, 0, r0b, carry, carry); + MP_ADD_CARRY(r1a, r3, r1a, carry, carry); + MP_ADD_CARRY(r1b, 0, r1b, carry, carry); + MP_ADD_CARRY(r2a, 0, r2a, carry, carry); + MP_ADD_CARRY(r2b, 0, r2b, carry, carry); + r3 = carry; + } + + /* check for final reduction */ + /* + * our field is 0xffffffffffffffff, 0xfffffffffffffffe, + * 0xffffffffffffffff. That means we can only be over and need + * one more reduction + * if r2 == 0xffffffffffffffffff (same as r2+1 == 0) + * and + * r1 == 0xffffffffffffffffff or + * r1 == 0xfffffffffffffffffe and r0 = 0xfffffffffffffffff + * In all cases, we subtract the field (or add the 2's + * complement value (1,1,0)). (r0, r1, r2) + */ + if (((r2b == 0xffffffff) && (r2a == 0xffffffff) + && (r1b == 0xffffffff) ) && + ((r1a == 0xffffffff) || + (r1a == 0xfffffffe) && (r0a == 0xffffffff) && + (r0b == 0xffffffff)) ) { + /* do a quick subtract */ + MP_ADD_CARRY(r0a, 1, r0a, 0, carry); + r0b += carry; + r1a = r1b = r2a = r2b = 0; + } + + /* set the lower words of r */ + if (a != r) { + MP_CHECKOK(s_mp_pad(r, 6)); + } + MP_DIGIT(r, 5) = r2b; + MP_DIGIT(r, 4) = r2a; + MP_DIGIT(r, 3) = r1b; + MP_DIGIT(r, 2) = r1a; + MP_DIGIT(r, 1) = r0b; + MP_DIGIT(r, 0) = r0a; + MP_USED(r) = 6; +#else + switch (a_used) { + case 6: + a5 = MP_DIGIT(a, 5); + case 5: + a4 = MP_DIGIT(a, 4); + case 4: + a3 = MP_DIGIT(a, 3); + } + + r2 = MP_DIGIT(a, 2); + r1 = MP_DIGIT(a, 1); + r0 = MP_DIGIT(a, 0); + + /* implement r = (a2,a1,a0)+(a5,a5,a5)+(a4,a4,0)+(0,a3,a3) */ +#ifndef MPI_AMD64_ADD + MP_ADD_CARRY(r0, a3, r0, 0, carry); + MP_ADD_CARRY(r1, a3, r1, carry, carry); + MP_ADD_CARRY(r2, a4, r2, carry, carry); + r3 = carry; + MP_ADD_CARRY(r0, a5, r0, 0, carry); + MP_ADD_CARRY(r1, a5, r1, carry, carry); + MP_ADD_CARRY(r2, a5, r2, carry, carry); + r3 += carry; + MP_ADD_CARRY(r1, a4, r1, 0, carry); + MP_ADD_CARRY(r2, 0, r2, carry, carry); + r3 += carry; + +#else + r2 = MP_DIGIT(a, 2); + r1 = MP_DIGIT(a, 1); + r0 = MP_DIGIT(a, 0); + + /* set the lower words of r */ + __asm__ ( + "xorq %3,%3 \n\t" + "addq %4,%0 \n\t" + "adcq %4,%1 \n\t" + "adcq %5,%2 \n\t" + "adcq $0,%3 \n\t" + "addq %6,%0 \n\t" + "adcq %6,%1 \n\t" + "adcq %6,%2 \n\t" + "adcq $0,%3 \n\t" + "addq %5,%1 \n\t" + "adcq $0,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3), "=r"(a3), + "=r"(a4), "=r"(a5) + : "0" (r0), "1" (r1), "2" (r2), "3" (r3), + "4" (a3), "5" (a4), "6"(a5) + : "%cc" ); +#endif + + /* reduce out the carry */ + while (r3) { +#ifndef MPI_AMD64_ADD + MP_ADD_CARRY(r0, r3, r0, 0, carry); + MP_ADD_CARRY(r1, r3, r1, carry, carry); + MP_ADD_CARRY(r2, 0, r2, carry, carry); + r3 = carry; +#else + a3=r3; + __asm__ ( + "xorq %3,%3 \n\t" + "addq %4,%0 \n\t" + "adcq %4,%1 \n\t" + "adcq $0,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3), "=r"(a3) + : "0" (r0), "1" (r1), "2" (r2), "3" (r3), "4"(a3) + : "%cc" ); +#endif + } + + /* check for final reduction */ + /* + * our field is 0xffffffffffffffff, 0xfffffffffffffffe, + * 0xffffffffffffffff. That means we can only be over and need + * one more reduction + * if r2 == 0xffffffffffffffffff (same as r2+1 == 0) + * and + * r1 == 0xffffffffffffffffff or + * r1 == 0xfffffffffffffffffe and r0 = 0xfffffffffffffffff + * In all cases, we subtract the field (or add the 2's + * complement value (1,1,0)). (r0, r1, r2) + */ + if (r3 || ((r2 == MP_DIGIT_MAX) && + ((r1 == MP_DIGIT_MAX) || + ((r1 == (MP_DIGIT_MAX-1)) && (r0 == MP_DIGIT_MAX))))) { + /* do a quick subtract */ + r0++; + r1 = r2 = 0; + } + /* set the lower words of r */ + if (a != r) { + MP_CHECKOK(s_mp_pad(r, 3)); + } + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_USED(r) = 3; +#endif + } + + CLEANUP: + return res; +} + +#ifndef ECL_THIRTY_TWO_BIT +/* Compute the sum of 192 bit curves. Do the work in-line since the + * number of words are so small, we don't want to overhead of mp function + * calls. Uses optimized modular reduction for p192. + */ +mp_err +ec_GFp_nistp192_add(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a0 = 0, a1 = 0, a2 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0; + mp_digit carry; + + switch(MP_USED(a)) { + case 3: + a2 = MP_DIGIT(a,2); + case 2: + a1 = MP_DIGIT(a,1); + case 1: + a0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 3: + r2 = MP_DIGIT(b,2); + case 2: + r1 = MP_DIGIT(b,1); + case 1: + r0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + MP_ADD_CARRY(a0, r0, r0, 0, carry); + MP_ADD_CARRY(a1, r1, r1, carry, carry); + MP_ADD_CARRY(a2, r2, r2, carry, carry); +#else + __asm__ ( + "xorq %3,%3 \n\t" + "addq %4,%0 \n\t" + "adcq %5,%1 \n\t" + "adcq %6,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(carry) + : "r" (a0), "r" (a1), "r" (a2), "0" (r0), + "1" (r1), "2" (r2) + : "%cc" ); +#endif + + /* Do quick 'subract' if we've gone over + * (add the 2's complement of the curve field) */ + if (carry || ((r2 == MP_DIGIT_MAX) && + ((r1 == MP_DIGIT_MAX) || + ((r1 == (MP_DIGIT_MAX-1)) && (r0 == MP_DIGIT_MAX))))) { +#ifndef MPI_AMD64_ADD + MP_ADD_CARRY(r0, 1, r0, 0, carry); + MP_ADD_CARRY(r1, 1, r1, carry, carry); + MP_ADD_CARRY(r2, 0, r2, carry, carry); +#else + __asm__ ( + "addq $1,%0 \n\t" + "adcq $1,%1 \n\t" + "adcq $0,%2 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2) + : "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + } + + + MP_CHECKOK(s_mp_pad(r, 3)); + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 3; + s_mp_clamp(r); + + + CLEANUP: + return res; +} + +/* Compute the diff of 192 bit curves. Do the work in-line since the + * number of words are so small, we don't want to overhead of mp function + * calls. Uses optimized modular reduction for p192. + */ +mp_err +ec_GFp_nistp192_sub(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit b0 = 0, b1 = 0, b2 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0; + mp_digit borrow; + + switch(MP_USED(a)) { + case 3: + r2 = MP_DIGIT(a,2); + case 2: + r1 = MP_DIGIT(a,1); + case 1: + r0 = MP_DIGIT(a,0); + } + + switch(MP_USED(b)) { + case 3: + b2 = MP_DIGIT(b,2); + case 2: + b1 = MP_DIGIT(b,1); + case 1: + b0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + MP_SUB_BORROW(r0, b0, r0, 0, borrow); + MP_SUB_BORROW(r1, b1, r1, borrow, borrow); + MP_SUB_BORROW(r2, b2, r2, borrow, borrow); +#else + __asm__ ( + "xorq %3,%3 \n\t" + "subq %4,%0 \n\t" + "sbbq %5,%1 \n\t" + "sbbq %6,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(borrow) + : "r" (b0), "r" (b1), "r" (b2), "0" (r0), + "1" (r1), "2" (r2) + : "%cc" ); +#endif + + /* Do quick 'add' if we've gone under 0 + * (subtract the 2's complement of the curve field) */ + if (borrow) { +#ifndef MPI_AMD64_ADD + MP_SUB_BORROW(r0, 1, r0, 0, borrow); + MP_SUB_BORROW(r1, 1, r1, borrow, borrow); + MP_SUB_BORROW(r2, 0, r2, borrow, borrow); +#else + __asm__ ( + "subq $1,%0 \n\t" + "sbbq $1,%1 \n\t" + "sbbq $0,%2 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2) + : "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + } + + MP_CHECKOK(s_mp_pad(r, 3)); + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 3; + s_mp_clamp(r); + + CLEANUP: + return res; +} + +#endif + +/* Compute the square of polynomial a, reduce modulo p192. Store the + * result in r. r could be a. Uses optimized modular reduction for p192. + */ +mp_err +ec_GFp_nistp192_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_sqr(a, r)); + MP_CHECKOK(ec_GFp_nistp192_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p192. + * Store the result in r. r could be a or b; a could be b. Uses + * optimized modular reduction for p192. */ +mp_err +ec_GFp_nistp192_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(ec_GFp_nistp192_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Divides two field elements. If a is NULL, then returns the inverse of + * b. */ +mp_err +ec_GFp_nistp192_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_int t; + + /* If a is NULL, then return the inverse of b, otherwise return a/b. */ + if (a == NULL) { + return mp_invmod(b, &meth->irr, r); + } else { + /* MPI doesn't support divmod, so we implement it using invmod and + * mulmod. */ + MP_CHECKOK(mp_init(&t, FLAG(b))); + MP_CHECKOK(mp_invmod(b, &meth->irr, &t)); + MP_CHECKOK(mp_mul(a, &t, r)); + MP_CHECKOK(ec_GFp_nistp192_mod(r, r, meth)); + CLEANUP: + mp_clear(&t); + return res; + } +} + +/* Wire in fast field arithmetic and precomputation of base point for + * named curves. */ +mp_err +ec_group_set_gfp192(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P192) { + group->meth->field_mod = &ec_GFp_nistp192_mod; + group->meth->field_mul = &ec_GFp_nistp192_mul; + group->meth->field_sqr = &ec_GFp_nistp192_sqr; + group->meth->field_div = &ec_GFp_nistp192_div; +#ifndef ECL_THIRTY_TWO_BIT + group->meth->field_add = &ec_GFp_nistp192_add; + group->meth->field_sub = &ec_GFp_nistp192_sub; +#endif + } + return MP_OKAY; +} diff --git a/usr/src/common/crypto/ecc/ecp_224.c b/usr/src/common/crypto/ecc/ecp_224.c new file mode 100644 index 0000000000..76ae22a2f5 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp_224.c @@ -0,0 +1,382 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for prime field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecp.h" +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +#define ECP224_DIGITS ECL_CURVE_DIGITS(224) + +/* Fast modular reduction for p224 = 2^224 - 2^96 + 1. a can be r. Uses + * algorithm 7 from Brown, Hankerson, Lopez, Menezes. Software + * Implementation of the NIST Elliptic Curves over Prime Fields. */ +mp_err +ec_GFp_nistp224_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_size a_used = MP_USED(a); + + int r3b; + mp_digit carry; +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a6a = 0, a6b = 0, + a5a = 0, a5b = 0, a4a = 0, a4b = 0, a3a = 0, a3b = 0; + mp_digit r0a, r0b, r1a, r1b, r2a, r2b, r3a; +#else + mp_digit a6 = 0, a5 = 0, a4 = 0, a3b = 0, a5a = 0; + mp_digit a6b = 0, a6a_a5b = 0, a5b = 0, a5a_a4b = 0, a4a_a3b = 0; + mp_digit r0, r1, r2, r3; +#endif + + /* reduction not needed if a is not larger than field size */ + if (a_used < ECP224_DIGITS) { + if (a == r) return MP_OKAY; + return mp_copy(a, r); + } + /* for polynomials larger than twice the field size, use regular + * reduction */ + if (a_used > ECL_CURVE_DIGITS(224*2)) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { +#ifdef ECL_THIRTY_TWO_BIT + /* copy out upper words of a */ + switch (a_used) { + case 14: + a6b = MP_DIGIT(a, 13); + case 13: + a6a = MP_DIGIT(a, 12); + case 12: + a5b = MP_DIGIT(a, 11); + case 11: + a5a = MP_DIGIT(a, 10); + case 10: + a4b = MP_DIGIT(a, 9); + case 9: + a4a = MP_DIGIT(a, 8); + case 8: + a3b = MP_DIGIT(a, 7); + } + r3a = MP_DIGIT(a, 6); + r2b= MP_DIGIT(a, 5); + r2a= MP_DIGIT(a, 4); + r1b = MP_DIGIT(a, 3); + r1a = MP_DIGIT(a, 2); + r0b = MP_DIGIT(a, 1); + r0a = MP_DIGIT(a, 0); + + + /* implement r = (a3a,a2,a1,a0) + +(a5a, a4,a3b, 0) + +( 0, a6,a5b, 0) + -( 0 0, 0|a6b, a6a|a5b ) + -( a6b, a6a|a5b, a5a|a4b, a4a|a3b ) */ + MP_ADD_CARRY (r1b, a3b, r1b, 0, carry); + MP_ADD_CARRY (r2a, a4a, r2a, carry, carry); + MP_ADD_CARRY (r2b, a4b, r2b, carry, carry); + MP_ADD_CARRY (r3a, a5a, r3a, carry, carry); + r3b = carry; + MP_ADD_CARRY (r1b, a5b, r1b, 0, carry); + MP_ADD_CARRY (r2a, a6a, r2a, carry, carry); + MP_ADD_CARRY (r2b, a6b, r2b, carry, carry); + MP_ADD_CARRY (r3a, 0, r3a, carry, carry); + r3b += carry; + MP_SUB_BORROW(r0a, a3b, r0a, 0, carry); + MP_SUB_BORROW(r0b, a4a, r0b, carry, carry); + MP_SUB_BORROW(r1a, a4b, r1a, carry, carry); + MP_SUB_BORROW(r1b, a5a, r1b, carry, carry); + MP_SUB_BORROW(r2a, a5b, r2a, carry, carry); + MP_SUB_BORROW(r2b, a6a, r2b, carry, carry); + MP_SUB_BORROW(r3a, a6b, r3a, carry, carry); + r3b -= carry; + MP_SUB_BORROW(r0a, a5b, r0a, 0, carry); + MP_SUB_BORROW(r0b, a6a, r0b, carry, carry); + MP_SUB_BORROW(r1a, a6b, r1a, carry, carry); + if (carry) { + MP_SUB_BORROW(r1b, 0, r1b, carry, carry); + MP_SUB_BORROW(r2a, 0, r2a, carry, carry); + MP_SUB_BORROW(r2b, 0, r2b, carry, carry); + MP_SUB_BORROW(r3a, 0, r3a, carry, carry); + r3b -= carry; + } + + while (r3b > 0) { + int tmp; + MP_ADD_CARRY(r1b, r3b, r1b, 0, carry); + if (carry) { + MP_ADD_CARRY(r2a, 0, r2a, carry, carry); + MP_ADD_CARRY(r2b, 0, r2b, carry, carry); + MP_ADD_CARRY(r3a, 0, r3a, carry, carry); + } + tmp = carry; + MP_SUB_BORROW(r0a, r3b, r0a, 0, carry); + if (carry) { + MP_SUB_BORROW(r0b, 0, r0b, carry, carry); + MP_SUB_BORROW(r1a, 0, r1a, carry, carry); + MP_SUB_BORROW(r1b, 0, r1b, carry, carry); + MP_SUB_BORROW(r2a, 0, r2a, carry, carry); + MP_SUB_BORROW(r2b, 0, r2b, carry, carry); + MP_SUB_BORROW(r3a, 0, r3a, carry, carry); + tmp -= carry; + } + r3b = tmp; + } + + while (r3b < 0) { + mp_digit maxInt = MP_DIGIT_MAX; + MP_ADD_CARRY (r0a, 1, r0a, 0, carry); + MP_ADD_CARRY (r0b, 0, r0b, carry, carry); + MP_ADD_CARRY (r1a, 0, r1a, carry, carry); + MP_ADD_CARRY (r1b, maxInt, r1b, carry, carry); + MP_ADD_CARRY (r2a, maxInt, r2a, carry, carry); + MP_ADD_CARRY (r2b, maxInt, r2b, carry, carry); + MP_ADD_CARRY (r3a, maxInt, r3a, carry, carry); + r3b += carry; + } + /* check for final reduction */ + /* now the only way we are over is if the top 4 words are all ones */ + if ((r3a == MP_DIGIT_MAX) && (r2b == MP_DIGIT_MAX) + && (r2a == MP_DIGIT_MAX) && (r1b == MP_DIGIT_MAX) && + ((r1a != 0) || (r0b != 0) || (r0a != 0)) ) { + /* one last subraction */ + MP_SUB_BORROW(r0a, 1, r0a, 0, carry); + MP_SUB_BORROW(r0b, 0, r0b, carry, carry); + MP_SUB_BORROW(r1a, 0, r1a, carry, carry); + r1b = r2a = r2b = r3a = 0; + } + + + if (a != r) { + MP_CHECKOK(s_mp_pad(r, 7)); + } + /* set the lower words of r */ + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 7; + MP_DIGIT(r, 6) = r3a; + MP_DIGIT(r, 5) = r2b; + MP_DIGIT(r, 4) = r2a; + MP_DIGIT(r, 3) = r1b; + MP_DIGIT(r, 2) = r1a; + MP_DIGIT(r, 1) = r0b; + MP_DIGIT(r, 0) = r0a; +#else + /* copy out upper words of a */ + switch (a_used) { + case 7: + a6 = MP_DIGIT(a, 6); + a6b = a6 >> 32; + a6a_a5b = a6 << 32; + case 6: + a5 = MP_DIGIT(a, 5); + a5b = a5 >> 32; + a6a_a5b |= a5b; + a5b = a5b << 32; + a5a_a4b = a5 << 32; + a5a = a5 & 0xffffffff; + case 5: + a4 = MP_DIGIT(a, 4); + a5a_a4b |= a4 >> 32; + a4a_a3b = a4 << 32; + case 4: + a3b = MP_DIGIT(a, 3) >> 32; + a4a_a3b |= a3b; + a3b = a3b << 32; + } + + r3 = MP_DIGIT(a, 3) & 0xffffffff; + r2 = MP_DIGIT(a, 2); + r1 = MP_DIGIT(a, 1); + r0 = MP_DIGIT(a, 0); + + /* implement r = (a3a,a2,a1,a0) + +(a5a, a4,a3b, 0) + +( 0, a6,a5b, 0) + -( 0 0, 0|a6b, a6a|a5b ) + -( a6b, a6a|a5b, a5a|a4b, a4a|a3b ) */ + MP_ADD_CARRY (r1, a3b, r1, 0, carry); + MP_ADD_CARRY (r2, a4 , r2, carry, carry); + MP_ADD_CARRY (r3, a5a, r3, carry, carry); + MP_ADD_CARRY (r1, a5b, r1, 0, carry); + MP_ADD_CARRY (r2, a6 , r2, carry, carry); + MP_ADD_CARRY (r3, 0, r3, carry, carry); + + MP_SUB_BORROW(r0, a4a_a3b, r0, 0, carry); + MP_SUB_BORROW(r1, a5a_a4b, r1, carry, carry); + MP_SUB_BORROW(r2, a6a_a5b, r2, carry, carry); + MP_SUB_BORROW(r3, a6b , r3, carry, carry); + MP_SUB_BORROW(r0, a6a_a5b, r0, 0, carry); + MP_SUB_BORROW(r1, a6b , r1, carry, carry); + if (carry) { + MP_SUB_BORROW(r2, 0, r2, carry, carry); + MP_SUB_BORROW(r3, 0, r3, carry, carry); + } + + + /* if the value is negative, r3 has a 2's complement + * high value */ + r3b = (int)(r3 >>32); + while (r3b > 0) { + r3 &= 0xffffffff; + MP_ADD_CARRY(r1,((mp_digit)r3b) << 32, r1, 0, carry); + if (carry) { + MP_ADD_CARRY(r2, 0, r2, carry, carry); + MP_ADD_CARRY(r3, 0, r3, carry, carry); + } + MP_SUB_BORROW(r0, r3b, r0, 0, carry); + if (carry) { + MP_SUB_BORROW(r1, 0, r1, carry, carry); + MP_SUB_BORROW(r2, 0, r2, carry, carry); + MP_SUB_BORROW(r3, 0, r3, carry, carry); + } + r3b = (int)(r3 >>32); + } + + while (r3b < 0) { + MP_ADD_CARRY (r0, 1, r0, 0, carry); + MP_ADD_CARRY (r1, MP_DIGIT_MAX <<32, r1, carry, carry); + MP_ADD_CARRY (r2, MP_DIGIT_MAX, r2, carry, carry); + MP_ADD_CARRY (r3, MP_DIGIT_MAX >> 32, r3, carry, carry); + r3b = (int)(r3 >>32); + } + /* check for final reduction */ + /* now the only way we are over is if the top 4 words are all ones */ + if ((r3 == (MP_DIGIT_MAX >> 32)) && (r2 == MP_DIGIT_MAX) + && ((r1 & MP_DIGIT_MAX << 32)== MP_DIGIT_MAX << 32) && + ((r1 != MP_DIGIT_MAX << 32 ) || (r0 != 0)) ) { + /* one last subraction */ + MP_SUB_BORROW(r0, 1, r0, 0, carry); + MP_SUB_BORROW(r1, 0, r1, carry, carry); + r2 = r3 = 0; + } + + + if (a != r) { + MP_CHECKOK(s_mp_pad(r, 4)); + } + /* set the lower words of r */ + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; +#endif + } + + CLEANUP: + return res; +} + +/* Compute the square of polynomial a, reduce modulo p224. Store the + * result in r. r could be a. Uses optimized modular reduction for p224. + */ +mp_err +ec_GFp_nistp224_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_sqr(a, r)); + MP_CHECKOK(ec_GFp_nistp224_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p224. + * Store the result in r. r could be a or b; a could be b. Uses + * optimized modular reduction for p224. */ +mp_err +ec_GFp_nistp224_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(ec_GFp_nistp224_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Divides two field elements. If a is NULL, then returns the inverse of + * b. */ +mp_err +ec_GFp_nistp224_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_int t; + + /* If a is NULL, then return the inverse of b, otherwise return a/b. */ + if (a == NULL) { + return mp_invmod(b, &meth->irr, r); + } else { + /* MPI doesn't support divmod, so we implement it using invmod and + * mulmod. */ + MP_CHECKOK(mp_init(&t, FLAG(b))); + MP_CHECKOK(mp_invmod(b, &meth->irr, &t)); + MP_CHECKOK(mp_mul(a, &t, r)); + MP_CHECKOK(ec_GFp_nistp224_mod(r, r, meth)); + CLEANUP: + mp_clear(&t); + return res; + } +} + +/* Wire in fast field arithmetic and precomputation of base point for + * named curves. */ +mp_err +ec_group_set_gfp224(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P224) { + group->meth->field_mod = &ec_GFp_nistp224_mod; + group->meth->field_mul = &ec_GFp_nistp224_mul; + group->meth->field_sqr = &ec_GFp_nistp224_sqr; + group->meth->field_div = &ec_GFp_nistp224_div; + } + return MP_OKAY; +} diff --git a/usr/src/common/crypto/ecc/ecp_256.c b/usr/src/common/crypto/ecc/ecp_256.c new file mode 100644 index 0000000000..9a661f07a2 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp_256.c @@ -0,0 +1,439 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for prime field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecp.h" +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* Fast modular reduction for p256 = 2^256 - 2^224 + 2^192+ 2^96 - 1. a can be r. + * Uses algorithm 2.29 from Hankerson, Menezes, Vanstone. Guide to + * Elliptic Curve Cryptography. */ +mp_err +ec_GFp_nistp256_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_size a_used = MP_USED(a); + int a_bits = mpl_significant_bits(a); + mp_digit carry; + +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a8=0, a9=0, a10=0, a11=0, a12=0, a13=0, a14=0, a15=0; + mp_digit r0, r1, r2, r3, r4, r5, r6, r7; + int r8; /* must be a signed value ! */ +#else + mp_digit a4=0, a5=0, a6=0, a7=0; + mp_digit a4h, a4l, a5h, a5l, a6h, a6l, a7h, a7l; + mp_digit r0, r1, r2, r3; + int r4; /* must be a signed value ! */ +#endif + /* for polynomials larger than twice the field size + * use regular reduction */ + if (a_bits < 256) { + if (a == r) return MP_OKAY; + return mp_copy(a,r); + } + if (a_bits > 512) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { + +#ifdef ECL_THIRTY_TWO_BIT + switch (a_used) { + case 16: + a15 = MP_DIGIT(a,15); + case 15: + a14 = MP_DIGIT(a,14); + case 14: + a13 = MP_DIGIT(a,13); + case 13: + a12 = MP_DIGIT(a,12); + case 12: + a11 = MP_DIGIT(a,11); + case 11: + a10 = MP_DIGIT(a,10); + case 10: + a9 = MP_DIGIT(a,9); + case 9: + a8 = MP_DIGIT(a,8); + } + + r0 = MP_DIGIT(a,0); + r1 = MP_DIGIT(a,1); + r2 = MP_DIGIT(a,2); + r3 = MP_DIGIT(a,3); + r4 = MP_DIGIT(a,4); + r5 = MP_DIGIT(a,5); + r6 = MP_DIGIT(a,6); + r7 = MP_DIGIT(a,7); + + /* sum 1 */ + MP_ADD_CARRY(r3, a11, r3, 0, carry); + MP_ADD_CARRY(r4, a12, r4, carry, carry); + MP_ADD_CARRY(r5, a13, r5, carry, carry); + MP_ADD_CARRY(r6, a14, r6, carry, carry); + MP_ADD_CARRY(r7, a15, r7, carry, carry); + r8 = carry; + MP_ADD_CARRY(r3, a11, r3, 0, carry); + MP_ADD_CARRY(r4, a12, r4, carry, carry); + MP_ADD_CARRY(r5, a13, r5, carry, carry); + MP_ADD_CARRY(r6, a14, r6, carry, carry); + MP_ADD_CARRY(r7, a15, r7, carry, carry); + r8 += carry; + /* sum 2 */ + MP_ADD_CARRY(r3, a12, r3, 0, carry); + MP_ADD_CARRY(r4, a13, r4, carry, carry); + MP_ADD_CARRY(r5, a14, r5, carry, carry); + MP_ADD_CARRY(r6, a15, r6, carry, carry); + MP_ADD_CARRY(r7, 0, r7, carry, carry); + r8 += carry; + /* combine last bottom of sum 3 with second sum 2 */ + MP_ADD_CARRY(r0, a8, r0, 0, carry); + MP_ADD_CARRY(r1, a9, r1, carry, carry); + MP_ADD_CARRY(r2, a10, r2, carry, carry); + MP_ADD_CARRY(r3, a12, r3, carry, carry); + MP_ADD_CARRY(r4, a13, r4, carry, carry); + MP_ADD_CARRY(r5, a14, r5, carry, carry); + MP_ADD_CARRY(r6, a15, r6, carry, carry); + MP_ADD_CARRY(r7, a15, r7, carry, carry); /* from sum 3 */ + r8 += carry; + /* sum 3 (rest of it)*/ + MP_ADD_CARRY(r6, a14, r6, 0, carry); + MP_ADD_CARRY(r7, 0, r7, carry, carry); + r8 += carry; + /* sum 4 (rest of it)*/ + MP_ADD_CARRY(r0, a9, r0, 0, carry); + MP_ADD_CARRY(r1, a10, r1, carry, carry); + MP_ADD_CARRY(r2, a11, r2, carry, carry); + MP_ADD_CARRY(r3, a13, r3, carry, carry); + MP_ADD_CARRY(r4, a14, r4, carry, carry); + MP_ADD_CARRY(r5, a15, r5, carry, carry); + MP_ADD_CARRY(r6, a13, r6, carry, carry); + MP_ADD_CARRY(r7, a8, r7, carry, carry); + r8 += carry; + /* diff 5 */ + MP_SUB_BORROW(r0, a11, r0, 0, carry); + MP_SUB_BORROW(r1, a12, r1, carry, carry); + MP_SUB_BORROW(r2, a13, r2, carry, carry); + MP_SUB_BORROW(r3, 0, r3, carry, carry); + MP_SUB_BORROW(r4, 0, r4, carry, carry); + MP_SUB_BORROW(r5, 0, r5, carry, carry); + MP_SUB_BORROW(r6, a8, r6, carry, carry); + MP_SUB_BORROW(r7, a10, r7, carry, carry); + r8 -= carry; + /* diff 6 */ + MP_SUB_BORROW(r0, a12, r0, 0, carry); + MP_SUB_BORROW(r1, a13, r1, carry, carry); + MP_SUB_BORROW(r2, a14, r2, carry, carry); + MP_SUB_BORROW(r3, a15, r3, carry, carry); + MP_SUB_BORROW(r4, 0, r4, carry, carry); + MP_SUB_BORROW(r5, 0, r5, carry, carry); + MP_SUB_BORROW(r6, a9, r6, carry, carry); + MP_SUB_BORROW(r7, a11, r7, carry, carry); + r8 -= carry; + /* diff 7 */ + MP_SUB_BORROW(r0, a13, r0, 0, carry); + MP_SUB_BORROW(r1, a14, r1, carry, carry); + MP_SUB_BORROW(r2, a15, r2, carry, carry); + MP_SUB_BORROW(r3, a8, r3, carry, carry); + MP_SUB_BORROW(r4, a9, r4, carry, carry); + MP_SUB_BORROW(r5, a10, r5, carry, carry); + MP_SUB_BORROW(r6, 0, r6, carry, carry); + MP_SUB_BORROW(r7, a12, r7, carry, carry); + r8 -= carry; + /* diff 8 */ + MP_SUB_BORROW(r0, a14, r0, 0, carry); + MP_SUB_BORROW(r1, a15, r1, carry, carry); + MP_SUB_BORROW(r2, 0, r2, carry, carry); + MP_SUB_BORROW(r3, a9, r3, carry, carry); + MP_SUB_BORROW(r4, a10, r4, carry, carry); + MP_SUB_BORROW(r5, a11, r5, carry, carry); + MP_SUB_BORROW(r6, 0, r6, carry, carry); + MP_SUB_BORROW(r7, a13, r7, carry, carry); + r8 -= carry; + + /* reduce the overflows */ + while (r8 > 0) { + mp_digit r8_d = r8; + MP_ADD_CARRY(r0, r8_d, r0, 0, carry); + MP_ADD_CARRY(r1, 0, r1, carry, carry); + MP_ADD_CARRY(r2, 0, r2, carry, carry); + MP_ADD_CARRY(r3, -r8_d, r3, carry, carry); + MP_ADD_CARRY(r4, MP_DIGIT_MAX, r4, carry, carry); + MP_ADD_CARRY(r5, MP_DIGIT_MAX, r5, carry, carry); + MP_ADD_CARRY(r6, -(r8_d+1), r6, carry, carry); + MP_ADD_CARRY(r7, (r8_d-1), r7, carry, carry); + r8 = carry; + } + + /* reduce the underflows */ + while (r8 < 0) { + mp_digit r8_d = -r8; + MP_SUB_BORROW(r0, r8_d, r0, 0, carry); + MP_SUB_BORROW(r1, 0, r1, carry, carry); + MP_SUB_BORROW(r2, 0, r2, carry, carry); + MP_SUB_BORROW(r3, -r8_d, r3, carry, carry); + MP_SUB_BORROW(r4, MP_DIGIT_MAX, r4, carry, carry); + MP_SUB_BORROW(r5, MP_DIGIT_MAX, r5, carry, carry); + MP_SUB_BORROW(r6, -(r8_d+1), r6, carry, carry); + MP_SUB_BORROW(r7, (r8_d-1), r7, carry, carry); + r8 = -carry; + } + if (a != r) { + MP_CHECKOK(s_mp_pad(r,8)); + } + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 8; + + MP_DIGIT(r,7) = r7; + MP_DIGIT(r,6) = r6; + MP_DIGIT(r,5) = r5; + MP_DIGIT(r,4) = r4; + MP_DIGIT(r,3) = r3; + MP_DIGIT(r,2) = r2; + MP_DIGIT(r,1) = r1; + MP_DIGIT(r,0) = r0; + + /* final reduction if necessary */ + if ((r7 == MP_DIGIT_MAX) && + ((r6 > 1) || ((r6 == 1) && + (r5 || r4 || r3 || + ((r2 == MP_DIGIT_MAX) && (r1 == MP_DIGIT_MAX) + && (r0 == MP_DIGIT_MAX)))))) { + MP_CHECKOK(mp_sub(r, &meth->irr, r)); + } +#ifdef notdef + + + /* smooth the negatives */ + while (MP_SIGN(r) != MP_ZPOS) { + MP_CHECKOK(mp_add(r, &meth->irr, r)); + } + while (MP_USED(r) > 8) { + MP_CHECKOK(mp_sub(r, &meth->irr, r)); + } + + /* final reduction if necessary */ + if (MP_DIGIT(r,7) >= MP_DIGIT(&meth->irr,7)) { + if (mp_cmp(r,&meth->irr) != MP_LT) { + MP_CHECKOK(mp_sub(r, &meth->irr, r)); + } + } +#endif + s_mp_clamp(r); +#else + switch (a_used) { + case 8: + a7 = MP_DIGIT(a,7); + case 7: + a6 = MP_DIGIT(a,6); + case 6: + a5 = MP_DIGIT(a,5); + case 5: + a4 = MP_DIGIT(a,4); + } + a7l = a7 << 32; + a7h = a7 >> 32; + a6l = a6 << 32; + a6h = a6 >> 32; + a5l = a5 << 32; + a5h = a5 >> 32; + a4l = a4 << 32; + a4h = a4 >> 32; + r3 = MP_DIGIT(a,3); + r2 = MP_DIGIT(a,2); + r1 = MP_DIGIT(a,1); + r0 = MP_DIGIT(a,0); + + /* sum 1 */ + MP_ADD_CARRY(r1, a5h << 32, r1, 0, carry); + MP_ADD_CARRY(r2, a6, r2, carry, carry); + MP_ADD_CARRY(r3, a7, r3, carry, carry); + r4 = carry; + MP_ADD_CARRY(r1, a5h << 32, r1, 0, carry); + MP_ADD_CARRY(r2, a6, r2, carry, carry); + MP_ADD_CARRY(r3, a7, r3, carry, carry); + r4 += carry; + /* sum 2 */ + MP_ADD_CARRY(r1, a6l, r1, 0, carry); + MP_ADD_CARRY(r2, a6h | a7l, r2, carry, carry); + MP_ADD_CARRY(r3, a7h, r3, carry, carry); + r4 += carry; + MP_ADD_CARRY(r1, a6l, r1, 0, carry); + MP_ADD_CARRY(r2, a6h | a7l, r2, carry, carry); + MP_ADD_CARRY(r3, a7h, r3, carry, carry); + r4 += carry; + + /* sum 3 */ + MP_ADD_CARRY(r0, a4, r0, 0, carry); + MP_ADD_CARRY(r1, a5l >> 32, r1, carry, carry); + MP_ADD_CARRY(r2, 0, r2, carry, carry); + MP_ADD_CARRY(r3, a7, r3, carry, carry); + r4 += carry; + /* sum 4 */ + MP_ADD_CARRY(r0, a4h | a5l, r0, 0, carry); + MP_ADD_CARRY(r1, a5h|(a6h<<32), r1, carry, carry); + MP_ADD_CARRY(r2, a7, r2, carry, carry); + MP_ADD_CARRY(r3, a6h | a4l, r3, carry, carry); + r4 += carry; + /* diff 5 */ + MP_SUB_BORROW(r0, a5h | a6l, r0, 0, carry); + MP_SUB_BORROW(r1, a6h, r1, carry, carry); + MP_SUB_BORROW(r2, 0, r2, carry, carry); + MP_SUB_BORROW(r3, (a4l>>32)|a5l,r3, carry, carry); + r4 -= carry; + /* diff 6 */ + MP_SUB_BORROW(r0, a6, r0, 0, carry); + MP_SUB_BORROW(r1, a7, r1, carry, carry); + MP_SUB_BORROW(r2, 0, r2, carry, carry); + MP_SUB_BORROW(r3, a4h|(a5h<<32),r3, carry, carry); + r4 -= carry; + /* diff 7 */ + MP_SUB_BORROW(r0, a6h|a7l, r0, 0, carry); + MP_SUB_BORROW(r1, a7h|a4l, r1, carry, carry); + MP_SUB_BORROW(r2, a4h|a5l, r2, carry, carry); + MP_SUB_BORROW(r3, a6l, r3, carry, carry); + r4 -= carry; + /* diff 8 */ + MP_SUB_BORROW(r0, a7, r0, 0, carry); + MP_SUB_BORROW(r1, a4h<<32, r1, carry, carry); + MP_SUB_BORROW(r2, a5, r2, carry, carry); + MP_SUB_BORROW(r3, a6h<<32, r3, carry, carry); + r4 -= carry; + + /* reduce the overflows */ + while (r4 > 0) { + mp_digit r4_long = r4; + mp_digit r4l = (r4_long << 32); + MP_ADD_CARRY(r0, r4_long, r0, 0, carry); + MP_ADD_CARRY(r1, -r4l, r1, carry, carry); + MP_ADD_CARRY(r2, MP_DIGIT_MAX, r2, carry, carry); + MP_ADD_CARRY(r3, r4l-r4_long-1,r3, carry, carry); + r4 = carry; + } + + /* reduce the underflows */ + while (r4 < 0) { + mp_digit r4_long = -r4; + mp_digit r4l = (r4_long << 32); + MP_SUB_BORROW(r0, r4_long, r0, 0, carry); + MP_SUB_BORROW(r1, -r4l, r1, carry, carry); + MP_SUB_BORROW(r2, MP_DIGIT_MAX, r2, carry, carry); + MP_SUB_BORROW(r3, r4l-r4_long-1,r3, carry, carry); + r4 = -carry; + } + + if (a != r) { + MP_CHECKOK(s_mp_pad(r,4)); + } + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 4; + + MP_DIGIT(r,3) = r3; + MP_DIGIT(r,2) = r2; + MP_DIGIT(r,1) = r1; + MP_DIGIT(r,0) = r0; + + /* final reduction if necessary */ + if ((r3 > 0xFFFFFFFF00000001ULL) || + ((r3 == 0xFFFFFFFF00000001ULL) && + (r2 || (r1 >> 32)|| + (r1 == 0xFFFFFFFFULL && r0 == MP_DIGIT_MAX)))) { + /* very rare, just use mp_sub */ + MP_CHECKOK(mp_sub(r, &meth->irr, r)); + } + + s_mp_clamp(r); +#endif + } + + CLEANUP: + return res; +} + +/* Compute the square of polynomial a, reduce modulo p256. Store the + * result in r. r could be a. Uses optimized modular reduction for p256. + */ +mp_err +ec_GFp_nistp256_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_sqr(a, r)); + MP_CHECKOK(ec_GFp_nistp256_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p256. + * Store the result in r. r could be a or b; a could be b. Uses + * optimized modular reduction for p256. */ +mp_err +ec_GFp_nistp256_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(ec_GFp_nistp256_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Wire in fast field arithmetic and precomputation of base point for + * named curves. */ +mp_err +ec_group_set_gfp256(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P256) { + group->meth->field_mod = &ec_GFp_nistp256_mod; + group->meth->field_mul = &ec_GFp_nistp256_mul; + group->meth->field_sqr = &ec_GFp_nistp256_sqr; + } + return MP_OKAY; +} diff --git a/usr/src/common/crypto/ecc/ecp_384.c b/usr/src/common/crypto/ecc/ecp_384.c new file mode 100644 index 0000000000..8da20dfb1d --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp_384.c @@ -0,0 +1,303 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for prime field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecp.h" +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* Fast modular reduction for p384 = 2^384 - 2^128 - 2^96 + 2^32 - 1. a can be r. + * Uses algorithm 2.30 from Hankerson, Menezes, Vanstone. Guide to + * Elliptic Curve Cryptography. */ +mp_err +ec_GFp_nistp384_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + int a_bits = mpl_significant_bits(a); + int i; + + /* m1, m2 are statically-allocated mp_int of exactly the size we need */ + mp_int m[10]; + +#ifdef ECL_THIRTY_TWO_BIT + mp_digit s[10][12]; + for (i = 0; i < 10; i++) { + MP_SIGN(&m[i]) = MP_ZPOS; + MP_ALLOC(&m[i]) = 12; + MP_USED(&m[i]) = 12; + MP_DIGITS(&m[i]) = s[i]; + } +#else + mp_digit s[10][6]; + for (i = 0; i < 10; i++) { + MP_SIGN(&m[i]) = MP_ZPOS; + MP_ALLOC(&m[i]) = 6; + MP_USED(&m[i]) = 6; + MP_DIGITS(&m[i]) = s[i]; + } +#endif + +#ifdef ECL_THIRTY_TWO_BIT + /* for polynomials larger than twice the field size or polynomials + * not using all words, use regular reduction */ + if ((a_bits > 768) || (a_bits <= 736)) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { + for (i = 0; i < 12; i++) { + s[0][i] = MP_DIGIT(a, i); + } + s[1][0] = 0; + s[1][1] = 0; + s[1][2] = 0; + s[1][3] = 0; + s[1][4] = MP_DIGIT(a, 21); + s[1][5] = MP_DIGIT(a, 22); + s[1][6] = MP_DIGIT(a, 23); + s[1][7] = 0; + s[1][8] = 0; + s[1][9] = 0; + s[1][10] = 0; + s[1][11] = 0; + for (i = 0; i < 12; i++) { + s[2][i] = MP_DIGIT(a, i+12); + } + s[3][0] = MP_DIGIT(a, 21); + s[3][1] = MP_DIGIT(a, 22); + s[3][2] = MP_DIGIT(a, 23); + for (i = 3; i < 12; i++) { + s[3][i] = MP_DIGIT(a, i+9); + } + s[4][0] = 0; + s[4][1] = MP_DIGIT(a, 23); + s[4][2] = 0; + s[4][3] = MP_DIGIT(a, 20); + for (i = 4; i < 12; i++) { + s[4][i] = MP_DIGIT(a, i+8); + } + s[5][0] = 0; + s[5][1] = 0; + s[5][2] = 0; + s[5][3] = 0; + s[5][4] = MP_DIGIT(a, 20); + s[5][5] = MP_DIGIT(a, 21); + s[5][6] = MP_DIGIT(a, 22); + s[5][7] = MP_DIGIT(a, 23); + s[5][8] = 0; + s[5][9] = 0; + s[5][10] = 0; + s[5][11] = 0; + s[6][0] = MP_DIGIT(a, 20); + s[6][1] = 0; + s[6][2] = 0; + s[6][3] = MP_DIGIT(a, 21); + s[6][4] = MP_DIGIT(a, 22); + s[6][5] = MP_DIGIT(a, 23); + s[6][6] = 0; + s[6][7] = 0; + s[6][8] = 0; + s[6][9] = 0; + s[6][10] = 0; + s[6][11] = 0; + s[7][0] = MP_DIGIT(a, 23); + for (i = 1; i < 12; i++) { + s[7][i] = MP_DIGIT(a, i+11); + } + s[8][0] = 0; + s[8][1] = MP_DIGIT(a, 20); + s[8][2] = MP_DIGIT(a, 21); + s[8][3] = MP_DIGIT(a, 22); + s[8][4] = MP_DIGIT(a, 23); + s[8][5] = 0; + s[8][6] = 0; + s[8][7] = 0; + s[8][8] = 0; + s[8][9] = 0; + s[8][10] = 0; + s[8][11] = 0; + s[9][0] = 0; + s[9][1] = 0; + s[9][2] = 0; + s[9][3] = MP_DIGIT(a, 23); + s[9][4] = MP_DIGIT(a, 23); + s[9][5] = 0; + s[9][6] = 0; + s[9][7] = 0; + s[9][8] = 0; + s[9][9] = 0; + s[9][10] = 0; + s[9][11] = 0; + + MP_CHECKOK(mp_add(&m[0], &m[1], r)); + MP_CHECKOK(mp_add(r, &m[1], r)); + MP_CHECKOK(mp_add(r, &m[2], r)); + MP_CHECKOK(mp_add(r, &m[3], r)); + MP_CHECKOK(mp_add(r, &m[4], r)); + MP_CHECKOK(mp_add(r, &m[5], r)); + MP_CHECKOK(mp_add(r, &m[6], r)); + MP_CHECKOK(mp_sub(r, &m[7], r)); + MP_CHECKOK(mp_sub(r, &m[8], r)); + MP_CHECKOK(mp_submod(r, &m[9], &meth->irr, r)); + s_mp_clamp(r); + } +#else + /* for polynomials larger than twice the field size or polynomials + * not using all words, use regular reduction */ + if ((a_bits > 768) || (a_bits <= 736)) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { + for (i = 0; i < 6; i++) { + s[0][i] = MP_DIGIT(a, i); + } + s[1][0] = 0; + s[1][1] = 0; + s[1][2] = (MP_DIGIT(a, 10) >> 32) | (MP_DIGIT(a, 11) << 32); + s[1][3] = MP_DIGIT(a, 11) >> 32; + s[1][4] = 0; + s[1][5] = 0; + for (i = 0; i < 6; i++) { + s[2][i] = MP_DIGIT(a, i+6); + } + s[3][0] = (MP_DIGIT(a, 10) >> 32) | (MP_DIGIT(a, 11) << 32); + s[3][1] = (MP_DIGIT(a, 11) >> 32) | (MP_DIGIT(a, 6) << 32); + for (i = 2; i < 6; i++) { + s[3][i] = (MP_DIGIT(a, i+4) >> 32) | (MP_DIGIT(a, i+5) << 32); + } + s[4][0] = (MP_DIGIT(a, 11) >> 32) << 32; + s[4][1] = MP_DIGIT(a, 10) << 32; + for (i = 2; i < 6; i++) { + s[4][i] = MP_DIGIT(a, i+4); + } + s[5][0] = 0; + s[5][1] = 0; + s[5][2] = MP_DIGIT(a, 10); + s[5][3] = MP_DIGIT(a, 11); + s[5][4] = 0; + s[5][5] = 0; + s[6][0] = (MP_DIGIT(a, 10) << 32) >> 32; + s[6][1] = (MP_DIGIT(a, 10) >> 32) << 32; + s[6][2] = MP_DIGIT(a, 11); + s[6][3] = 0; + s[6][4] = 0; + s[6][5] = 0; + s[7][0] = (MP_DIGIT(a, 11) >> 32) | (MP_DIGIT(a, 6) << 32); + for (i = 1; i < 6; i++) { + s[7][i] = (MP_DIGIT(a, i+5) >> 32) | (MP_DIGIT(a, i+6) << 32); + } + s[8][0] = MP_DIGIT(a, 10) << 32; + s[8][1] = (MP_DIGIT(a, 10) >> 32) | (MP_DIGIT(a, 11) << 32); + s[8][2] = MP_DIGIT(a, 11) >> 32; + s[8][3] = 0; + s[8][4] = 0; + s[8][5] = 0; + s[9][0] = 0; + s[9][1] = (MP_DIGIT(a, 11) >> 32) << 32; + s[9][2] = MP_DIGIT(a, 11) >> 32; + s[9][3] = 0; + s[9][4] = 0; + s[9][5] = 0; + + MP_CHECKOK(mp_add(&m[0], &m[1], r)); + MP_CHECKOK(mp_add(r, &m[1], r)); + MP_CHECKOK(mp_add(r, &m[2], r)); + MP_CHECKOK(mp_add(r, &m[3], r)); + MP_CHECKOK(mp_add(r, &m[4], r)); + MP_CHECKOK(mp_add(r, &m[5], r)); + MP_CHECKOK(mp_add(r, &m[6], r)); + MP_CHECKOK(mp_sub(r, &m[7], r)); + MP_CHECKOK(mp_sub(r, &m[8], r)); + MP_CHECKOK(mp_submod(r, &m[9], &meth->irr, r)); + s_mp_clamp(r); + } +#endif + + CLEANUP: + return res; +} + +/* Compute the square of polynomial a, reduce modulo p384. Store the + * result in r. r could be a. Uses optimized modular reduction for p384. + */ +mp_err +ec_GFp_nistp384_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_sqr(a, r)); + MP_CHECKOK(ec_GFp_nistp384_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p384. + * Store the result in r. r could be a or b; a could be b. Uses + * optimized modular reduction for p384. */ +mp_err +ec_GFp_nistp384_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(ec_GFp_nistp384_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Wire in fast field arithmetic and precomputation of base point for + * named curves. */ +mp_err +ec_group_set_gfp384(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P384) { + group->meth->field_mod = &ec_GFp_nistp384_mod; + group->meth->field_mul = &ec_GFp_nistp384_mul; + group->meth->field_sqr = &ec_GFp_nistp384_sqr; + } + return MP_OKAY; +} diff --git a/usr/src/common/crypto/ecc/ecp_521.c b/usr/src/common/crypto/ecc/ecp_521.c new file mode 100644 index 0000000000..2f0e1b34d1 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp_521.c @@ -0,0 +1,180 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for prime field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecp.h" +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +#define ECP521_DIGITS ECL_CURVE_DIGITS(521) + +/* Fast modular reduction for p521 = 2^521 - 1. a can be r. Uses + * algorithm 2.31 from Hankerson, Menezes, Vanstone. Guide to + * Elliptic Curve Cryptography. */ +mp_err +ec_GFp_nistp521_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + int a_bits = mpl_significant_bits(a); + int i; + + /* m1, m2 are statically-allocated mp_int of exactly the size we need */ + mp_int m1; + + mp_digit s1[ECP521_DIGITS] = { 0 }; + + MP_SIGN(&m1) = MP_ZPOS; + MP_ALLOC(&m1) = ECP521_DIGITS; + MP_USED(&m1) = ECP521_DIGITS; + MP_DIGITS(&m1) = s1; + + if (a_bits < 521) { + if (a==r) return MP_OKAY; + return mp_copy(a, r); + } + /* for polynomials larger than twice the field size or polynomials + * not using all words, use regular reduction */ + if (a_bits > (521*2)) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { +#define FIRST_DIGIT (ECP521_DIGITS-1) + for (i = FIRST_DIGIT; i < MP_USED(a)-1; i++) { + s1[i-FIRST_DIGIT] = (MP_DIGIT(a, i) >> 9) + | (MP_DIGIT(a, 1+i) << (MP_DIGIT_BIT-9)); + } + s1[i-FIRST_DIGIT] = MP_DIGIT(a, i) >> 9; + + if ( a != r ) { + MP_CHECKOK(s_mp_pad(r,ECP521_DIGITS)); + for (i = 0; i < ECP521_DIGITS; i++) { + MP_DIGIT(r,i) = MP_DIGIT(a, i); + } + } + MP_USED(r) = ECP521_DIGITS; + MP_DIGIT(r,FIRST_DIGIT) &= 0x1FF; + + MP_CHECKOK(s_mp_add(r, &m1)); + if (MP_DIGIT(r, FIRST_DIGIT) & 0x200) { + MP_CHECKOK(s_mp_add_d(r,1)); + MP_DIGIT(r,FIRST_DIGIT) &= 0x1FF; + } + s_mp_clamp(r); + } + + CLEANUP: + return res; +} + +/* Compute the square of polynomial a, reduce modulo p521. Store the + * result in r. r could be a. Uses optimized modular reduction for p521. + */ +mp_err +ec_GFp_nistp521_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_sqr(a, r)); + MP_CHECKOK(ec_GFp_nistp521_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p521. + * Store the result in r. r could be a or b; a could be b. Uses + * optimized modular reduction for p521. */ +mp_err +ec_GFp_nistp521_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(ec_GFp_nistp521_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Divides two field elements. If a is NULL, then returns the inverse of + * b. */ +mp_err +ec_GFp_nistp521_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_int t; + + /* If a is NULL, then return the inverse of b, otherwise return a/b. */ + if (a == NULL) { + return mp_invmod(b, &meth->irr, r); + } else { + /* MPI doesn't support divmod, so we implement it using invmod and + * mulmod. */ + MP_CHECKOK(mp_init(&t, FLAG(b))); + MP_CHECKOK(mp_invmod(b, &meth->irr, &t)); + MP_CHECKOK(mp_mul(a, &t, r)); + MP_CHECKOK(ec_GFp_nistp521_mod(r, r, meth)); + CLEANUP: + mp_clear(&t); + return res; + } +} + +/* Wire in fast field arithmetic and precomputation of base point for + * named curves. */ +mp_err +ec_group_set_gfp521(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P521) { + group->meth->field_mod = &ec_GFp_nistp521_mod; + group->meth->field_mul = &ec_GFp_nistp521_mul; + group->meth->field_sqr = &ec_GFp_nistp521_sqr; + group->meth->field_div = &ec_GFp_nistp521_div; + } + return MP_OKAY; +} diff --git a/usr/src/common/crypto/ecc/ecp_aff.c b/usr/src/common/crypto/ecc/ecp_aff.c new file mode 100644 index 0000000000..515b1e6bdb --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp_aff.c @@ -0,0 +1,367 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for prime field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang-Shantz <sheueling.chang@sun.com>, + * Stephen Fung <fungstep@hotmail.com>, and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories. + * Bodo Moeller <moeller@cdc.informatik.tu-darmstadt.de>, + * Nils Larsch <nla@trustcenter.de>, and + * Lenka Fibikova <fibikova@exp-math.uni-essen.de>, the OpenSSL Project + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecp.h" +#include "mplogic.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +/* Checks if point P(px, py) is at infinity. Uses affine coordinates. */ +mp_err +ec_GFp_pt_is_inf_aff(const mp_int *px, const mp_int *py) +{ + + if ((mp_cmp_z(px) == 0) && (mp_cmp_z(py) == 0)) { + return MP_YES; + } else { + return MP_NO; + } + +} + +/* Sets P(px, py) to be the point at infinity. Uses affine coordinates. */ +mp_err +ec_GFp_pt_set_inf_aff(mp_int *px, mp_int *py) +{ + mp_zero(px); + mp_zero(py); + return MP_OKAY; +} + +/* Computes R = P + Q based on IEEE P1363 A.10.1. Elliptic curve points P, + * Q, and R can all be identical. Uses affine coordinates. Assumes input + * is already field-encoded using field_enc, and returns output that is + * still field-encoded. */ +mp_err +ec_GFp_pt_add_aff(const mp_int *px, const mp_int *py, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int lambda, temp, tempx, tempy; + + MP_DIGITS(&lambda) = 0; + MP_DIGITS(&temp) = 0; + MP_DIGITS(&tempx) = 0; + MP_DIGITS(&tempy) = 0; + MP_CHECKOK(mp_init(&lambda, FLAG(px))); + MP_CHECKOK(mp_init(&temp, FLAG(px))); + MP_CHECKOK(mp_init(&tempx, FLAG(px))); + MP_CHECKOK(mp_init(&tempy, FLAG(px))); + /* if P = inf, then R = Q */ + if (ec_GFp_pt_is_inf_aff(px, py) == 0) { + MP_CHECKOK(mp_copy(qx, rx)); + MP_CHECKOK(mp_copy(qy, ry)); + res = MP_OKAY; + goto CLEANUP; + } + /* if Q = inf, then R = P */ + if (ec_GFp_pt_is_inf_aff(qx, qy) == 0) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + res = MP_OKAY; + goto CLEANUP; + } + /* if px != qx, then lambda = (py-qy) / (px-qx) */ + if (mp_cmp(px, qx) != 0) { + MP_CHECKOK(group->meth->field_sub(py, qy, &tempy, group->meth)); + MP_CHECKOK(group->meth->field_sub(px, qx, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_div(&tempy, &tempx, &lambda, group->meth)); + } else { + /* if py != qy or qy = 0, then R = inf */ + if (((mp_cmp(py, qy) != 0)) || (mp_cmp_z(qy) == 0)) { + mp_zero(rx); + mp_zero(ry); + res = MP_OKAY; + goto CLEANUP; + } + /* lambda = (3qx^2+a) / (2qy) */ + MP_CHECKOK(group->meth->field_sqr(qx, &tempx, group->meth)); + MP_CHECKOK(mp_set_int(&temp, 3)); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(&temp, &temp, group->meth)); + } + MP_CHECKOK(group->meth-> + field_mul(&tempx, &temp, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, &group->curvea, &tempx, group->meth)); + MP_CHECKOK(mp_set_int(&temp, 2)); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(&temp, &temp, group->meth)); + } + MP_CHECKOK(group->meth->field_mul(qy, &temp, &tempy, group->meth)); + MP_CHECKOK(group->meth-> + field_div(&tempx, &tempy, &lambda, group->meth)); + } + /* rx = lambda^2 - px - qx */ + MP_CHECKOK(group->meth->field_sqr(&lambda, &tempx, group->meth)); + MP_CHECKOK(group->meth->field_sub(&tempx, px, &tempx, group->meth)); + MP_CHECKOK(group->meth->field_sub(&tempx, qx, &tempx, group->meth)); + /* ry = (x1-x2) * lambda - y1 */ + MP_CHECKOK(group->meth->field_sub(qx, &tempx, &tempy, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(&tempy, &lambda, &tempy, group->meth)); + MP_CHECKOK(group->meth->field_sub(&tempy, qy, &tempy, group->meth)); + MP_CHECKOK(mp_copy(&tempx, rx)); + MP_CHECKOK(mp_copy(&tempy, ry)); + + CLEANUP: + mp_clear(&lambda); + mp_clear(&temp); + mp_clear(&tempx); + mp_clear(&tempy); + return res; +} + +/* Computes R = P - Q. Elliptic curve points P, Q, and R can all be + * identical. Uses affine coordinates. Assumes input is already + * field-encoded using field_enc, and returns output that is still + * field-encoded. */ +mp_err +ec_GFp_pt_sub_aff(const mp_int *px, const mp_int *py, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int nqy; + + MP_DIGITS(&nqy) = 0; + MP_CHECKOK(mp_init(&nqy, FLAG(px))); + /* nqy = -qy */ + MP_CHECKOK(group->meth->field_neg(qy, &nqy, group->meth)); + res = group->point_add(px, py, qx, &nqy, rx, ry, group); + CLEANUP: + mp_clear(&nqy); + return res; +} + +/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses + * affine coordinates. Assumes input is already field-encoded using + * field_enc, and returns output that is still field-encoded. */ +mp_err +ec_GFp_pt_dbl_aff(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group) +{ + return ec_GFp_pt_add_aff(px, py, px, py, rx, ry, group); +} + +/* by default, this routine is unused and thus doesn't need to be compiled */ +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF +/* Computes R = nP based on IEEE P1363 A.10.3. Elliptic curve points P and + * R can be identical. Uses affine coordinates. Assumes input is already + * field-encoded using field_enc, and returns output that is still + * field-encoded. */ +mp_err +ec_GFp_pt_mul_aff(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int k, k3, qx, qy, sx, sy; + int b1, b3, i, l; + + MP_DIGITS(&k) = 0; + MP_DIGITS(&k3) = 0; + MP_DIGITS(&qx) = 0; + MP_DIGITS(&qy) = 0; + MP_DIGITS(&sx) = 0; + MP_DIGITS(&sy) = 0; + MP_CHECKOK(mp_init(&k)); + MP_CHECKOK(mp_init(&k3)); + MP_CHECKOK(mp_init(&qx)); + MP_CHECKOK(mp_init(&qy)); + MP_CHECKOK(mp_init(&sx)); + MP_CHECKOK(mp_init(&sy)); + + /* if n = 0 then r = inf */ + if (mp_cmp_z(n) == 0) { + mp_zero(rx); + mp_zero(ry); + res = MP_OKAY; + goto CLEANUP; + } + /* Q = P, k = n */ + MP_CHECKOK(mp_copy(px, &qx)); + MP_CHECKOK(mp_copy(py, &qy)); + MP_CHECKOK(mp_copy(n, &k)); + /* if n < 0 then Q = -Q, k = -k */ + if (mp_cmp_z(n) < 0) { + MP_CHECKOK(group->meth->field_neg(&qy, &qy, group->meth)); + MP_CHECKOK(mp_neg(&k, &k)); + } +#ifdef ECL_DEBUG /* basic double and add method */ + l = mpl_significant_bits(&k) - 1; + MP_CHECKOK(mp_copy(&qx, &sx)); + MP_CHECKOK(mp_copy(&qy, &sy)); + for (i = l - 1; i >= 0; i--) { + /* S = 2S */ + MP_CHECKOK(group->point_dbl(&sx, &sy, &sx, &sy, group)); + /* if k_i = 1, then S = S + Q */ + if (mpl_get_bit(&k, i) != 0) { + MP_CHECKOK(group-> + point_add(&sx, &sy, &qx, &qy, &sx, &sy, group)); + } + } +#else /* double and add/subtract method from + * standard */ + /* k3 = 3 * k */ + MP_CHECKOK(mp_set_int(&k3, 3)); + MP_CHECKOK(mp_mul(&k, &k3, &k3)); + /* S = Q */ + MP_CHECKOK(mp_copy(&qx, &sx)); + MP_CHECKOK(mp_copy(&qy, &sy)); + /* l = index of high order bit in binary representation of 3*k */ + l = mpl_significant_bits(&k3) - 1; + /* for i = l-1 downto 1 */ + for (i = l - 1; i >= 1; i--) { + /* S = 2S */ + MP_CHECKOK(group->point_dbl(&sx, &sy, &sx, &sy, group)); + b3 = MP_GET_BIT(&k3, i); + b1 = MP_GET_BIT(&k, i); + /* if k3_i = 1 and k_i = 0, then S = S + Q */ + if ((b3 == 1) && (b1 == 0)) { + MP_CHECKOK(group-> + point_add(&sx, &sy, &qx, &qy, &sx, &sy, group)); + /* if k3_i = 0 and k_i = 1, then S = S - Q */ + } else if ((b3 == 0) && (b1 == 1)) { + MP_CHECKOK(group-> + point_sub(&sx, &sy, &qx, &qy, &sx, &sy, group)); + } + } +#endif + /* output S */ + MP_CHECKOK(mp_copy(&sx, rx)); + MP_CHECKOK(mp_copy(&sy, ry)); + + CLEANUP: + mp_clear(&k); + mp_clear(&k3); + mp_clear(&qx); + mp_clear(&qy); + mp_clear(&sx); + mp_clear(&sy); + return res; +} +#endif + +/* Validates a point on a GFp curve. */ +mp_err +ec_GFp_validate_point(const mp_int *px, const mp_int *py, const ECGroup *group) +{ + mp_err res = MP_NO; + mp_int accl, accr, tmp, pxt, pyt; + + MP_DIGITS(&accl) = 0; + MP_DIGITS(&accr) = 0; + MP_DIGITS(&tmp) = 0; + MP_DIGITS(&pxt) = 0; + MP_DIGITS(&pyt) = 0; + MP_CHECKOK(mp_init(&accl, FLAG(px))); + MP_CHECKOK(mp_init(&accr, FLAG(px))); + MP_CHECKOK(mp_init(&tmp, FLAG(px))); + MP_CHECKOK(mp_init(&pxt, FLAG(px))); + MP_CHECKOK(mp_init(&pyt, FLAG(px))); + + /* 1: Verify that publicValue is not the point at infinity */ + if (ec_GFp_pt_is_inf_aff(px, py) == MP_YES) { + res = MP_NO; + goto CLEANUP; + } + /* 2: Verify that the coordinates of publicValue are elements + * of the field. + */ + if ((MP_SIGN(px) == MP_NEG) || (mp_cmp(px, &group->meth->irr) >= 0) || + (MP_SIGN(py) == MP_NEG) || (mp_cmp(py, &group->meth->irr) >= 0)) { + res = MP_NO; + goto CLEANUP; + } + /* 3: Verify that publicValue is on the curve. */ + if (group->meth->field_enc) { + group->meth->field_enc(px, &pxt, group->meth); + group->meth->field_enc(py, &pyt, group->meth); + } else { + mp_copy(px, &pxt); + mp_copy(py, &pyt); + } + /* left-hand side: y^2 */ + MP_CHECKOK( group->meth->field_sqr(&pyt, &accl, group->meth) ); + /* right-hand side: x^3 + a*x + b */ + MP_CHECKOK( group->meth->field_sqr(&pxt, &tmp, group->meth) ); + MP_CHECKOK( group->meth->field_mul(&pxt, &tmp, &accr, group->meth) ); + MP_CHECKOK( group->meth->field_mul(&group->curvea, &pxt, &tmp, group->meth) ); + MP_CHECKOK( group->meth->field_add(&tmp, &accr, &accr, group->meth) ); + MP_CHECKOK( group->meth->field_add(&accr, &group->curveb, &accr, group->meth) ); + /* check LHS - RHS == 0 */ + MP_CHECKOK( group->meth->field_sub(&accl, &accr, &accr, group->meth) ); + if (mp_cmp_z(&accr) != 0) { + res = MP_NO; + goto CLEANUP; + } + /* 4: Verify that the order of the curve times the publicValue + * is the point at infinity. + */ + MP_CHECKOK( ECPoint_mul(group, &group->order, px, py, &pxt, &pyt) ); + if (ec_GFp_pt_is_inf_aff(&pxt, &pyt) != MP_YES) { + res = MP_NO; + goto CLEANUP; + } + + res = MP_YES; + +CLEANUP: + mp_clear(&accl); + mp_clear(&accr); + mp_clear(&tmp); + mp_clear(&pxt); + mp_clear(&pyt); + return res; +} diff --git a/usr/src/common/crypto/ecc/ecp_jac.c b/usr/src/common/crypto/ecc/ecp_jac.c new file mode 100644 index 0000000000..4c6b8280c2 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp_jac.c @@ -0,0 +1,563 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for prime field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang-Shantz <sheueling.chang@sun.com>, + * Stephen Fung <fungstep@hotmail.com>, and + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories. + * Bodo Moeller <moeller@cdc.informatik.tu-darmstadt.de>, + * Nils Larsch <nla@trustcenter.de>, and + * Lenka Fibikova <fibikova@exp-math.uni-essen.de>, the OpenSSL Project + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecp.h" +#include "mplogic.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif +#ifdef ECL_DEBUG +#include <assert.h> +#endif + +/* Converts a point P(px, py) from affine coordinates to Jacobian + * projective coordinates R(rx, ry, rz). Assumes input is already + * field-encoded using field_enc, and returns output that is still + * field-encoded. */ +mp_err +ec_GFp_pt_aff2jac(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group) +{ + mp_err res = MP_OKAY; + + if (ec_GFp_pt_is_inf_aff(px, py) == MP_YES) { + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, rz)); + } else { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + MP_CHECKOK(mp_set_int(rz, 1)); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(rz, rz, group->meth)); + } + } + CLEANUP: + return res; +} + +/* Converts a point P(px, py, pz) from Jacobian projective coordinates to + * affine coordinates R(rx, ry). P and R can share x and y coordinates. + * Assumes input is already field-encoded using field_enc, and returns + * output that is still field-encoded. */ +mp_err +ec_GFp_pt_jac2aff(const mp_int *px, const mp_int *py, const mp_int *pz, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int z1, z2, z3; + + MP_DIGITS(&z1) = 0; + MP_DIGITS(&z2) = 0; + MP_DIGITS(&z3) = 0; + MP_CHECKOK(mp_init(&z1, FLAG(px))); + MP_CHECKOK(mp_init(&z2, FLAG(px))); + MP_CHECKOK(mp_init(&z3, FLAG(px))); + + /* if point at infinity, then set point at infinity and exit */ + if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { + MP_CHECKOK(ec_GFp_pt_set_inf_aff(rx, ry)); + goto CLEANUP; + } + + /* transform (px, py, pz) into (px / pz^2, py / pz^3) */ + if (mp_cmp_d(pz, 1) == 0) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + } else { + MP_CHECKOK(group->meth->field_div(NULL, pz, &z1, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&z1, &z2, group->meth)); + MP_CHECKOK(group->meth->field_mul(&z1, &z2, &z3, group->meth)); + MP_CHECKOK(group->meth->field_mul(px, &z2, rx, group->meth)); + MP_CHECKOK(group->meth->field_mul(py, &z3, ry, group->meth)); + } + + CLEANUP: + mp_clear(&z1); + mp_clear(&z2); + mp_clear(&z3); + return res; +} + +/* Checks if point P(px, py, pz) is at infinity. Uses Jacobian + * coordinates. */ +mp_err +ec_GFp_pt_is_inf_jac(const mp_int *px, const mp_int *py, const mp_int *pz) +{ + return mp_cmp_z(pz); +} + +/* Sets P(px, py, pz) to be the point at infinity. Uses Jacobian + * coordinates. */ +mp_err +ec_GFp_pt_set_inf_jac(mp_int *px, mp_int *py, mp_int *pz) +{ + mp_zero(pz); + return MP_OKAY; +} + +/* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is + * (qx, qy, 1). Elliptic curve points P, Q, and R can all be identical. + * Uses mixed Jacobian-affine coordinates. Assumes input is already + * field-encoded using field_enc, and returns output that is still + * field-encoded. Uses equation (2) from Brown, Hankerson, Lopez, and + * Menezes. Software Implementation of the NIST Elliptic Curves Over Prime + * Fields. */ +mp_err +ec_GFp_pt_add_jac_aff(const mp_int *px, const mp_int *py, const mp_int *pz, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int A, B, C, D, C2, C3; + + MP_DIGITS(&A) = 0; + MP_DIGITS(&B) = 0; + MP_DIGITS(&C) = 0; + MP_DIGITS(&D) = 0; + MP_DIGITS(&C2) = 0; + MP_DIGITS(&C3) = 0; + MP_CHECKOK(mp_init(&A, FLAG(px))); + MP_CHECKOK(mp_init(&B, FLAG(px))); + MP_CHECKOK(mp_init(&C, FLAG(px))); + MP_CHECKOK(mp_init(&D, FLAG(px))); + MP_CHECKOK(mp_init(&C2, FLAG(px))); + MP_CHECKOK(mp_init(&C3, FLAG(px))); + + /* If either P or Q is the point at infinity, then return the other + * point */ + if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { + MP_CHECKOK(ec_GFp_pt_aff2jac(qx, qy, rx, ry, rz, group)); + goto CLEANUP; + } + if (ec_GFp_pt_is_inf_aff(qx, qy) == MP_YES) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + MP_CHECKOK(mp_copy(pz, rz)); + goto CLEANUP; + } + + /* A = qx * pz^2, B = qy * pz^3 */ + MP_CHECKOK(group->meth->field_sqr(pz, &A, group->meth)); + MP_CHECKOK(group->meth->field_mul(&A, pz, &B, group->meth)); + MP_CHECKOK(group->meth->field_mul(&A, qx, &A, group->meth)); + MP_CHECKOK(group->meth->field_mul(&B, qy, &B, group->meth)); + + /* C = A - px, D = B - py */ + MP_CHECKOK(group->meth->field_sub(&A, px, &C, group->meth)); + MP_CHECKOK(group->meth->field_sub(&B, py, &D, group->meth)); + + /* C2 = C^2, C3 = C^3 */ + MP_CHECKOK(group->meth->field_sqr(&C, &C2, group->meth)); + MP_CHECKOK(group->meth->field_mul(&C, &C2, &C3, group->meth)); + + /* rz = pz * C */ + MP_CHECKOK(group->meth->field_mul(pz, &C, rz, group->meth)); + + /* C = px * C^2 */ + MP_CHECKOK(group->meth->field_mul(px, &C2, &C, group->meth)); + /* A = D^2 */ + MP_CHECKOK(group->meth->field_sqr(&D, &A, group->meth)); + + /* rx = D^2 - (C^3 + 2 * (px * C^2)) */ + MP_CHECKOK(group->meth->field_add(&C, &C, rx, group->meth)); + MP_CHECKOK(group->meth->field_add(&C3, rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_sub(&A, rx, rx, group->meth)); + + /* C3 = py * C^3 */ + MP_CHECKOK(group->meth->field_mul(py, &C3, &C3, group->meth)); + + /* ry = D * (px * C^2 - rx) - py * C^3 */ + MP_CHECKOK(group->meth->field_sub(&C, rx, ry, group->meth)); + MP_CHECKOK(group->meth->field_mul(&D, ry, ry, group->meth)); + MP_CHECKOK(group->meth->field_sub(ry, &C3, ry, group->meth)); + + CLEANUP: + mp_clear(&A); + mp_clear(&B); + mp_clear(&C); + mp_clear(&D); + mp_clear(&C2); + mp_clear(&C3); + return res; +} + +/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses + * Jacobian coordinates. + * + * Assumes input is already field-encoded using field_enc, and returns + * output that is still field-encoded. + * + * This routine implements Point Doubling in the Jacobian Projective + * space as described in the paper "Efficient elliptic curve exponentiation + * using mixed coordinates", by H. Cohen, A Miyaji, T. Ono. + */ +mp_err +ec_GFp_pt_dbl_jac(const mp_int *px, const mp_int *py, const mp_int *pz, + mp_int *rx, mp_int *ry, mp_int *rz, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int t0, t1, M, S; + + MP_DIGITS(&t0) = 0; + MP_DIGITS(&t1) = 0; + MP_DIGITS(&M) = 0; + MP_DIGITS(&S) = 0; + MP_CHECKOK(mp_init(&t0, FLAG(px))); + MP_CHECKOK(mp_init(&t1, FLAG(px))); + MP_CHECKOK(mp_init(&M, FLAG(px))); + MP_CHECKOK(mp_init(&S, FLAG(px))); + + if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, rz)); + goto CLEANUP; + } + + if (mp_cmp_d(pz, 1) == 0) { + /* M = 3 * px^2 + a */ + MP_CHECKOK(group->meth->field_sqr(px, &t0, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, &t0, &M, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, &M, &t0, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&t0, &group->curvea, &M, group->meth)); + } else if (mp_cmp_int(&group->curvea, -3, FLAG(px)) == 0) { + /* M = 3 * (px + pz^2) * (px - pz^2) */ + MP_CHECKOK(group->meth->field_sqr(pz, &M, group->meth)); + MP_CHECKOK(group->meth->field_add(px, &M, &t0, group->meth)); + MP_CHECKOK(group->meth->field_sub(px, &M, &t1, group->meth)); + MP_CHECKOK(group->meth->field_mul(&t0, &t1, &M, group->meth)); + MP_CHECKOK(group->meth->field_add(&M, &M, &t0, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, &M, &M, group->meth)); + } else { + /* M = 3 * (px^2) + a * (pz^4) */ + MP_CHECKOK(group->meth->field_sqr(px, &t0, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, &t0, &M, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, &M, &t0, group->meth)); + MP_CHECKOK(group->meth->field_sqr(pz, &M, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&M, &M, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(&M, &group->curvea, &M, group->meth)); + MP_CHECKOK(group->meth->field_add(&M, &t0, &M, group->meth)); + } + + /* rz = 2 * py * pz */ + /* t0 = 4 * py^2 */ + if (mp_cmp_d(pz, 1) == 0) { + MP_CHECKOK(group->meth->field_add(py, py, rz, group->meth)); + MP_CHECKOK(group->meth->field_sqr(rz, &t0, group->meth)); + } else { + MP_CHECKOK(group->meth->field_add(py, py, &t0, group->meth)); + MP_CHECKOK(group->meth->field_mul(&t0, pz, rz, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&t0, &t0, group->meth)); + } + + /* S = 4 * px * py^2 = px * (2 * py)^2 */ + MP_CHECKOK(group->meth->field_mul(px, &t0, &S, group->meth)); + + /* rx = M^2 - 2 * S */ + MP_CHECKOK(group->meth->field_add(&S, &S, &t1, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&M, rx, group->meth)); + MP_CHECKOK(group->meth->field_sub(rx, &t1, rx, group->meth)); + + /* ry = M * (S - rx) - 8 * py^4 */ + MP_CHECKOK(group->meth->field_sqr(&t0, &t1, group->meth)); + if (mp_isodd(&t1)) { + MP_CHECKOK(mp_add(&t1, &group->meth->irr, &t1)); + } + MP_CHECKOK(mp_div_2(&t1, &t1)); + MP_CHECKOK(group->meth->field_sub(&S, rx, &S, group->meth)); + MP_CHECKOK(group->meth->field_mul(&M, &S, &M, group->meth)); + MP_CHECKOK(group->meth->field_sub(&M, &t1, ry, group->meth)); + + CLEANUP: + mp_clear(&t0); + mp_clear(&t1); + mp_clear(&M); + mp_clear(&S); + return res; +} + +/* by default, this routine is unused and thus doesn't need to be compiled */ +#ifdef ECL_ENABLE_GFP_PT_MUL_JAC +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GFp. Elliptic curve points P and R can be + * identical. Uses mixed Jacobian-affine coordinates. Assumes input is + * already field-encoded using field_enc, and returns output that is still + * field-encoded. Uses 4-bit window method. */ +mp_err +ec_GFp_pt_mul_jac(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int precomp[16][2], rz; + int i, ni, d; + + MP_DIGITS(&rz) = 0; + for (i = 0; i < 16; i++) { + MP_DIGITS(&precomp[i][0]) = 0; + MP_DIGITS(&precomp[i][1]) = 0; + } + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK((n != NULL) && (px != NULL) && (py != NULL), MP_BADARG); + + /* initialize precomputation table */ + for (i = 0; i < 16; i++) { + MP_CHECKOK(mp_init(&precomp[i][0])); + MP_CHECKOK(mp_init(&precomp[i][1])); + } + + /* fill precomputation table */ + mp_zero(&precomp[0][0]); + mp_zero(&precomp[0][1]); + MP_CHECKOK(mp_copy(px, &precomp[1][0])); + MP_CHECKOK(mp_copy(py, &precomp[1][1])); + for (i = 2; i < 16; i++) { + MP_CHECKOK(group-> + point_add(&precomp[1][0], &precomp[1][1], + &precomp[i - 1][0], &precomp[i - 1][1], + &precomp[i][0], &precomp[i][1], group)); + } + + d = (mpl_significant_bits(n) + 3) / 4; + + /* R = inf */ + MP_CHECKOK(mp_init(&rz)); + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, &rz)); + + for (i = d - 1; i >= 0; i--) { + /* compute window ni */ + ni = MP_GET_BIT(n, 4 * i + 3); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i + 2); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i + 1); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i); + /* R = 2^4 * R */ + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + /* R = R + (ni * P) */ + MP_CHECKOK(ec_GFp_pt_add_jac_aff + (rx, ry, &rz, &precomp[ni][0], &precomp[ni][1], rx, ry, + &rz, group)); + } + + /* convert result S to affine coordinates */ + MP_CHECKOK(ec_GFp_pt_jac2aff(rx, ry, &rz, rx, ry, group)); + + CLEANUP: + mp_clear(&rz); + for (i = 0; i < 16; i++) { + mp_clear(&precomp[i][0]); + mp_clear(&precomp[i][1]); + } + return res; +} +#endif + +/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k1 * G + + * k2 * P(x, y), where G is the generator (base point) of the group of + * points on the elliptic curve. Allows k1 = NULL or { k2, P } = NULL. + * Uses mixed Jacobian-affine coordinates. Input and output values are + * assumed to be NOT field-encoded. Uses algorithm 15 (simultaneous + * multiple point multiplication) from Brown, Hankerson, Lopez, Menezes. + * Software Implementation of the NIST Elliptic Curves over Prime Fields. */ +mp_err +ec_GFp_pts_mul_jac(const mp_int *k1, const mp_int *k2, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int precomp[4][4][2]; + mp_int rz; + const mp_int *a, *b; + int i, j; + int ai, bi, d; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + MP_DIGITS(&precomp[i][j][0]) = 0; + MP_DIGITS(&precomp[i][j][1]) = 0; + } + } + MP_DIGITS(&rz) = 0; + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK(!((k1 == NULL) + && ((k2 == NULL) || (px == NULL) + || (py == NULL))), MP_BADARG); + + /* if some arguments are not defined used ECPoint_mul */ + if (k1 == NULL) { + return ECPoint_mul(group, k2, px, py, rx, ry); + } else if ((k2 == NULL) || (px == NULL) || (py == NULL)) { + return ECPoint_mul(group, k1, NULL, NULL, rx, ry); + } + + /* initialize precomputation table */ + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + MP_CHECKOK(mp_init(&precomp[i][j][0], FLAG(k1))); + MP_CHECKOK(mp_init(&precomp[i][j][1], FLAG(k1))); + } + } + + /* fill precomputation table */ + /* assign {k1, k2} = {a, b} such that len(a) >= len(b) */ + if (mpl_significant_bits(k1) < mpl_significant_bits(k2)) { + a = k2; + b = k1; + if (group->meth->field_enc) { + MP_CHECKOK(group->meth-> + field_enc(px, &precomp[1][0][0], group->meth)); + MP_CHECKOK(group->meth-> + field_enc(py, &precomp[1][0][1], group->meth)); + } else { + MP_CHECKOK(mp_copy(px, &precomp[1][0][0])); + MP_CHECKOK(mp_copy(py, &precomp[1][0][1])); + } + MP_CHECKOK(mp_copy(&group->genx, &precomp[0][1][0])); + MP_CHECKOK(mp_copy(&group->geny, &precomp[0][1][1])); + } else { + a = k1; + b = k2; + MP_CHECKOK(mp_copy(&group->genx, &precomp[1][0][0])); + MP_CHECKOK(mp_copy(&group->geny, &precomp[1][0][1])); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth-> + field_enc(px, &precomp[0][1][0], group->meth)); + MP_CHECKOK(group->meth-> + field_enc(py, &precomp[0][1][1], group->meth)); + } else { + MP_CHECKOK(mp_copy(px, &precomp[0][1][0])); + MP_CHECKOK(mp_copy(py, &precomp[0][1][1])); + } + } + /* precompute [*][0][*] */ + mp_zero(&precomp[0][0][0]); + mp_zero(&precomp[0][0][1]); + MP_CHECKOK(group-> + point_dbl(&precomp[1][0][0], &precomp[1][0][1], + &precomp[2][0][0], &precomp[2][0][1], group)); + MP_CHECKOK(group-> + point_add(&precomp[1][0][0], &precomp[1][0][1], + &precomp[2][0][0], &precomp[2][0][1], + &precomp[3][0][0], &precomp[3][0][1], group)); + /* precompute [*][1][*] */ + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][1][0], &precomp[0][1][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][1][0], &precomp[i][1][1], group)); + } + /* precompute [*][2][*] */ + MP_CHECKOK(group-> + point_dbl(&precomp[0][1][0], &precomp[0][1][1], + &precomp[0][2][0], &precomp[0][2][1], group)); + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][2][0], &precomp[0][2][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][2][0], &precomp[i][2][1], group)); + } + /* precompute [*][3][*] */ + MP_CHECKOK(group-> + point_add(&precomp[0][1][0], &precomp[0][1][1], + &precomp[0][2][0], &precomp[0][2][1], + &precomp[0][3][0], &precomp[0][3][1], group)); + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][3][0], &precomp[0][3][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][3][0], &precomp[i][3][1], group)); + } + + d = (mpl_significant_bits(a) + 1) / 2; + + /* R = inf */ + MP_CHECKOK(mp_init(&rz, FLAG(k1))); + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, &rz)); + + for (i = d - 1; i >= 0; i--) { + ai = MP_GET_BIT(a, 2 * i + 1); + ai <<= 1; + ai |= MP_GET_BIT(a, 2 * i); + bi = MP_GET_BIT(b, 2 * i + 1); + bi <<= 1; + bi |= MP_GET_BIT(b, 2 * i); + /* R = 2^2 * R */ + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + /* R = R + (ai * A + bi * B) */ + MP_CHECKOK(ec_GFp_pt_add_jac_aff + (rx, ry, &rz, &precomp[ai][bi][0], &precomp[ai][bi][1], + rx, ry, &rz, group)); + } + + MP_CHECKOK(ec_GFp_pt_jac2aff(rx, ry, &rz, rx, ry, group)); + + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_dec(ry, ry, group->meth)); + } + + CLEANUP: + mp_clear(&rz); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + mp_clear(&precomp[i][j][0]); + mp_clear(&precomp[i][j][1]); + } + } + return res; +} diff --git a/usr/src/common/crypto/ecc/ecp_jm.c b/usr/src/common/crypto/ecc/ecp_jm.c new file mode 100644 index 0000000000..78daa2d971 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp_jm.c @@ -0,0 +1,341 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for prime field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stephen Fung <fungstep@hotmail.com>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ecp.h" +#include "ecl-priv.h" +#include "mplogic.h" +#ifndef _KERNEL +#include <stdlib.h> +#endif + +#define MAX_SCRATCH 6 + +/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses + * Modified Jacobian coordinates. + * + * Assumes input is already field-encoded using field_enc, and returns + * output that is still field-encoded. + * + */ +mp_err +ec_GFp_pt_dbl_jm(const mp_int *px, const mp_int *py, const mp_int *pz, + const mp_int *paz4, mp_int *rx, mp_int *ry, mp_int *rz, + mp_int *raz4, mp_int scratch[], const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int *t0, *t1, *M, *S; + + t0 = &scratch[0]; + t1 = &scratch[1]; + M = &scratch[2]; + S = &scratch[3]; + +#if MAX_SCRATCH < 4 +#error "Scratch array defined too small " +#endif + + /* Check for point at infinity */ + if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { + /* Set r = pt at infinity by setting rz = 0 */ + + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, rz)); + goto CLEANUP; + } + + /* M = 3 (px^2) + a*(pz^4) */ + MP_CHECKOK(group->meth->field_sqr(px, t0, group->meth)); + MP_CHECKOK(group->meth->field_add(t0, t0, M, group->meth)); + MP_CHECKOK(group->meth->field_add(t0, M, t0, group->meth)); + MP_CHECKOK(group->meth->field_add(t0, paz4, M, group->meth)); + + /* rz = 2 * py * pz */ + MP_CHECKOK(group->meth->field_mul(py, pz, S, group->meth)); + MP_CHECKOK(group->meth->field_add(S, S, rz, group->meth)); + + /* t0 = 2y^2 , t1 = 8y^4 */ + MP_CHECKOK(group->meth->field_sqr(py, t0, group->meth)); + MP_CHECKOK(group->meth->field_add(t0, t0, t0, group->meth)); + MP_CHECKOK(group->meth->field_sqr(t0, t1, group->meth)); + MP_CHECKOK(group->meth->field_add(t1, t1, t1, group->meth)); + + /* S = 4 * px * py^2 = 2 * px * t0 */ + MP_CHECKOK(group->meth->field_mul(px, t0, S, group->meth)); + MP_CHECKOK(group->meth->field_add(S, S, S, group->meth)); + + + /* rx = M^2 - 2S */ + MP_CHECKOK(group->meth->field_sqr(M, rx, group->meth)); + MP_CHECKOK(group->meth->field_sub(rx, S, rx, group->meth)); + MP_CHECKOK(group->meth->field_sub(rx, S, rx, group->meth)); + + /* ry = M * (S - rx) - t1 */ + MP_CHECKOK(group->meth->field_sub(S, rx, S, group->meth)); + MP_CHECKOK(group->meth->field_mul(S, M, ry, group->meth)); + MP_CHECKOK(group->meth->field_sub(ry, t1, ry, group->meth)); + + /* ra*z^4 = 2*t1*(apz4) */ + MP_CHECKOK(group->meth->field_mul(paz4, t1, raz4, group->meth)); + MP_CHECKOK(group->meth->field_add(raz4, raz4, raz4, group->meth)); + + + CLEANUP: + return res; +} + +/* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is + * (qx, qy, 1). Elliptic curve points P, Q, and R can all be identical. + * Uses mixed Modified_Jacobian-affine coordinates. Assumes input is + * already field-encoded using field_enc, and returns output that is still + * field-encoded. */ +mp_err +ec_GFp_pt_add_jm_aff(const mp_int *px, const mp_int *py, const mp_int *pz, + const mp_int *paz4, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, mp_int *rz, + mp_int *raz4, mp_int scratch[], const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int *A, *B, *C, *D, *C2, *C3; + + A = &scratch[0]; + B = &scratch[1]; + C = &scratch[2]; + D = &scratch[3]; + C2 = &scratch[4]; + C3 = &scratch[5]; + +#if MAX_SCRATCH < 6 +#error "Scratch array defined too small " +#endif + + /* If either P or Q is the point at infinity, then return the other + * point */ + if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { + MP_CHECKOK(ec_GFp_pt_aff2jac(qx, qy, rx, ry, rz, group)); + MP_CHECKOK(group->meth->field_sqr(rz, raz4, group->meth)); + MP_CHECKOK(group->meth->field_sqr(raz4, raz4, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(raz4, &group->curvea, raz4, group->meth)); + goto CLEANUP; + } + if (ec_GFp_pt_is_inf_aff(qx, qy) == MP_YES) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + MP_CHECKOK(mp_copy(pz, rz)); + MP_CHECKOK(mp_copy(paz4, raz4)); + goto CLEANUP; + } + + /* A = qx * pz^2, B = qy * pz^3 */ + MP_CHECKOK(group->meth->field_sqr(pz, A, group->meth)); + MP_CHECKOK(group->meth->field_mul(A, pz, B, group->meth)); + MP_CHECKOK(group->meth->field_mul(A, qx, A, group->meth)); + MP_CHECKOK(group->meth->field_mul(B, qy, B, group->meth)); + + /* C = A - px, D = B - py */ + MP_CHECKOK(group->meth->field_sub(A, px, C, group->meth)); + MP_CHECKOK(group->meth->field_sub(B, py, D, group->meth)); + + /* C2 = C^2, C3 = C^3 */ + MP_CHECKOK(group->meth->field_sqr(C, C2, group->meth)); + MP_CHECKOK(group->meth->field_mul(C, C2, C3, group->meth)); + + /* rz = pz * C */ + MP_CHECKOK(group->meth->field_mul(pz, C, rz, group->meth)); + + /* C = px * C^2 */ + MP_CHECKOK(group->meth->field_mul(px, C2, C, group->meth)); + /* A = D^2 */ + MP_CHECKOK(group->meth->field_sqr(D, A, group->meth)); + + /* rx = D^2 - (C^3 + 2 * (px * C^2)) */ + MP_CHECKOK(group->meth->field_add(C, C, rx, group->meth)); + MP_CHECKOK(group->meth->field_add(C3, rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_sub(A, rx, rx, group->meth)); + + /* C3 = py * C^3 */ + MP_CHECKOK(group->meth->field_mul(py, C3, C3, group->meth)); + + /* ry = D * (px * C^2 - rx) - py * C^3 */ + MP_CHECKOK(group->meth->field_sub(C, rx, ry, group->meth)); + MP_CHECKOK(group->meth->field_mul(D, ry, ry, group->meth)); + MP_CHECKOK(group->meth->field_sub(ry, C3, ry, group->meth)); + + /* raz4 = a * rz^4 */ + MP_CHECKOK(group->meth->field_sqr(rz, raz4, group->meth)); + MP_CHECKOK(group->meth->field_sqr(raz4, raz4, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(raz4, &group->curvea, raz4, group->meth)); +CLEANUP: + return res; +} + +/* Computes R = nP where R is (rx, ry) and P is the base point. Elliptic + * curve points P and R can be identical. Uses mixed Modified-Jacobian + * co-ordinates for doubling and Chudnovsky Jacobian coordinates for + * additions. Assumes input is already field-encoded using field_enc, and + * returns output that is still field-encoded. Uses 5-bit window NAF + * method (algorithm 11) for scalar-point multiplication from Brown, + * Hankerson, Lopez, Menezes. Software Implementation of the NIST Elliptic + * Curves Over Prime Fields. */ +mp_err +ec_GFp_pt_mul_jm_wNAF(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int precomp[16][2], rz, tpx, tpy; + mp_int raz4; + mp_int scratch[MAX_SCRATCH]; + signed char *naf = NULL; + int i, orderBitSize; + + MP_DIGITS(&rz) = 0; + MP_DIGITS(&raz4) = 0; + MP_DIGITS(&tpx) = 0; + MP_DIGITS(&tpy) = 0; + for (i = 0; i < 16; i++) { + MP_DIGITS(&precomp[i][0]) = 0; + MP_DIGITS(&precomp[i][1]) = 0; + } + for (i = 0; i < MAX_SCRATCH; i++) { + MP_DIGITS(&scratch[i]) = 0; + } + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK((n != NULL) && (px != NULL) && (py != NULL), MP_BADARG); + + /* initialize precomputation table */ + MP_CHECKOK(mp_init(&tpx, FLAG(n))); + MP_CHECKOK(mp_init(&tpy, FLAG(n)));; + MP_CHECKOK(mp_init(&rz, FLAG(n))); + MP_CHECKOK(mp_init(&raz4, FLAG(n))); + + for (i = 0; i < 16; i++) { + MP_CHECKOK(mp_init(&precomp[i][0], FLAG(n))); + MP_CHECKOK(mp_init(&precomp[i][1], FLAG(n))); + } + for (i = 0; i < MAX_SCRATCH; i++) { + MP_CHECKOK(mp_init(&scratch[i], FLAG(n))); + } + + /* Set out[8] = P */ + MP_CHECKOK(mp_copy(px, &precomp[8][0])); + MP_CHECKOK(mp_copy(py, &precomp[8][1])); + + /* Set (tpx, tpy) = 2P */ + MP_CHECKOK(group-> + point_dbl(&precomp[8][0], &precomp[8][1], &tpx, &tpy, + group)); + + /* Set 3P, 5P, ..., 15P */ + for (i = 8; i < 15; i++) { + MP_CHECKOK(group-> + point_add(&precomp[i][0], &precomp[i][1], &tpx, &tpy, + &precomp[i + 1][0], &precomp[i + 1][1], + group)); + } + + /* Set -15P, -13P, ..., -P */ + for (i = 0; i < 8; i++) { + MP_CHECKOK(mp_copy(&precomp[15 - i][0], &precomp[i][0])); + MP_CHECKOK(group->meth-> + field_neg(&precomp[15 - i][1], &precomp[i][1], + group->meth)); + } + + /* R = inf */ + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, &rz)); + + orderBitSize = mpl_significant_bits(&group->order); + + /* Allocate memory for NAF */ +#ifdef _KERNEL + naf = (signed char *) kmem_alloc((orderBitSize + 1), FLAG(n)); +#else + naf = (signed char *) malloc(sizeof(signed char) * (orderBitSize + 1)); + if (naf == NULL) { + res = MP_MEM; + goto CLEANUP; + } +#endif + + /* Compute 5NAF */ + ec_compute_wNAF(naf, orderBitSize, n, 5); + + /* wNAF method */ + for (i = orderBitSize; i >= 0; i--) { + /* R = 2R */ + ec_GFp_pt_dbl_jm(rx, ry, &rz, &raz4, rx, ry, &rz, + &raz4, scratch, group); + if (naf[i] != 0) { + ec_GFp_pt_add_jm_aff(rx, ry, &rz, &raz4, + &precomp[(naf[i] + 15) / 2][0], + &precomp[(naf[i] + 15) / 2][1], rx, ry, + &rz, &raz4, scratch, group); + } + } + + /* convert result S to affine coordinates */ + MP_CHECKOK(ec_GFp_pt_jac2aff(rx, ry, &rz, rx, ry, group)); + + CLEANUP: + for (i = 0; i < MAX_SCRATCH; i++) { + mp_clear(&scratch[i]); + } + for (i = 0; i < 16; i++) { + mp_clear(&precomp[i][0]); + mp_clear(&precomp[i][1]); + } + mp_clear(&tpx); + mp_clear(&tpy); + mp_clear(&rz); + mp_clear(&raz4); +#ifdef _KERNEL + kmem_free(naf, (orderBitSize + 1)); +#else + free(naf); +#endif + return res; +} diff --git a/usr/src/common/crypto/ecc/ecp_mont.c b/usr/src/common/crypto/ecc/ecp_mont.c new file mode 100644 index 0000000000..00ada56ed1 --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp_mont.c @@ -0,0 +1,211 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Uses Montgomery reduction for field arithmetic. See mpi/mpmontg.c for + * code implementation. */ + +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" +#include "ecl-priv.h" +#include "ecp.h" +#ifndef _KERNEL +#include <stdlib.h> +#include <stdio.h> +#endif + +/* Construct a generic GFMethod for arithmetic over prime fields with + * irreducible irr. */ +GFMethod * +GFMethod_consGFp_mont(const mp_int *irr) +{ + mp_err res = MP_OKAY; + int i; + GFMethod *meth = NULL; + mp_mont_modulus *mmm; + + meth = GFMethod_consGFp(irr); + if (meth == NULL) + return NULL; + +#ifdef _KERNEL + mmm = (mp_mont_modulus *) kmem_alloc(sizeof(mp_mont_modulus), + FLAG(irr)); +#else + mmm = (mp_mont_modulus *) malloc(sizeof(mp_mont_modulus)); +#endif + if (mmm == NULL) { + res = MP_MEM; + goto CLEANUP; + } + + meth->field_mul = &ec_GFp_mul_mont; + meth->field_sqr = &ec_GFp_sqr_mont; + meth->field_div = &ec_GFp_div_mont; + meth->field_enc = &ec_GFp_enc_mont; + meth->field_dec = &ec_GFp_dec_mont; + meth->extra1 = mmm; + meth->extra2 = NULL; + meth->extra_free = &ec_GFp_extra_free_mont; + + mmm->N = meth->irr; + i = mpl_significant_bits(&meth->irr); + i += MP_DIGIT_BIT - 1; + mmm->b = i - i % MP_DIGIT_BIT; + mmm->n0prime = 0 - s_mp_invmod_radix(MP_DIGIT(&meth->irr, 0)); + + CLEANUP: + if (res != MP_OKAY) { + GFMethod_free(meth); + return NULL; + } + return meth; +} + +/* Wrapper functions for generic prime field arithmetic. */ + +/* Field multiplication using Montgomery reduction. */ +mp_err +ec_GFp_mul_mont(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + +#ifdef MP_MONT_USE_MP_MUL + /* if MP_MONT_USE_MP_MUL is defined, then the function s_mp_mul_mont + * is not implemented and we have to use mp_mul and s_mp_redc directly + */ + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(s_mp_redc(r, (mp_mont_modulus *) meth->extra1)); +#else + mp_int s; + + MP_DIGITS(&s) = 0; + /* s_mp_mul_mont doesn't allow source and destination to be the same */ + if ((a == r) || (b == r)) { + MP_CHECKOK(mp_init(&s, FLAG(a))); + MP_CHECKOK(s_mp_mul_mont + (a, b, &s, (mp_mont_modulus *) meth->extra1)); + MP_CHECKOK(mp_copy(&s, r)); + mp_clear(&s); + } else { + return s_mp_mul_mont(a, b, r, (mp_mont_modulus *) meth->extra1); + } +#endif + CLEANUP: + return res; +} + +/* Field squaring using Montgomery reduction. */ +mp_err +ec_GFp_sqr_mont(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + return ec_GFp_mul_mont(a, a, r, meth); +} + +/* Field division using Montgomery reduction. */ +mp_err +ec_GFp_div_mont(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + /* if A=aZ represents a encoded in montgomery coordinates with Z and # + * and \ respectively represent multiplication and division in + * montgomery coordinates, then A\B = (a/b)Z = (A/B)Z and Binv = + * (1/b)Z = (1/B)(Z^2) where B # Binv = Z */ + MP_CHECKOK(ec_GFp_div(a, b, r, meth)); + MP_CHECKOK(ec_GFp_enc_mont(r, r, meth)); + if (a == NULL) { + MP_CHECKOK(ec_GFp_enc_mont(r, r, meth)); + } + CLEANUP: + return res; +} + +/* Encode a field element in Montgomery form. See s_mp_to_mont in + * mpi/mpmontg.c */ +mp_err +ec_GFp_enc_mont(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_mont_modulus *mmm; + mp_err res = MP_OKAY; + + mmm = (mp_mont_modulus *) meth->extra1; + MP_CHECKOK(mpl_lsh(a, r, mmm->b)); + MP_CHECKOK(mp_mod(r, &mmm->N, r)); + CLEANUP: + return res; +} + +/* Decode a field element from Montgomery form. */ +mp_err +ec_GFp_dec_mont(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + if (a != r) { + MP_CHECKOK(mp_copy(a, r)); + } + MP_CHECKOK(s_mp_redc(r, (mp_mont_modulus *) meth->extra1)); + CLEANUP: + return res; +} + +/* Free the memory allocated to the extra fields of Montgomery GFMethod + * object. */ +void +ec_GFp_extra_free_mont(GFMethod *meth) +{ + if (meth->extra1 != NULL) { +#ifdef _KERNEL + kmem_free(meth->extra1, sizeof(mp_mont_modulus)); +#else + free(meth->extra1); +#endif + meth->extra1 = NULL; + } +} diff --git a/usr/src/common/crypto/ecc/ecp_test.c b/usr/src/common/crypto/ecc/ecp_test.c new file mode 100644 index 0000000000..7df62901cc --- /dev/null +++ b/usr/src/common/crypto/ecc/ecp_test.c @@ -0,0 +1,482 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the elliptic curve math library for prime field curves. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef _KERNEL +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/param.h> +#include <sys/modctl.h> +#include <sys/ddi.h> +#include <sys/crypto/spi.h> +#include <sys/sysmacros.h> +#include <sys/strsun.h> +#include <sys/md5.h> +#include <sys/sha1.h> +#include <sys/sha2.h> +#include <sys/random.h> +#include <sys/conf.h> +#include <sys/devops.h> +#include <sys/sunddi.h> +#include <sys/varargs.h> +#include <sys/kmem.h> +#include <sys/kstat.h> +#include <sys/crypto/common.h> +#else +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <assert.h> +#include <time.h> +#include <sys/time.h> +#include <sys/resource.h> +#endif /* _KERNEL */ + +#include "mpi.h" +#include "mplogic.h" +#include "mpprime.h" +#include "ecl.h" +#include "ecl-curve.h" +#include "ecp.h" +#include "ecc_impl.h" +#include "ec.h" + +#ifndef KM_SLEEP +#define KM_SLEEP 0 +#endif + +#ifndef _KERNEL +/* Time k repetitions of operation op. */ +#define M_TimeOperation(op, k) { \ + double dStart, dNow, dUserTime; \ + struct rusage ru; \ + int i; \ + getrusage(RUSAGE_SELF, &ru); \ + dStart = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + for (i = 0; i < k; i++) { \ + { op; } \ + }; \ + getrusage(RUSAGE_SELF, &ru); \ + dNow = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + dUserTime = dNow-dStart; \ + if (dUserTime) printf(" %-45s k: %6i, t: %6.2f sec\n", #op, k, dUserTime); \ +} +#else +#define M_TimeOperation(op, k) +#endif + +/* Test curve using generic field arithmetic. */ +#define ECTEST_GENERIC_GFP(name_c, name) \ + printf("Testing %s using generic implementation...\n", name_c); \ + params = EC_GetNamedCurveParams(name, KM_SLEEP); \ + if (params == NULL) { \ + printf(" Error: could not construct params.\n"); \ + res = MP_NO; \ + goto CLEANUP; \ + } \ + ECGroup_free(group); \ + group = ECGroup_fromHex(params, KM_SLEEP); \ + if (group == NULL) { \ + printf(" Error: could not construct group.\n"); \ + res = MP_NO; \ + goto CLEANUP; \ + } \ + MP_CHECKOK( ectest_curve_GFp(group, ectestPrint, ectestTime, 1, KM_SLEEP) ); \ + printf("... okay.\n"); + +/* Test curve using specific field arithmetic. */ +#define ECTEST_NAMED_GFP(name_c, name) \ + printf("Testing %s using specific implementation...\n", name_c); \ + ECGroup_free(group); \ + group = ECGroup_fromName(name, KM_SLEEP); \ + if (group == NULL) { \ + printf(" Warning: could not construct group.\n"); \ + printf("... failed; continuing with remaining tests.\n"); \ + } else { \ + MP_CHECKOK( ectest_curve_GFp(group, ectestPrint, ectestTime, 0, KM_SLEEP) ); \ + printf("... okay.\n"); \ + } + +/* Performs basic tests of elliptic curve cryptography over prime fields. + * If tests fail, then it prints an error message, aborts, and returns an + * error code. Otherwise, returns 0. */ +int +ectest_curve_GFp(ECGroup *group, int ectestPrint, int ectestTime, + int generic, int kmflag) +{ + + mp_int one, order_1, gx, gy, rx, ry, n; + int size; + mp_err res; + char s[1000]; + + /* initialize values */ + MP_CHECKOK(mp_init(&one, kmflag)); + MP_CHECKOK(mp_init(&order_1, kmflag)); + MP_CHECKOK(mp_init(&gx, kmflag)); + MP_CHECKOK(mp_init(&gy, kmflag)); + MP_CHECKOK(mp_init(&rx, kmflag)); + MP_CHECKOK(mp_init(&ry, kmflag)); + MP_CHECKOK(mp_init(&n, kmflag)); + + MP_CHECKOK(mp_set_int(&one, 1)); + MP_CHECKOK(mp_sub(&group->order, &one, &order_1)); + + /* encode base point */ + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(&group->genx, &gx, group->meth)); + MP_CHECKOK(group->meth->field_dec(&group->geny, &gy, group->meth)); + } else { + MP_CHECKOK(mp_copy(&group->genx, &gx)); + MP_CHECKOK(mp_copy(&group->geny, &gy)); + } + if (ectestPrint) { + /* output base point */ + printf(" base point P:\n"); + MP_CHECKOK(mp_toradix(&gx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&gy, s, 16)); + printf(" %s\n", s); + if (group->meth->field_enc) { + printf(" base point P (encoded):\n"); + MP_CHECKOK(mp_toradix(&group->genx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&group->geny, s, 16)); + printf(" %s\n", s); + } + } + +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ec_GFp_pt_mul_aff + (&order_1, &group->genx, &group->geny, &rx, &ry, group)); + if (ectestPrint) { + printf(" (order-1)*P (affine):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(group->meth->field_neg(&ry, &ry, group->meth)); + if ((mp_cmp(&rx, &group->genx) != 0) + || (mp_cmp(&ry, &group->geny) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ec_GFp_pt_mul_jac + (&order_1, &group->genx, &group->geny, &rx, &ry, group)); + if (ectestPrint) { + printf(" (order-1)*P (jacobian):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(group->meth->field_neg(&ry, &ry, group->meth)); + if ((mp_cmp(&rx, &group->genx) != 0) + || (mp_cmp(&ry, &group->geny) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ECPoint_mul(group, &order_1, NULL, NULL, &rx, &ry)); + if (ectestPrint) { + printf(" (order-1)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(mp_submod(&group->meth->irr, &ry, &group->meth->irr, &ry)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ECPoint_mul(group, &order_1, &gx, &gy, &rx, &ry)); + if (ectestPrint) { + printf(" (order-1)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(mp_submod(&group->meth->irr, &ry, &group->meth->irr, &ry)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ec_GFp_pt_mul_aff + (&group->order, &group->genx, &group->geny, &rx, &ry, + group)); + if (ectestPrint) { + printf(" (order)*P (affine):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + +#ifdef ECL_ENABLE_GFP_PT_MUL_JAC + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ec_GFp_pt_mul_jac + (&group->order, &group->genx, &group->geny, &rx, &ry, + group)); + if (ectestPrint) { + printf(" (order)*P (jacobian):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ECPoint_mul(group, &group->order, NULL, NULL, &rx, &ry)); + if (ectestPrint) { + printf(" (order)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ECPoint_mul(group, &group->order, &gx, &gy, &rx, &ry)); + if (ectestPrint) { + printf(" (order)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* check that (order-1)P + (order-1)P + P == (order-1)P */ + MP_CHECKOK(ECPoints_mul + (group, &order_1, &order_1, &gx, &gy, &rx, &ry)); + MP_CHECKOK(ECPoints_mul(group, &one, &one, &rx, &ry, &rx, &ry)); + if (ectestPrint) { + printf + (" (order-1)*P + (order-1)*P + P == (order-1)*P (ECPoints_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(mp_submod(&group->meth->irr, &ry, &group->meth->irr, &ry)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* test validate_point function */ + if (ECPoint_validate(group, &gx, &gy) != MP_YES) { + printf(" Error: validate point on base point failed.\n"); + res = MP_NO; + goto CLEANUP; + } + MP_CHECKOK(mp_add_d(&gy, 1, &ry)); + if (ECPoint_validate(group, &gx, &ry) != MP_NO) { + printf(" Error: validate point on invalid point passed.\n"); + res = MP_NO; + goto CLEANUP; + } + + if (ectestTime) { + /* compute random scalar */ + size = mpl_significant_bits(&group->meth->irr); + if (size < MP_OKAY) { + goto CLEANUP; + } + MP_CHECKOK(mpp_random_size(&n, (size + ECL_BITS - 1) / ECL_BITS)); + MP_CHECKOK(group->meth->field_mod(&n, &n, group->meth)); + /* timed test */ + if (generic) { +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF + M_TimeOperation(MP_CHECKOK + (ec_GFp_pt_mul_aff + (&n, &group->genx, &group->geny, &rx, &ry, + group)), 100); +#endif + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, NULL, NULL, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoints_mul + (group, &n, &n, &gx, &gy, &rx, &ry)), 100); + } else { + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, NULL, NULL, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, &gx, &gy, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoints_mul + (group, &n, &n, &gx, &gy, &rx, &ry)), 100); + } + } + + CLEANUP: + mp_clear(&one); + mp_clear(&order_1); + mp_clear(&gx); + mp_clear(&gy); + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&n); + if (res != MP_OKAY) { +#ifdef _KERNEL + printf(" Error: exiting with error value 0x%x\n", res); +#else + printf(" Error: exiting with error value %i\n", res); +#endif + } + return res; +} + +/* Performs tests of elliptic curve cryptography over prime fields If + * tests fail, then it prints an error message, aborts, and returns an + * error code. Otherwise, returns 0. */ +int +ecp_test() +{ + + int ectestTime = 0; + int ectestPrint = 0; + int i; + ECGroup *group = NULL; + ECCurveParams *params = NULL; + mp_err res; + + /* generic arithmetic tests */ + ECTEST_GENERIC_GFP("SECP-160R1", ECCurve_SECG_PRIME_160R1); + + /* specific arithmetic tests */ + ECTEST_NAMED_GFP("NIST-P192", ECCurve_NIST_P192); + ECTEST_NAMED_GFP("NIST-P224", ECCurve_NIST_P224); + ECTEST_NAMED_GFP("NIST-P256", ECCurve_NIST_P256); + ECTEST_NAMED_GFP("NIST-P384", ECCurve_NIST_P384); + ECTEST_NAMED_GFP("NIST-P521", ECCurve_NIST_P521); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME192v1", ECCurve_X9_62_PRIME_192V1); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME192v2", ECCurve_X9_62_PRIME_192V2); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME192v3", ECCurve_X9_62_PRIME_192V3); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME239v1", ECCurve_X9_62_PRIME_239V1); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME239v2", ECCurve_X9_62_PRIME_239V2); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME239v3", ECCurve_X9_62_PRIME_239V3); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME256v1", ECCurve_X9_62_PRIME_256V1); + ECTEST_NAMED_GFP("SECP-112R1", ECCurve_SECG_PRIME_112R1); + ECTEST_NAMED_GFP("SECP-112R2", ECCurve_SECG_PRIME_112R2); + ECTEST_NAMED_GFP("SECP-128R1", ECCurve_SECG_PRIME_128R1); + ECTEST_NAMED_GFP("SECP-128R2", ECCurve_SECG_PRIME_128R2); + ECTEST_NAMED_GFP("SECP-160K1", ECCurve_SECG_PRIME_160K1); + ECTEST_NAMED_GFP("SECP-160R1", ECCurve_SECG_PRIME_160R1); + ECTEST_NAMED_GFP("SECP-160R2", ECCurve_SECG_PRIME_160R2); + ECTEST_NAMED_GFP("SECP-192K1", ECCurve_SECG_PRIME_192K1); + ECTEST_NAMED_GFP("SECP-192R1", ECCurve_SECG_PRIME_192R1); + ECTEST_NAMED_GFP("SECP-224K1", ECCurve_SECG_PRIME_224K1); + ECTEST_NAMED_GFP("SECP-224R1", ECCurve_SECG_PRIME_224R1); + ECTEST_NAMED_GFP("SECP-256K1", ECCurve_SECG_PRIME_256K1); + ECTEST_NAMED_GFP("SECP-256R1", ECCurve_SECG_PRIME_256R1); + ECTEST_NAMED_GFP("SECP-384R1", ECCurve_SECG_PRIME_384R1); + ECTEST_NAMED_GFP("SECP-521R1", ECCurve_SECG_PRIME_521R1); + ECTEST_NAMED_GFP("WTLS-6 (112)", ECCurve_WTLS_6); + ECTEST_NAMED_GFP("WTLS-7 (160)", ECCurve_WTLS_7); + ECTEST_NAMED_GFP("WTLS-8 (112)", ECCurve_WTLS_8); + ECTEST_NAMED_GFP("WTLS-9 (160)", ECCurve_WTLS_9); + ECTEST_NAMED_GFP("WTLS-12 (224)", ECCurve_WTLS_12); + + CLEANUP: + EC_FreeCurveParams(params); + ECGroup_free(group); + if (res != MP_OKAY) { +#ifdef _KERNEL + printf("Error: exiting with error value 0x%x\n", res); +#else + printf("Error: exiting with error value %i\n", res); +#endif + } + return res; +} diff --git a/usr/src/common/crypto/ecc/oid.c b/usr/src/common/crypto/ecc/oid.c new file mode 100644 index 0000000000..1f4cfc7637 --- /dev/null +++ b/usr/src/common/crypto/ecc/oid.c @@ -0,0 +1,454 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/param.h> +#ifdef _KERNEL +#include <sys/kmem.h> +#else +#include <string.h> +#endif +#include "ec.h" +#include "ecl-curve.h" +#include "ecc_impl.h" +#include "secoidt.h" + +#define CERTICOM_OID 0x2b, 0x81, 0x04 +#define SECG_OID CERTICOM_OID, 0x00 + +#define ANSI_X962_OID 0x2a, 0x86, 0x48, 0xce, 0x3d +#define ANSI_X962_CURVE_OID ANSI_X962_OID, 0x03 +#define ANSI_X962_GF2m_OID ANSI_X962_CURVE_OID, 0x00 +#define ANSI_X962_GFp_OID ANSI_X962_CURVE_OID, 0x01 + +#define CONST_OID static const unsigned char + +/* ANSI X9.62 prime curve OIDs */ +/* NOTE: prime192v1 is the same as secp192r1, prime256v1 is the + * same as secp256r1 + */ +CONST_OID ansiX962prime192v1[] = { ANSI_X962_GFp_OID, 0x01 }; +CONST_OID ansiX962prime192v2[] = { ANSI_X962_GFp_OID, 0x02 }; +CONST_OID ansiX962prime192v3[] = { ANSI_X962_GFp_OID, 0x03 }; +CONST_OID ansiX962prime239v1[] = { ANSI_X962_GFp_OID, 0x04 }; +CONST_OID ansiX962prime239v2[] = { ANSI_X962_GFp_OID, 0x05 }; +CONST_OID ansiX962prime239v3[] = { ANSI_X962_GFp_OID, 0x06 }; +CONST_OID ansiX962prime256v1[] = { ANSI_X962_GFp_OID, 0x07 }; + +/* SECG prime curve OIDs */ +CONST_OID secgECsecp112r1[] = { SECG_OID, 0x06 }; +CONST_OID secgECsecp112r2[] = { SECG_OID, 0x07 }; +CONST_OID secgECsecp128r1[] = { SECG_OID, 0x1c }; +CONST_OID secgECsecp128r2[] = { SECG_OID, 0x1d }; +CONST_OID secgECsecp160k1[] = { SECG_OID, 0x09 }; +CONST_OID secgECsecp160r1[] = { SECG_OID, 0x08 }; +CONST_OID secgECsecp160r2[] = { SECG_OID, 0x1e }; +CONST_OID secgECsecp192k1[] = { SECG_OID, 0x1f }; +CONST_OID secgECsecp224k1[] = { SECG_OID, 0x20 }; +CONST_OID secgECsecp224r1[] = { SECG_OID, 0x21 }; +CONST_OID secgECsecp256k1[] = { SECG_OID, 0x0a }; +CONST_OID secgECsecp384r1[] = { SECG_OID, 0x22 }; +CONST_OID secgECsecp521r1[] = { SECG_OID, 0x23 }; + +/* SECG characterisitic two curve OIDs */ +CONST_OID secgECsect113r1[] = {SECG_OID, 0x04 }; +CONST_OID secgECsect113r2[] = {SECG_OID, 0x05 }; +CONST_OID secgECsect131r1[] = {SECG_OID, 0x16 }; +CONST_OID secgECsect131r2[] = {SECG_OID, 0x17 }; +CONST_OID secgECsect163k1[] = {SECG_OID, 0x01 }; +CONST_OID secgECsect163r1[] = {SECG_OID, 0x02 }; +CONST_OID secgECsect163r2[] = {SECG_OID, 0x0f }; +CONST_OID secgECsect193r1[] = {SECG_OID, 0x18 }; +CONST_OID secgECsect193r2[] = {SECG_OID, 0x19 }; +CONST_OID secgECsect233k1[] = {SECG_OID, 0x1a }; +CONST_OID secgECsect233r1[] = {SECG_OID, 0x1b }; +CONST_OID secgECsect239k1[] = {SECG_OID, 0x03 }; +CONST_OID secgECsect283k1[] = {SECG_OID, 0x10 }; +CONST_OID secgECsect283r1[] = {SECG_OID, 0x11 }; +CONST_OID secgECsect409k1[] = {SECG_OID, 0x24 }; +CONST_OID secgECsect409r1[] = {SECG_OID, 0x25 }; +CONST_OID secgECsect571k1[] = {SECG_OID, 0x26 }; +CONST_OID secgECsect571r1[] = {SECG_OID, 0x27 }; + +/* ANSI X9.62 characteristic two curve OIDs */ +CONST_OID ansiX962c2pnb163v1[] = { ANSI_X962_GF2m_OID, 0x01 }; +CONST_OID ansiX962c2pnb163v2[] = { ANSI_X962_GF2m_OID, 0x02 }; +CONST_OID ansiX962c2pnb163v3[] = { ANSI_X962_GF2m_OID, 0x03 }; +CONST_OID ansiX962c2pnb176v1[] = { ANSI_X962_GF2m_OID, 0x04 }; +CONST_OID ansiX962c2tnb191v1[] = { ANSI_X962_GF2m_OID, 0x05 }; +CONST_OID ansiX962c2tnb191v2[] = { ANSI_X962_GF2m_OID, 0x06 }; +CONST_OID ansiX962c2tnb191v3[] = { ANSI_X962_GF2m_OID, 0x07 }; +CONST_OID ansiX962c2onb191v4[] = { ANSI_X962_GF2m_OID, 0x08 }; +CONST_OID ansiX962c2onb191v5[] = { ANSI_X962_GF2m_OID, 0x09 }; +CONST_OID ansiX962c2pnb208w1[] = { ANSI_X962_GF2m_OID, 0x0a }; +CONST_OID ansiX962c2tnb239v1[] = { ANSI_X962_GF2m_OID, 0x0b }; +CONST_OID ansiX962c2tnb239v2[] = { ANSI_X962_GF2m_OID, 0x0c }; +CONST_OID ansiX962c2tnb239v3[] = { ANSI_X962_GF2m_OID, 0x0d }; +CONST_OID ansiX962c2onb239v4[] = { ANSI_X962_GF2m_OID, 0x0e }; +CONST_OID ansiX962c2onb239v5[] = { ANSI_X962_GF2m_OID, 0x0f }; +CONST_OID ansiX962c2pnb272w1[] = { ANSI_X962_GF2m_OID, 0x10 }; +CONST_OID ansiX962c2pnb304w1[] = { ANSI_X962_GF2m_OID, 0x11 }; +CONST_OID ansiX962c2tnb359v1[] = { ANSI_X962_GF2m_OID, 0x12 }; +CONST_OID ansiX962c2pnb368w1[] = { ANSI_X962_GF2m_OID, 0x13 }; +CONST_OID ansiX962c2tnb431r1[] = { ANSI_X962_GF2m_OID, 0x14 }; + +#define OI(x) { siDEROID, (unsigned char *)x, sizeof x } +#ifndef SECOID_NO_STRINGS +#define OD(oid,tag,desc,mech,ext) { OI(oid), tag, desc, mech, ext } +#else +#define OD(oid,tag,desc,mech,ext) { OI(oid), tag, 0, mech, ext } +#endif + +#define CKM_INVALID_MECHANISM 0xffffffffUL + +/* XXX this is incorrect */ +#define INVALID_CERT_EXTENSION 1 + +#define CKM_ECDSA 0x00001041 +#define CKM_ECDSA_SHA1 0x00001042 +#define CKM_ECDH1_DERIVE 0x00001050 + +static SECOidData ANSI_prime_oids[] = { + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + + OD( ansiX962prime192v1, ECCurve_NIST_P192, + "ANSI X9.62 elliptic curve prime192v1 (aka secp192r1, NIST P-192)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962prime192v2, ECCurve_X9_62_PRIME_192V2, + "ANSI X9.62 elliptic curve prime192v2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962prime192v3, ECCurve_X9_62_PRIME_192V3, + "ANSI X9.62 elliptic curve prime192v3", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962prime239v1, ECCurve_X9_62_PRIME_239V1, + "ANSI X9.62 elliptic curve prime239v1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962prime239v2, ECCurve_X9_62_PRIME_239V2, + "ANSI X9.62 elliptic curve prime239v2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962prime239v3, ECCurve_X9_62_PRIME_239V3, + "ANSI X9.62 elliptic curve prime239v3", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962prime256v1, ECCurve_NIST_P256, + "ANSI X9.62 elliptic curve prime256v1 (aka secp256r1, NIST P-256)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ) +}; + +static SECOidData SECG_oids[] = { + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + + OD( secgECsect163k1, ECCurve_NIST_K163, + "SECG elliptic curve sect163k1 (aka NIST K-163)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect163r1, ECCurve_SECG_CHAR2_163R1, + "SECG elliptic curve sect163r1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect239k1, ECCurve_SECG_CHAR2_239K1, + "SECG elliptic curve sect239k1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect113r1, ECCurve_SECG_CHAR2_113R1, + "SECG elliptic curve sect113r1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect113r2, ECCurve_SECG_CHAR2_113R2, + "SECG elliptic curve sect113r2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp112r1, ECCurve_SECG_PRIME_112R1, + "SECG elliptic curve secp112r1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp112r2, ECCurve_SECG_PRIME_112R2, + "SECG elliptic curve secp112r2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp160r1, ECCurve_SECG_PRIME_160R1, + "SECG elliptic curve secp160r1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp160k1, ECCurve_SECG_PRIME_160K1, + "SECG elliptic curve secp160k1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp256k1, ECCurve_SECG_PRIME_256K1, + "SECG elliptic curve secp256k1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + OD( secgECsect163r2, ECCurve_NIST_B163, + "SECG elliptic curve sect163r2 (aka NIST B-163)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect283k1, ECCurve_NIST_K283, + "SECG elliptic curve sect283k1 (aka NIST K-283)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect283r1, ECCurve_NIST_B283, + "SECG elliptic curve sect283r1 (aka NIST B-283)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + OD( secgECsect131r1, ECCurve_SECG_CHAR2_131R1, + "SECG elliptic curve sect131r1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect131r2, ECCurve_SECG_CHAR2_131R2, + "SECG elliptic curve sect131r2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect193r1, ECCurve_SECG_CHAR2_193R1, + "SECG elliptic curve sect193r1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect193r2, ECCurve_SECG_CHAR2_193R2, + "SECG elliptic curve sect193r2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect233k1, ECCurve_NIST_K233, + "SECG elliptic curve sect233k1 (aka NIST K-233)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect233r1, ECCurve_NIST_B233, + "SECG elliptic curve sect233r1 (aka NIST B-233)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp128r1, ECCurve_SECG_PRIME_128R1, + "SECG elliptic curve secp128r1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp128r2, ECCurve_SECG_PRIME_128R2, + "SECG elliptic curve secp128r2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp160r2, ECCurve_SECG_PRIME_160R2, + "SECG elliptic curve secp160r2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp192k1, ECCurve_SECG_PRIME_192K1, + "SECG elliptic curve secp192k1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp224k1, ECCurve_SECG_PRIME_224K1, + "SECG elliptic curve secp224k1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp224r1, ECCurve_NIST_P224, + "SECG elliptic curve secp224r1 (aka NIST P-224)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp384r1, ECCurve_NIST_P384, + "SECG elliptic curve secp384r1 (aka NIST P-384)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsecp521r1, ECCurve_NIST_P521, + "SECG elliptic curve secp521r1 (aka NIST P-521)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect409k1, ECCurve_NIST_K409, + "SECG elliptic curve sect409k1 (aka NIST K-409)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect409r1, ECCurve_NIST_B409, + "SECG elliptic curve sect409r1 (aka NIST B-409)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect571k1, ECCurve_NIST_K571, + "SECG elliptic curve sect571k1 (aka NIST K-571)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( secgECsect571r1, ECCurve_NIST_B571, + "SECG elliptic curve sect571r1 (aka NIST B-571)", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ) +}; + +static SECOidData ANSI_oids[] = { + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + + /* ANSI X9.62 named elliptic curves (characteristic two field) */ + OD( ansiX962c2pnb163v1, ECCurve_X9_62_CHAR2_PNB163V1, + "ANSI X9.62 elliptic curve c2pnb163v1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2pnb163v2, ECCurve_X9_62_CHAR2_PNB163V2, + "ANSI X9.62 elliptic curve c2pnb163v2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2pnb163v3, ECCurve_X9_62_CHAR2_PNB163V3, + "ANSI X9.62 elliptic curve c2pnb163v3", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2pnb176v1, ECCurve_X9_62_CHAR2_PNB176V1, + "ANSI X9.62 elliptic curve c2pnb176v1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2tnb191v1, ECCurve_X9_62_CHAR2_TNB191V1, + "ANSI X9.62 elliptic curve c2tnb191v1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2tnb191v2, ECCurve_X9_62_CHAR2_TNB191V2, + "ANSI X9.62 elliptic curve c2tnb191v2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2tnb191v3, ECCurve_X9_62_CHAR2_TNB191V3, + "ANSI X9.62 elliptic curve c2tnb191v3", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + OD( ansiX962c2pnb208w1, ECCurve_X9_62_CHAR2_PNB208W1, + "ANSI X9.62 elliptic curve c2pnb208w1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2tnb239v1, ECCurve_X9_62_CHAR2_TNB239V1, + "ANSI X9.62 elliptic curve c2tnb239v1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2tnb239v2, ECCurve_X9_62_CHAR2_TNB239V2, + "ANSI X9.62 elliptic curve c2tnb239v2", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2tnb239v3, ECCurve_X9_62_CHAR2_TNB239V3, + "ANSI X9.62 elliptic curve c2tnb239v3", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + { { siDEROID, NULL, 0 }, ECCurve_noName, + "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION }, + OD( ansiX962c2pnb272w1, ECCurve_X9_62_CHAR2_PNB272W1, + "ANSI X9.62 elliptic curve c2pnb272w1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2pnb304w1, ECCurve_X9_62_CHAR2_PNB304W1, + "ANSI X9.62 elliptic curve c2pnb304w1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2tnb359v1, ECCurve_X9_62_CHAR2_TNB359V1, + "ANSI X9.62 elliptic curve c2tnb359v1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2pnb368w1, ECCurve_X9_62_CHAR2_PNB368W1, + "ANSI X9.62 elliptic curve c2pnb368w1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ), + OD( ansiX962c2tnb431r1, ECCurve_X9_62_CHAR2_TNB431R1, + "ANSI X9.62 elliptic curve c2tnb431r1", + CKM_INVALID_MECHANISM, + INVALID_CERT_EXTENSION ) +}; + +SECOidData * +SECOID_FindOID(const SECItem *oid) +{ + SECOidData *po; + SECOidData *ret; + int i; + + if (oid->len == 8) { + if (oid->data[6] == 0x00) { + /* XXX bounds check */ + po = &ANSI_oids[oid->data[7]]; + if (memcmp(oid->data, po->oid.data, 8) == 0) + ret = po; + } + if (oid->data[6] == 0x01) { + /* XXX bounds check */ + po = &ANSI_prime_oids[oid->data[7]]; + if (memcmp(oid->data, po->oid.data, 8) == 0) + ret = po; + } + } else if (oid->len == 5) { + /* XXX bounds check */ + po = &SECG_oids[oid->data[4]]; + if (memcmp(oid->data, po->oid.data, 5) == 0) + ret = po; + } else { + ret = NULL; + } + return(ret); +} + +ECCurveName +SECOID_FindOIDTag(const SECItem *oid) +{ + SECOidData *oiddata; + + oiddata = SECOID_FindOID (oid); + if (oiddata == NULL) + return ECCurve_noName; + + return oiddata->offset; +} diff --git a/usr/src/common/crypto/ecc/secitem.c b/usr/src/common/crypto/ecc/secitem.c new file mode 100644 index 0000000000..e2aed8f409 --- /dev/null +++ b/usr/src/common/crypto/ecc/secitem.c @@ -0,0 +1,176 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Support routines for SECItem data structure. + * + * $Id: secitem.c,v 1.14 2006/05/22 22:24:34 wtchang%redhat.com Exp $ + */ + +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/param.h> +#ifdef _KERNEL +#include <sys/kmem.h> +#else +#include <string.h> +#include <strings.h> +#include <assert.h> +#endif +#include "ec.h" +#include "ecl-curve.h" +#include "ecc_impl.h" + +void SECITEM_FreeItem(SECItem *, PRBool); + +SECItem * +SECITEM_AllocItem(PRArenaPool *arena, SECItem *item, unsigned int len, + int kmflag) +{ + SECItem *result = NULL; + void *mark = NULL; + + if (arena != NULL) { + mark = PORT_ArenaMark(arena); + } + + if (item == NULL) { + if (arena != NULL) { + result = PORT_ArenaZAlloc(arena, sizeof(SECItem), kmflag); + } else { + result = PORT_ZAlloc(sizeof(SECItem), kmflag); + } + if (result == NULL) { + goto loser; + } + } else { + PORT_Assert(item->data == NULL); + result = item; + } + + result->len = len; + if (len) { + if (arena != NULL) { + result->data = PORT_ArenaAlloc(arena, len, kmflag); + } else { + result->data = PORT_Alloc(len, kmflag); + } + if (result->data == NULL) { + goto loser; + } + } else { + result->data = NULL; + } + + if (mark) { + PORT_ArenaUnmark(arena, mark); + } + return(result); + +loser: + if ( arena != NULL ) { + if (mark) { + PORT_ArenaRelease(arena, mark); + } + if (item != NULL) { + item->data = NULL; + item->len = 0; + } + } else { + if (result != NULL) { + SECITEM_FreeItem(result, (item == NULL) ? PR_TRUE : PR_FALSE); + } + /* + * If item is not NULL, the above has set item->data and + * item->len to 0. + */ + } + return(NULL); +} + +SECStatus +SECITEM_CopyItem(PRArenaPool *arena, SECItem *to, const SECItem *from, + int kmflag) +{ + to->type = from->type; + if (from->data && from->len) { + if ( arena ) { + to->data = (unsigned char*) PORT_ArenaAlloc(arena, from->len, + kmflag); + } else { + to->data = (unsigned char*) PORT_Alloc(from->len, kmflag); + } + + if (!to->data) { + return SECFailure; + } + PORT_Memcpy(to->data, from->data, from->len); + to->len = from->len; + } else { + to->data = 0; + to->len = 0; + } + return SECSuccess; +} + +void +SECITEM_FreeItem(SECItem *zap, PRBool freeit) +{ + if (zap) { +#ifdef _KERNEL + kmem_free(zap->data, zap->len); +#else + free(zap->data); +#endif + zap->data = 0; + zap->len = 0; + if (freeit) { +#ifdef _KERNEL + kmem_free(zap, sizeof (SECItem)); +#else + free(zap); +#endif + } + } +} diff --git a/usr/src/common/crypto/ecc/secoidt.h b/usr/src/common/crypto/ecc/secoidt.h new file mode 100644 index 0000000000..a5d65f0a26 --- /dev/null +++ b/usr/src/common/crypto/ecc/secoidt.h @@ -0,0 +1,90 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _SECOIDT_H_ +#define _SECOIDT_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * secoidt.h - public data structures for ASN.1 OID functions + * + * $Id: secoidt.h,v 1.23 2007/05/05 22:45:16 nelson%bolyard.com Exp $ + */ + +typedef struct SECOidDataStr SECOidData; +typedef struct SECAlgorithmIDStr SECAlgorithmID; + +/* +** An X.500 algorithm identifier +*/ +struct SECAlgorithmIDStr { + SECItem algorithm; + SECItem parameters; +}; + +#define SEC_OID_SECG_EC_SECP192R1 SEC_OID_ANSIX962_EC_PRIME192V1 +#define SEC_OID_SECG_EC_SECP256R1 SEC_OID_ANSIX962_EC_PRIME256V1 +#define SEC_OID_PKCS12_KEY_USAGE SEC_OID_X509_KEY_USAGE + +/* fake OID for DSS sign/verify */ +#define SEC_OID_SHA SEC_OID_MISS_DSS + +typedef enum { + INVALID_CERT_EXTENSION = 0, + UNSUPPORTED_CERT_EXTENSION = 1, + SUPPORTED_CERT_EXTENSION = 2 +} SECSupportExtenTag; + +struct SECOidDataStr { + SECItem oid; + ECCurveName offset; + const char * desc; + unsigned long mechanism; + SECSupportExtenTag supportedExtension; + /* only used for x.509 v3 extensions, so + that we can print the names of those + extensions that we don't even support */ +}; + +#endif /* _SECOIDT_H_ */ diff --git a/usr/src/common/mpi/THIRDPARTYLICENSE b/usr/src/common/mpi/THIRDPARTYLICENSE new file mode 100644 index 0000000000..b286702b01 --- /dev/null +++ b/usr/src/common/mpi/THIRDPARTYLICENSE @@ -0,0 +1,34 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ______________________________________________. + * + * The Initial Developer of the Original Code is ______. + * Portions created by ______________________ are Copyright (C) ______ + * _______________________. All Rights Reserved. + * + * Contributor(s): ___________________________________________________. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ diff --git a/usr/src/common/mpi/logtab.h b/usr/src/common/mpi/logtab.h new file mode 100644 index 0000000000..a311a4574f --- /dev/null +++ b/usr/src/common/mpi/logtab.h @@ -0,0 +1,69 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _LOGTAB_H +#define _LOGTAB_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +const float s_logv_2[] = { + 0.000000000f, 0.000000000f, 1.000000000f, 0.630929754f, /* 0 1 2 3 */ + 0.500000000f, 0.430676558f, 0.386852807f, 0.356207187f, /* 4 5 6 7 */ + 0.333333333f, 0.315464877f, 0.301029996f, 0.289064826f, /* 8 9 10 11 */ + 0.278942946f, 0.270238154f, 0.262649535f, 0.255958025f, /* 12 13 14 15 */ + 0.250000000f, 0.244650542f, 0.239812467f, 0.235408913f, /* 16 17 18 19 */ + 0.231378213f, 0.227670249f, 0.224243824f, 0.221064729f, /* 20 21 22 23 */ + 0.218104292f, 0.215338279f, 0.212746054f, 0.210309918f, /* 24 25 26 27 */ + 0.208014598f, 0.205846832f, 0.203795047f, 0.201849087f, /* 28 29 30 31 */ + 0.200000000f, 0.198239863f, 0.196561632f, 0.194959022f, /* 32 33 34 35 */ + 0.193426404f, 0.191958720f, 0.190551412f, 0.189200360f, /* 36 37 38 39 */ + 0.187901825f, 0.186652411f, 0.185449023f, 0.184288833f, /* 40 41 42 43 */ + 0.183169251f, 0.182087900f, 0.181042597f, 0.180031327f, /* 44 45 46 47 */ + 0.179052232f, 0.178103594f, 0.177183820f, 0.176291434f, /* 48 49 50 51 */ + 0.175425064f, 0.174583430f, 0.173765343f, 0.172969690f, /* 52 53 54 55 */ + 0.172195434f, 0.171441601f, 0.170707280f, 0.169991616f, /* 56 57 58 59 */ + 0.169293808f, 0.168613099f, 0.167948779f, 0.167300179f, /* 60 61 62 63 */ + 0.166666667f +}; + +#endif /* _LOGTAB_H */ diff --git a/usr/src/common/mpi/mp_gf2m-priv.h b/usr/src/common/mpi/mp_gf2m-priv.h new file mode 100644 index 0000000000..570e1ea33a --- /dev/null +++ b/usr/src/common/mpi/mp_gf2m-priv.h @@ -0,0 +1,110 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Multi-precision Binary Polynomial Arithmetic Library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang Shantz <sheueling.chang@sun.com> and + * Douglas Stebila <douglas@stebila.ca> of Sun Laboratories. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _MP_GF2M_PRIV_H_ +#define _MP_GF2M_PRIV_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mpi-priv.h" + +extern const mp_digit mp_gf2m_sqr_tb[16]; + +#if defined(MP_USE_UINT_DIGIT) +#define MP_DIGIT_BITS 32 +#else +#define MP_DIGIT_BITS 64 +#endif + +/* Platform-specific macros for fast binary polynomial squaring. */ +#if MP_DIGIT_BITS == 32 +#define gf2m_SQR1(w) \ + mp_gf2m_sqr_tb[(w) >> 28 & 0xF] << 24 | mp_gf2m_sqr_tb[(w) >> 24 & 0xF] << 16 | \ + mp_gf2m_sqr_tb[(w) >> 20 & 0xF] << 8 | mp_gf2m_sqr_tb[(w) >> 16 & 0xF] +#define gf2m_SQR0(w) \ + mp_gf2m_sqr_tb[(w) >> 12 & 0xF] << 24 | mp_gf2m_sqr_tb[(w) >> 8 & 0xF] << 16 | \ + mp_gf2m_sqr_tb[(w) >> 4 & 0xF] << 8 | mp_gf2m_sqr_tb[(w) & 0xF] +#else +#define gf2m_SQR1(w) \ + mp_gf2m_sqr_tb[(w) >> 60 & 0xF] << 56 | mp_gf2m_sqr_tb[(w) >> 56 & 0xF] << 48 | \ + mp_gf2m_sqr_tb[(w) >> 52 & 0xF] << 40 | mp_gf2m_sqr_tb[(w) >> 48 & 0xF] << 32 | \ + mp_gf2m_sqr_tb[(w) >> 44 & 0xF] << 24 | mp_gf2m_sqr_tb[(w) >> 40 & 0xF] << 16 | \ + mp_gf2m_sqr_tb[(w) >> 36 & 0xF] << 8 | mp_gf2m_sqr_tb[(w) >> 32 & 0xF] +#define gf2m_SQR0(w) \ + mp_gf2m_sqr_tb[(w) >> 28 & 0xF] << 56 | mp_gf2m_sqr_tb[(w) >> 24 & 0xF] << 48 | \ + mp_gf2m_sqr_tb[(w) >> 20 & 0xF] << 40 | mp_gf2m_sqr_tb[(w) >> 16 & 0xF] << 32 | \ + mp_gf2m_sqr_tb[(w) >> 12 & 0xF] << 24 | mp_gf2m_sqr_tb[(w) >> 8 & 0xF] << 16 | \ + mp_gf2m_sqr_tb[(w) >> 4 & 0xF] << 8 | mp_gf2m_sqr_tb[(w) & 0xF] +#endif + +/* Multiply two binary polynomials mp_digits a, b. + * Result is a polynomial with degree < 2 * MP_DIGIT_BITS - 1. + * Output in two mp_digits rh, rl. + */ +void s_bmul_1x1(mp_digit *rh, mp_digit *rl, const mp_digit a, const mp_digit b); + +/* Compute xor-multiply of two binary polynomials (a1, a0) x (b1, b0) + * result is a binary polynomial in 4 mp_digits r[4]. + * The caller MUST ensure that r has the right amount of space allocated. + */ +void s_bmul_2x2(mp_digit *r, const mp_digit a1, const mp_digit a0, const mp_digit b1, + const mp_digit b0); + +/* Compute xor-multiply of two binary polynomials (a2, a1, a0) x (b2, b1, b0) + * result is a binary polynomial in 6 mp_digits r[6]. + * The caller MUST ensure that r has the right amount of space allocated. + */ +void s_bmul_3x3(mp_digit *r, const mp_digit a2, const mp_digit a1, const mp_digit a0, + const mp_digit b2, const mp_digit b1, const mp_digit b0); + +/* Compute xor-multiply of two binary polynomials (a3, a2, a1, a0) x (b3, b2, b1, b0) + * result is a binary polynomial in 8 mp_digits r[8]. + * The caller MUST ensure that r has the right amount of space allocated. + */ +void s_bmul_4x4(mp_digit *r, const mp_digit a3, const mp_digit a2, const mp_digit a1, + const mp_digit a0, const mp_digit b3, const mp_digit b2, const mp_digit b1, + const mp_digit b0); + +#endif /* _MP_GF2M_PRIV_H_ */ diff --git a/usr/src/common/mpi/mp_gf2m.c b/usr/src/common/mpi/mp_gf2m.c new file mode 100644 index 0000000000..a0140b24ba --- /dev/null +++ b/usr/src/common/mpi/mp_gf2m.c @@ -0,0 +1,612 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Multi-precision Binary Polynomial Arithmetic Library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang Shantz <sheueling.chang@sun.com> and + * Douglas Stebila <douglas@stebila.ca> of Sun Laboratories. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mp_gf2m.h" +#include "mp_gf2m-priv.h" +#include "mplogic.h" +#include "mpi-priv.h" + +const mp_digit mp_gf2m_sqr_tb[16] = +{ + 0, 1, 4, 5, 16, 17, 20, 21, + 64, 65, 68, 69, 80, 81, 84, 85 +}; + +/* Multiply two binary polynomials mp_digits a, b. + * Result is a polynomial with degree < 2 * MP_DIGIT_BITS - 1. + * Output in two mp_digits rh, rl. + */ +#if MP_DIGIT_BITS == 32 +void +s_bmul_1x1(mp_digit *rh, mp_digit *rl, const mp_digit a, const mp_digit b) +{ + register mp_digit h, l, s; + mp_digit tab[8], top2b = a >> 30; + register mp_digit a1, a2, a4; + + a1 = a & (0x3FFFFFFF); a2 = a1 << 1; a4 = a2 << 1; + + tab[0] = 0; tab[1] = a1; tab[2] = a2; tab[3] = a1^a2; + tab[4] = a4; tab[5] = a1^a4; tab[6] = a2^a4; tab[7] = a1^a2^a4; + + s = tab[b & 0x7]; l = s; + s = tab[b >> 3 & 0x7]; l ^= s << 3; h = s >> 29; + s = tab[b >> 6 & 0x7]; l ^= s << 6; h ^= s >> 26; + s = tab[b >> 9 & 0x7]; l ^= s << 9; h ^= s >> 23; + s = tab[b >> 12 & 0x7]; l ^= s << 12; h ^= s >> 20; + s = tab[b >> 15 & 0x7]; l ^= s << 15; h ^= s >> 17; + s = tab[b >> 18 & 0x7]; l ^= s << 18; h ^= s >> 14; + s = tab[b >> 21 & 0x7]; l ^= s << 21; h ^= s >> 11; + s = tab[b >> 24 & 0x7]; l ^= s << 24; h ^= s >> 8; + s = tab[b >> 27 & 0x7]; l ^= s << 27; h ^= s >> 5; + s = tab[b >> 30 ]; l ^= s << 30; h ^= s >> 2; + + /* compensate for the top two bits of a */ + + if (top2b & 01) { l ^= b << 30; h ^= b >> 2; } + if (top2b & 02) { l ^= b << 31; h ^= b >> 1; } + + *rh = h; *rl = l; +} +#else +void +s_bmul_1x1(mp_digit *rh, mp_digit *rl, const mp_digit a, const mp_digit b) +{ + register mp_digit h, l, s; + mp_digit tab[16], top3b = a >> 61; + register mp_digit a1, a2, a4, a8; + + a1 = a & (0x1FFFFFFFFFFFFFFFULL); a2 = a1 << 1; + a4 = a2 << 1; a8 = a4 << 1; + tab[ 0] = 0; tab[ 1] = a1; tab[ 2] = a2; tab[ 3] = a1^a2; + tab[ 4] = a4; tab[ 5] = a1^a4; tab[ 6] = a2^a4; tab[ 7] = a1^a2^a4; + tab[ 8] = a8; tab[ 9] = a1^a8; tab[10] = a2^a8; tab[11] = a1^a2^a8; + tab[12] = a4^a8; tab[13] = a1^a4^a8; tab[14] = a2^a4^a8; tab[15] = a1^a2^a4^a8; + + s = tab[b & 0xF]; l = s; + s = tab[b >> 4 & 0xF]; l ^= s << 4; h = s >> 60; + s = tab[b >> 8 & 0xF]; l ^= s << 8; h ^= s >> 56; + s = tab[b >> 12 & 0xF]; l ^= s << 12; h ^= s >> 52; + s = tab[b >> 16 & 0xF]; l ^= s << 16; h ^= s >> 48; + s = tab[b >> 20 & 0xF]; l ^= s << 20; h ^= s >> 44; + s = tab[b >> 24 & 0xF]; l ^= s << 24; h ^= s >> 40; + s = tab[b >> 28 & 0xF]; l ^= s << 28; h ^= s >> 36; + s = tab[b >> 32 & 0xF]; l ^= s << 32; h ^= s >> 32; + s = tab[b >> 36 & 0xF]; l ^= s << 36; h ^= s >> 28; + s = tab[b >> 40 & 0xF]; l ^= s << 40; h ^= s >> 24; + s = tab[b >> 44 & 0xF]; l ^= s << 44; h ^= s >> 20; + s = tab[b >> 48 & 0xF]; l ^= s << 48; h ^= s >> 16; + s = tab[b >> 52 & 0xF]; l ^= s << 52; h ^= s >> 12; + s = tab[b >> 56 & 0xF]; l ^= s << 56; h ^= s >> 8; + s = tab[b >> 60 ]; l ^= s << 60; h ^= s >> 4; + + /* compensate for the top three bits of a */ + + if (top3b & 01) { l ^= b << 61; h ^= b >> 3; } + if (top3b & 02) { l ^= b << 62; h ^= b >> 2; } + if (top3b & 04) { l ^= b << 63; h ^= b >> 1; } + + *rh = h; *rl = l; +} +#endif + +/* Compute xor-multiply of two binary polynomials (a1, a0) x (b1, b0) + * result is a binary polynomial in 4 mp_digits r[4]. + * The caller MUST ensure that r has the right amount of space allocated. + */ +void +s_bmul_2x2(mp_digit *r, const mp_digit a1, const mp_digit a0, const mp_digit b1, + const mp_digit b0) +{ + mp_digit m1, m0; + /* r[3] = h1, r[2] = h0; r[1] = l1; r[0] = l0 */ + s_bmul_1x1(r+3, r+2, a1, b1); + s_bmul_1x1(r+1, r, a0, b0); + s_bmul_1x1(&m1, &m0, a0 ^ a1, b0 ^ b1); + /* Correction on m1 ^= l1 ^ h1; m0 ^= l0 ^ h0; */ + r[2] ^= m1 ^ r[1] ^ r[3]; /* h0 ^= m1 ^ l1 ^ h1; */ + r[1] = r[3] ^ r[2] ^ r[0] ^ m1 ^ m0; /* l1 ^= l0 ^ h0 ^ m0; */ +} + +/* Compute xor-multiply of two binary polynomials (a2, a1, a0) x (b2, b1, b0) + * result is a binary polynomial in 6 mp_digits r[6]. + * The caller MUST ensure that r has the right amount of space allocated. + */ +void +s_bmul_3x3(mp_digit *r, const mp_digit a2, const mp_digit a1, const mp_digit a0, + const mp_digit b2, const mp_digit b1, const mp_digit b0) +{ + mp_digit zm[4]; + + s_bmul_1x1(r+5, r+4, a2, b2); /* fill top 2 words */ + s_bmul_2x2(zm, a1, a2^a0, b1, b2^b0); /* fill middle 4 words */ + s_bmul_2x2(r, a1, a0, b1, b0); /* fill bottom 4 words */ + + zm[3] ^= r[3]; + zm[2] ^= r[2]; + zm[1] ^= r[1] ^ r[5]; + zm[0] ^= r[0] ^ r[4]; + + r[5] ^= zm[3]; + r[4] ^= zm[2]; + r[3] ^= zm[1]; + r[2] ^= zm[0]; +} + +/* Compute xor-multiply of two binary polynomials (a3, a2, a1, a0) x (b3, b2, b1, b0) + * result is a binary polynomial in 8 mp_digits r[8]. + * The caller MUST ensure that r has the right amount of space allocated. + */ +void s_bmul_4x4(mp_digit *r, const mp_digit a3, const mp_digit a2, const mp_digit a1, + const mp_digit a0, const mp_digit b3, const mp_digit b2, const mp_digit b1, + const mp_digit b0) +{ + mp_digit zm[4]; + + s_bmul_2x2(r+4, a3, a2, b3, b2); /* fill top 4 words */ + s_bmul_2x2(zm, a3^a1, a2^a0, b3^b1, b2^b0); /* fill middle 4 words */ + s_bmul_2x2(r, a1, a0, b1, b0); /* fill bottom 4 words */ + + zm[3] ^= r[3] ^ r[7]; + zm[2] ^= r[2] ^ r[6]; + zm[1] ^= r[1] ^ r[5]; + zm[0] ^= r[0] ^ r[4]; + + r[5] ^= zm[3]; + r[4] ^= zm[2]; + r[3] ^= zm[1]; + r[2] ^= zm[0]; +} + +/* Compute addition of two binary polynomials a and b, + * store result in c; c could be a or b, a and b could be equal; + * c is the bitwise XOR of a and b. + */ +mp_err +mp_badd(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_digit *pa, *pb, *pc; + mp_size ix; + mp_size used_pa, used_pb; + mp_err res = MP_OKAY; + + /* Add all digits up to the precision of b. If b had more + * precision than a initially, swap a, b first + */ + if (MP_USED(a) >= MP_USED(b)) { + pa = MP_DIGITS(a); + pb = MP_DIGITS(b); + used_pa = MP_USED(a); + used_pb = MP_USED(b); + } else { + pa = MP_DIGITS(b); + pb = MP_DIGITS(a); + used_pa = MP_USED(b); + used_pb = MP_USED(a); + } + + /* Make sure c has enough precision for the output value */ + MP_CHECKOK( s_mp_pad(c, used_pa) ); + + /* Do word-by-word xor */ + pc = MP_DIGITS(c); + for (ix = 0; ix < used_pb; ix++) { + (*pc++) = (*pa++) ^ (*pb++); + } + + /* Finish the rest of digits until we're actually done */ + for (; ix < used_pa; ++ix) { + *pc++ = *pa++; + } + + MP_USED(c) = used_pa; + MP_SIGN(c) = ZPOS; + s_mp_clamp(c); + +CLEANUP: + return res; +} + +#define s_mp_div2(a) MP_CHECKOK( mpl_rsh((a), (a), 1) ); + +/* Compute binary polynomial multiply d = a * b */ +static void +s_bmul_d(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *d) +{ + mp_digit a_i, a0b0, a1b1, carry = 0; + while (a_len--) { + a_i = *a++; + s_bmul_1x1(&a1b1, &a0b0, a_i, b); + *d++ = a0b0 ^ carry; + carry = a1b1; + } + *d = carry; +} + +/* Compute binary polynomial xor multiply accumulate d ^= a * b */ +static void +s_bmul_d_add(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *d) +{ + mp_digit a_i, a0b0, a1b1, carry = 0; + while (a_len--) { + a_i = *a++; + s_bmul_1x1(&a1b1, &a0b0, a_i, b); + *d++ ^= a0b0 ^ carry; + carry = a1b1; + } + *d ^= carry; +} + +/* Compute binary polynomial xor multiply c = a * b. + * All parameters may be identical. + */ +mp_err +mp_bmul(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_digit *pb, b_i; + mp_int tmp; + mp_size ib, a_used, b_used; + mp_err res = MP_OKAY; + + MP_DIGITS(&tmp) = 0; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if (a == c) { + MP_CHECKOK( mp_init_copy(&tmp, a) ); + if (a == b) + b = &tmp; + a = &tmp; + } else if (b == c) { + MP_CHECKOK( mp_init_copy(&tmp, b) ); + b = &tmp; + } + + if (MP_USED(a) < MP_USED(b)) { + const mp_int *xch = b; /* switch a and b if b longer */ + b = a; + a = xch; + } + + MP_USED(c) = 1; MP_DIGIT(c, 0) = 0; + MP_CHECKOK( s_mp_pad(c, USED(a) + USED(b)) ); + + pb = MP_DIGITS(b); + s_bmul_d(MP_DIGITS(a), MP_USED(a), *pb++, MP_DIGITS(c)); + + /* Outer loop: Digits of b */ + a_used = MP_USED(a); + b_used = MP_USED(b); + MP_USED(c) = a_used + b_used; + for (ib = 1; ib < b_used; ib++) { + b_i = *pb++; + + /* Inner product: Digits of a */ + if (b_i) + s_bmul_d_add(MP_DIGITS(a), a_used, b_i, MP_DIGITS(c) + ib); + else + MP_DIGIT(c, ib + a_used) = b_i; + } + + s_mp_clamp(c); + + SIGN(c) = ZPOS; + +CLEANUP: + mp_clear(&tmp); + return res; +} + + +/* Compute modular reduction of a and store result in r. + * r could be a. + * For modular arithmetic, the irreducible polynomial f(t) is represented + * as an array of int[], where f(t) is of the form: + * f(t) = t^p[0] + t^p[1] + ... + t^p[k] + * where m = p[0] > p[1] > ... > p[k] = 0. + */ +mp_err +mp_bmod(const mp_int *a, const unsigned int p[], mp_int *r) +{ + int j, k; + int n, dN, d0, d1; + mp_digit zz, *z, tmp; + mp_size used; + mp_err res = MP_OKAY; + + /* The algorithm does the reduction in place in r, + * if a != r, copy a into r first so reduction can be done in r + */ + if (a != r) { + MP_CHECKOK( mp_copy(a, r) ); + } + z = MP_DIGITS(r); + + /* start reduction */ + dN = p[0] / MP_DIGIT_BITS; + used = MP_USED(r); + + for (j = used - 1; j > dN;) { + + zz = z[j]; + if (zz == 0) { + j--; continue; + } + z[j] = 0; + + for (k = 1; p[k] > 0; k++) { + /* reducing component t^p[k] */ + n = p[0] - p[k]; + d0 = n % MP_DIGIT_BITS; + d1 = MP_DIGIT_BITS - d0; + n /= MP_DIGIT_BITS; + z[j-n] ^= (zz>>d0); + if (d0) + z[j-n-1] ^= (zz<<d1); + } + + /* reducing component t^0 */ + n = dN; + d0 = p[0] % MP_DIGIT_BITS; + d1 = MP_DIGIT_BITS - d0; + z[j-n] ^= (zz >> d0); + if (d0) + z[j-n-1] ^= (zz << d1); + + } + + /* final round of reduction */ + while (j == dN) { + + d0 = p[0] % MP_DIGIT_BITS; + zz = z[dN] >> d0; + if (zz == 0) break; + d1 = MP_DIGIT_BITS - d0; + + /* clear up the top d1 bits */ + if (d0) z[dN] = (z[dN] << d1) >> d1; + *z ^= zz; /* reduction t^0 component */ + + for (k = 1; p[k] > 0; k++) { + /* reducing component t^p[k]*/ + n = p[k] / MP_DIGIT_BITS; + d0 = p[k] % MP_DIGIT_BITS; + d1 = MP_DIGIT_BITS - d0; + z[n] ^= (zz << d0); + tmp = zz >> d1; + if (d0 && tmp) + z[n+1] ^= tmp; + } + } + + s_mp_clamp(r); +CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p, + * Store the result in r. r could be a or b; a could be b. + */ +mp_err +mp_bmulmod(const mp_int *a, const mp_int *b, const unsigned int p[], mp_int *r) +{ + mp_err res; + + if (a == b) return mp_bsqrmod(a, p, r); + if ((res = mp_bmul(a, b, r) ) != MP_OKAY) + return res; + return mp_bmod(r, p, r); +} + +/* Compute binary polynomial squaring c = a*a mod p . + * Parameter r and a can be identical. + */ + +mp_err +mp_bsqrmod(const mp_int *a, const unsigned int p[], mp_int *r) +{ + mp_digit *pa, *pr, a_i; + mp_int tmp; + mp_size ia, a_used; + mp_err res; + + ARGCHK(a != NULL && r != NULL, MP_BADARG); + MP_DIGITS(&tmp) = 0; + + if (a == r) { + MP_CHECKOK( mp_init_copy(&tmp, a) ); + a = &tmp; + } + + MP_USED(r) = 1; MP_DIGIT(r, 0) = 0; + MP_CHECKOK( s_mp_pad(r, 2*USED(a)) ); + + pa = MP_DIGITS(a); + pr = MP_DIGITS(r); + a_used = MP_USED(a); + MP_USED(r) = 2 * a_used; + + for (ia = 0; ia < a_used; ia++) { + a_i = *pa++; + *pr++ = gf2m_SQR0(a_i); + *pr++ = gf2m_SQR1(a_i); + } + + MP_CHECKOK( mp_bmod(r, p, r) ); + s_mp_clamp(r); + SIGN(r) = ZPOS; + +CLEANUP: + mp_clear(&tmp); + return res; +} + +/* Compute binary polynomial y/x mod p, y divided by x, reduce modulo p. + * Store the result in r. r could be x or y, and x could equal y. + * Uses algorithm Modular_Division_GF(2^m) from + * Chang-Shantz, S. "From Euclid's GCD to Montgomery Multiplication to + * the Great Divide". + */ +int +mp_bdivmod(const mp_int *y, const mp_int *x, const mp_int *pp, + const unsigned int p[], mp_int *r) +{ + mp_int aa, bb, uu; + mp_int *a, *b, *u, *v; + mp_err res = MP_OKAY; + + MP_DIGITS(&aa) = 0; + MP_DIGITS(&bb) = 0; + MP_DIGITS(&uu) = 0; + + MP_CHECKOK( mp_init_copy(&aa, x) ); + MP_CHECKOK( mp_init_copy(&uu, y) ); + MP_CHECKOK( mp_init_copy(&bb, pp) ); + MP_CHECKOK( s_mp_pad(r, USED(pp)) ); + MP_USED(r) = 1; MP_DIGIT(r, 0) = 0; + + a = &aa; b= &bb; u=&uu; v=r; + /* reduce x and y mod p */ + MP_CHECKOK( mp_bmod(a, p, a) ); + MP_CHECKOK( mp_bmod(u, p, u) ); + + while (!mp_isodd(a)) { + s_mp_div2(a); + if (mp_isodd(u)) { + MP_CHECKOK( mp_badd(u, pp, u) ); + } + s_mp_div2(u); + } + + do { + if (mp_cmp_mag(b, a) > 0) { + MP_CHECKOK( mp_badd(b, a, b) ); + MP_CHECKOK( mp_badd(v, u, v) ); + do { + s_mp_div2(b); + if (mp_isodd(v)) { + MP_CHECKOK( mp_badd(v, pp, v) ); + } + s_mp_div2(v); + } while (!mp_isodd(b)); + } + else if ((MP_DIGIT(a,0) == 1) && (MP_USED(a) == 1)) + break; + else { + MP_CHECKOK( mp_badd(a, b, a) ); + MP_CHECKOK( mp_badd(u, v, u) ); + do { + s_mp_div2(a); + if (mp_isodd(u)) { + MP_CHECKOK( mp_badd(u, pp, u) ); + } + s_mp_div2(u); + } while (!mp_isodd(a)); + } + } while (1); + + MP_CHECKOK( mp_copy(u, r) ); + +CLEANUP: + /* XXX this appears to be a memory leak in the NSS code */ + mp_clear(&aa); + mp_clear(&bb); + mp_clear(&uu); + return res; + +} + +/* Convert the bit-string representation of a polynomial a into an array + * of integers corresponding to the bits with non-zero coefficient. + * Up to max elements of the array will be filled. Return value is total + * number of coefficients that would be extracted if array was large enough. + */ +int +mp_bpoly2arr(const mp_int *a, unsigned int p[], int max) +{ + int i, j, k; + mp_digit top_bit, mask; + + top_bit = 1; + top_bit <<= MP_DIGIT_BIT - 1; + + for (k = 0; k < max; k++) p[k] = 0; + k = 0; + + for (i = MP_USED(a) - 1; i >= 0; i--) { + mask = top_bit; + for (j = MP_DIGIT_BIT - 1; j >= 0; j--) { + if (MP_DIGITS(a)[i] & mask) { + if (k < max) p[k] = MP_DIGIT_BIT * i + j; + k++; + } + mask >>= 1; + } + } + + return k; +} + +/* Convert the coefficient array representation of a polynomial to a + * bit-string. The array must be terminated by 0. + */ +mp_err +mp_barr2poly(const unsigned int p[], mp_int *a) +{ + + mp_err res = MP_OKAY; + int i; + + mp_zero(a); + for (i = 0; p[i] > 0; i++) { + MP_CHECKOK( mpl_set_bit(a, p[i], 1) ); + } + MP_CHECKOK( mpl_set_bit(a, 0, 1) ); + +CLEANUP: + return res; +} diff --git a/usr/src/common/mpi/mp_gf2m.h b/usr/src/common/mpi/mp_gf2m.h new file mode 100644 index 0000000000..ede370a923 --- /dev/null +++ b/usr/src/common/mpi/mp_gf2m.h @@ -0,0 +1,71 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Multi-precision Binary Polynomial Arithmetic Library. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang Shantz <sheueling.chang@sun.com> and + * Douglas Stebila <douglas@stebila.ca> of Sun Laboratories. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _MP_GF2M_H_ +#define _MP_GF2M_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mpi.h" + +mp_err mp_badd(const mp_int *a, const mp_int *b, mp_int *c); +mp_err mp_bmul(const mp_int *a, const mp_int *b, mp_int *c); + +/* For modular arithmetic, the irreducible polynomial f(t) is represented + * as an array of int[], where f(t) is of the form: + * f(t) = t^p[0] + t^p[1] + ... + t^p[k] + * where m = p[0] > p[1] > ... > p[k] = 0. + */ +mp_err mp_bmod(const mp_int *a, const unsigned int p[], mp_int *r); +mp_err mp_bmulmod(const mp_int *a, const mp_int *b, const unsigned int p[], + mp_int *r); +mp_err mp_bsqrmod(const mp_int *a, const unsigned int p[], mp_int *r); +mp_err mp_bdivmod(const mp_int *y, const mp_int *x, const mp_int *pp, + const unsigned int p[], mp_int *r); + +int mp_bpoly2arr(const mp_int *a, unsigned int p[], int max); +mp_err mp_barr2poly(const unsigned int p[], mp_int *a); + +#endif /* _MP_GF2M_H_ */ diff --git a/usr/src/common/mpi/mpcpucache.c b/usr/src/common/mpi/mpcpucache.c new file mode 100644 index 0000000000..7df7e289bc --- /dev/null +++ b/usr/src/common/mpi/mpcpucache.c @@ -0,0 +1,826 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Red Hat, Inc + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert Relyea <rrelyea@redhat.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mpi.h" + +/* + * This file implements a single function: s_mpi_getProcessorLineSize(); + * s_mpi_getProcessorLineSize() returns the size in bytes of the cache line + * if a cache exists, or zero if there is no cache. If more than one + * cache line exists, it should return the smallest line size (which is + * usually the L1 cache). + * + * mp_modexp uses this information to make sure that private key information + * isn't being leaked through the cache. + * + * Currently the file returns good data for most modern x86 processors, and + * reasonable data on 64-bit ppc processors. All other processors are assumed + * to have a cache line size of 32 bytes unless modified by target.mk. + * + */ + +#if defined(__GNUC__) +#if defined(i386) || defined(__i386) || defined(__X86__) || defined (_M_IX86) || defined(__x86_64__) || defined(__x86_64) +/* X86 processors have special instructions that tell us about the cache */ +#ifndef _KERNEL +#include "string.h" +#endif + +#if defined(__x86_64__) || defined(__x86_64) +#define AMD_64 1 +#endif + +/* Generic CPUID function */ +#if defined(AMD_64) +static void cpuid(unsigned long op, unsigned long *eax, + unsigned long *ebx, unsigned long *ecx, + unsigned long *edx) +{ + __asm__("cpuid\n\t" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "0" (op)); +} +#elif !defined(_MSC_VER) +static void cpuid(unsigned long op, unsigned long *eax, + unsigned long *ebx, unsigned long *ecx, + unsigned long *edx) +{ +/* sigh GCC isn't smart enough to save the ebx PIC register on it's own + * in this case, so do it by hand. */ + __asm__("pushl %%ebx\n\t" + "cpuid\n\t" + "mov %%ebx,%1\n\t" + "popl %%ebx\n\t" + : "=a" (*eax), + "=r" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "0" (op)); +} + +/* + * try flipping a processor flag to determine CPU type + */ +static unsigned long changeFlag(unsigned long flag) +{ + unsigned long changedFlags, originalFlags; + __asm__("pushfl\n\t" /* get the flags */ + "popl %0\n\t" + "movl %0,%1\n\t" /* save the original flags */ + "xorl %2,%0\n\t" /* flip the bit */ + "pushl %0\n\t" /* set the flags */ + "popfl\n\t" + "pushfl\n\t" /* get the flags again (for return) */ + "popl %0\n\t" + "pushl %1\n\t" /* restore the original flags */ + "popfl\n\t" + : "=r" (changedFlags), + "=r" (originalFlags), + "=r" (flag) + : "2" (flag)); + return changedFlags ^ originalFlags; +} + +#else + +/* + * windows versions of the above assembler + */ +#define wcpuid __asm __emit 0fh __asm __emit 0a2h +static void cpuid(unsigned long op, unsigned long *Reax, + unsigned long *Rebx, unsigned long *Recx, unsigned long *Redx) +{ + unsigned long Leax, Lebx, Lecx, Ledx; + __asm { + pushad + mov eax,op + wcpuid + mov Leax,eax + mov Lebx,ebx + mov Lecx,ecx + mov Ledx,edx + popad + } + *Reax = Leax; + *Rebx = Lebx; + *Recx = Lecx; + *Redx = Ledx; +} + +static unsigned long changeFlag(unsigned long flag) +{ + unsigned long changedFlags, originalFlags; + __asm { + push eax + push ebx + pushfd /* get the flags */ + pop eax + push eax /* save the flags on the stack */ + mov originalFlags,eax /* save the original flags */ + mov ebx,flag + xor eax,ebx /* flip the bit */ + push eax /* set the flags */ + popfd + pushfd /* get the flags again (for return) */ + pop eax + popfd /* restore the original flags */ + mov changedFlags,eax + pop ebx + pop eax + } + return changedFlags ^ originalFlags; +} +#endif +#endif /* __GNUC__ */ + +#if !defined(AMD_64) +#define AC_FLAG 0x40000 +#define ID_FLAG 0x200000 + +/* 386 processors can't flip the AC_FLAG, intel AP Note AP-485 */ +static int is386() +{ + return changeFlag(AC_FLAG) == 0; +} + +/* 486 processors can't flip the ID_FLAG, intel AP Note AP-485 */ +static int is486() +{ + return changeFlag(ID_FLAG) == 0; +} +#endif + + +/* + * table for Intel Cache. + * See Intel Application Note AP-485 for more information + */ + +typedef unsigned char CacheTypeEntry; + +typedef enum { + Cache_NONE = 0, + Cache_UNKNOWN = 1, + Cache_TLB = 2, + Cache_TLBi = 3, + Cache_TLBd = 4, + Cache_Trace = 5, + Cache_L1 = 6, + Cache_L1i = 7, + Cache_L1d = 8, + Cache_L2 = 9 , + Cache_L2i = 10 , + Cache_L2d = 11 , + Cache_L3 = 12 , + Cache_L3i = 13, + Cache_L3d = 14 +} CacheType; + +struct _cache { + CacheTypeEntry type; + unsigned char lineSize; +}; +static const struct _cache CacheMap[256] = { +/* 00 */ {Cache_NONE, 0 }, +/* 01 */ {Cache_TLBi, 0 }, +/* 02 */ {Cache_TLBi, 0 }, +/* 03 */ {Cache_TLBd, 0 }, +/* 04 */ {Cache_TLBd, }, +/* 05 */ {Cache_UNKNOWN, 0 }, +/* 06 */ {Cache_L1i, 32 }, +/* 07 */ {Cache_UNKNOWN, 0 }, +/* 08 */ {Cache_L1i, 32 }, +/* 09 */ {Cache_UNKNOWN, 0 }, +/* 0a */ {Cache_L1d, 32 }, +/* 0b */ {Cache_UNKNOWN, 0 }, +/* 0c */ {Cache_L1d, 32 }, +/* 0d */ {Cache_UNKNOWN, 0 }, +/* 0e */ {Cache_UNKNOWN, 0 }, +/* 0f */ {Cache_UNKNOWN, 0 }, +/* 10 */ {Cache_UNKNOWN, 0 }, +/* 11 */ {Cache_UNKNOWN, 0 }, +/* 12 */ {Cache_UNKNOWN, 0 }, +/* 13 */ {Cache_UNKNOWN, 0 }, +/* 14 */ {Cache_UNKNOWN, 0 }, +/* 15 */ {Cache_UNKNOWN, 0 }, +/* 16 */ {Cache_UNKNOWN, 0 }, +/* 17 */ {Cache_UNKNOWN, 0 }, +/* 18 */ {Cache_UNKNOWN, 0 }, +/* 19 */ {Cache_UNKNOWN, 0 }, +/* 1a */ {Cache_UNKNOWN, 0 }, +/* 1b */ {Cache_UNKNOWN, 0 }, +/* 1c */ {Cache_UNKNOWN, 0 }, +/* 1d */ {Cache_UNKNOWN, 0 }, +/* 1e */ {Cache_UNKNOWN, 0 }, +/* 1f */ {Cache_UNKNOWN, 0 }, +/* 20 */ {Cache_UNKNOWN, 0 }, +/* 21 */ {Cache_UNKNOWN, 0 }, +/* 22 */ {Cache_L3, 64 }, +/* 23 */ {Cache_L3, 64 }, +/* 24 */ {Cache_UNKNOWN, 0 }, +/* 25 */ {Cache_L3, 64 }, +/* 26 */ {Cache_UNKNOWN, 0 }, +/* 27 */ {Cache_UNKNOWN, 0 }, +/* 28 */ {Cache_UNKNOWN, 0 }, +/* 29 */ {Cache_L3, 64 }, +/* 2a */ {Cache_UNKNOWN, 0 }, +/* 2b */ {Cache_UNKNOWN, 0 }, +/* 2c */ {Cache_L1d, 64 }, +/* 2d */ {Cache_UNKNOWN, 0 }, +/* 2e */ {Cache_UNKNOWN, 0 }, +/* 2f */ {Cache_UNKNOWN, 0 }, +/* 30 */ {Cache_L1i, 64 }, +/* 31 */ {Cache_UNKNOWN, 0 }, +/* 32 */ {Cache_UNKNOWN, 0 }, +/* 33 */ {Cache_UNKNOWN, 0 }, +/* 34 */ {Cache_UNKNOWN, 0 }, +/* 35 */ {Cache_UNKNOWN, 0 }, +/* 36 */ {Cache_UNKNOWN, 0 }, +/* 37 */ {Cache_UNKNOWN, 0 }, +/* 38 */ {Cache_UNKNOWN, 0 }, +/* 39 */ {Cache_L2, 64 }, +/* 3a */ {Cache_UNKNOWN, 0 }, +/* 3b */ {Cache_L2, 64 }, +/* 3c */ {Cache_L2, 64 }, +/* 3d */ {Cache_UNKNOWN, 0 }, +/* 3e */ {Cache_UNKNOWN, 0 }, +/* 3f */ {Cache_UNKNOWN, 0 }, +/* 40 */ {Cache_L2, 0 }, +/* 41 */ {Cache_L2, 32 }, +/* 42 */ {Cache_L2, 32 }, +/* 43 */ {Cache_L2, 32 }, +/* 44 */ {Cache_L2, 32 }, +/* 45 */ {Cache_L2, 32 }, +/* 46 */ {Cache_UNKNOWN, 0 }, +/* 47 */ {Cache_UNKNOWN, 0 }, +/* 48 */ {Cache_UNKNOWN, 0 }, +/* 49 */ {Cache_UNKNOWN, 0 }, +/* 4a */ {Cache_UNKNOWN, 0 }, +/* 4b */ {Cache_UNKNOWN, 0 }, +/* 4c */ {Cache_UNKNOWN, 0 }, +/* 4d */ {Cache_UNKNOWN, 0 }, +/* 4e */ {Cache_UNKNOWN, 0 }, +/* 4f */ {Cache_UNKNOWN, 0 }, +/* 50 */ {Cache_TLBi, 0 }, +/* 51 */ {Cache_TLBi, 0 }, +/* 52 */ {Cache_TLBi, 0 }, +/* 53 */ {Cache_UNKNOWN, 0 }, +/* 54 */ {Cache_UNKNOWN, 0 }, +/* 55 */ {Cache_UNKNOWN, 0 }, +/* 56 */ {Cache_UNKNOWN, 0 }, +/* 57 */ {Cache_UNKNOWN, 0 }, +/* 58 */ {Cache_UNKNOWN, 0 }, +/* 59 */ {Cache_UNKNOWN, 0 }, +/* 5a */ {Cache_UNKNOWN, 0 }, +/* 5b */ {Cache_TLBd, 0 }, +/* 5c */ {Cache_TLBd, 0 }, +/* 5d */ {Cache_TLBd, 0 }, +/* 5e */ {Cache_UNKNOWN, 0 }, +/* 5f */ {Cache_UNKNOWN, 0 }, +/* 60 */ {Cache_UNKNOWN, 0 }, +/* 61 */ {Cache_UNKNOWN, 0 }, +/* 62 */ {Cache_UNKNOWN, 0 }, +/* 63 */ {Cache_UNKNOWN, 0 }, +/* 64 */ {Cache_UNKNOWN, 0 }, +/* 65 */ {Cache_UNKNOWN, 0 }, +/* 66 */ {Cache_L1d, 64 }, +/* 67 */ {Cache_L1d, 64 }, +/* 68 */ {Cache_L1d, 64 }, +/* 69 */ {Cache_UNKNOWN, 0 }, +/* 6a */ {Cache_UNKNOWN, 0 }, +/* 6b */ {Cache_UNKNOWN, 0 }, +/* 6c */ {Cache_UNKNOWN, 0 }, +/* 6d */ {Cache_UNKNOWN, 0 }, +/* 6e */ {Cache_UNKNOWN, 0 }, +/* 6f */ {Cache_UNKNOWN, 0 }, +/* 70 */ {Cache_Trace, 1 }, +/* 71 */ {Cache_Trace, 1 }, +/* 72 */ {Cache_Trace, 1 }, +/* 73 */ {Cache_UNKNOWN, 0 }, +/* 74 */ {Cache_UNKNOWN, 0 }, +/* 75 */ {Cache_UNKNOWN, 0 }, +/* 76 */ {Cache_UNKNOWN, 0 }, +/* 77 */ {Cache_UNKNOWN, 0 }, +/* 78 */ {Cache_UNKNOWN, 0 }, +/* 79 */ {Cache_L2, 64 }, +/* 7a */ {Cache_L2, 64 }, +/* 7b */ {Cache_L2, 64 }, +/* 7c */ {Cache_L2, 64 }, +/* 7d */ {Cache_UNKNOWN, 0 }, +/* 7e */ {Cache_UNKNOWN, 0 }, +/* 7f */ {Cache_UNKNOWN, 0 }, +/* 80 */ {Cache_UNKNOWN, 0 }, +/* 81 */ {Cache_UNKNOWN, 0 }, +/* 82 */ {Cache_L2, 32 }, +/* 83 */ {Cache_L2, 32 }, +/* 84 */ {Cache_L2, 32 }, +/* 85 */ {Cache_L2, 32 }, +/* 86 */ {Cache_L2, 64 }, +/* 87 */ {Cache_L2, 64 }, +/* 88 */ {Cache_UNKNOWN, 0 }, +/* 89 */ {Cache_UNKNOWN, 0 }, +/* 8a */ {Cache_UNKNOWN, 0 }, +/* 8b */ {Cache_UNKNOWN, 0 }, +/* 8c */ {Cache_UNKNOWN, 0 }, +/* 8d */ {Cache_UNKNOWN, 0 }, +/* 8e */ {Cache_UNKNOWN, 0 }, +/* 8f */ {Cache_UNKNOWN, 0 }, +/* 90 */ {Cache_UNKNOWN, 0 }, +/* 91 */ {Cache_UNKNOWN, 0 }, +/* 92 */ {Cache_UNKNOWN, 0 }, +/* 93 */ {Cache_UNKNOWN, 0 }, +/* 94 */ {Cache_UNKNOWN, 0 }, +/* 95 */ {Cache_UNKNOWN, 0 }, +/* 96 */ {Cache_UNKNOWN, 0 }, +/* 97 */ {Cache_UNKNOWN, 0 }, +/* 98 */ {Cache_UNKNOWN, 0 }, +/* 99 */ {Cache_UNKNOWN, 0 }, +/* 9a */ {Cache_UNKNOWN, 0 }, +/* 9b */ {Cache_UNKNOWN, 0 }, +/* 9c */ {Cache_UNKNOWN, 0 }, +/* 9d */ {Cache_UNKNOWN, 0 }, +/* 9e */ {Cache_UNKNOWN, 0 }, +/* 9f */ {Cache_UNKNOWN, 0 }, +/* a0 */ {Cache_UNKNOWN, 0 }, +/* a1 */ {Cache_UNKNOWN, 0 }, +/* a2 */ {Cache_UNKNOWN, 0 }, +/* a3 */ {Cache_UNKNOWN, 0 }, +/* a4 */ {Cache_UNKNOWN, 0 }, +/* a5 */ {Cache_UNKNOWN, 0 }, +/* a6 */ {Cache_UNKNOWN, 0 }, +/* a7 */ {Cache_UNKNOWN, 0 }, +/* a8 */ {Cache_UNKNOWN, 0 }, +/* a9 */ {Cache_UNKNOWN, 0 }, +/* aa */ {Cache_UNKNOWN, 0 }, +/* ab */ {Cache_UNKNOWN, 0 }, +/* ac */ {Cache_UNKNOWN, 0 }, +/* ad */ {Cache_UNKNOWN, 0 }, +/* ae */ {Cache_UNKNOWN, 0 }, +/* af */ {Cache_UNKNOWN, 0 }, +/* b0 */ {Cache_TLBi, 0 }, +/* b1 */ {Cache_UNKNOWN, 0 }, +/* b2 */ {Cache_UNKNOWN, 0 }, +/* b3 */ {Cache_TLBd, 0 }, +/* b4 */ {Cache_UNKNOWN, 0 }, +/* b5 */ {Cache_UNKNOWN, 0 }, +/* b6 */ {Cache_UNKNOWN, 0 }, +/* b7 */ {Cache_UNKNOWN, 0 }, +/* b8 */ {Cache_UNKNOWN, 0 }, +/* b9 */ {Cache_UNKNOWN, 0 }, +/* ba */ {Cache_UNKNOWN, 0 }, +/* bb */ {Cache_UNKNOWN, 0 }, +/* bc */ {Cache_UNKNOWN, 0 }, +/* bd */ {Cache_UNKNOWN, 0 }, +/* be */ {Cache_UNKNOWN, 0 }, +/* bf */ {Cache_UNKNOWN, 0 }, +/* c0 */ {Cache_UNKNOWN, 0 }, +/* c1 */ {Cache_UNKNOWN, 0 }, +/* c2 */ {Cache_UNKNOWN, 0 }, +/* c3 */ {Cache_UNKNOWN, 0 }, +/* c4 */ {Cache_UNKNOWN, 0 }, +/* c5 */ {Cache_UNKNOWN, 0 }, +/* c6 */ {Cache_UNKNOWN, 0 }, +/* c7 */ {Cache_UNKNOWN, 0 }, +/* c8 */ {Cache_UNKNOWN, 0 }, +/* c9 */ {Cache_UNKNOWN, 0 }, +/* ca */ {Cache_UNKNOWN, 0 }, +/* cb */ {Cache_UNKNOWN, 0 }, +/* cc */ {Cache_UNKNOWN, 0 }, +/* cd */ {Cache_UNKNOWN, 0 }, +/* ce */ {Cache_UNKNOWN, 0 }, +/* cf */ {Cache_UNKNOWN, 0 }, +/* d0 */ {Cache_UNKNOWN, 0 }, +/* d1 */ {Cache_UNKNOWN, 0 }, +/* d2 */ {Cache_UNKNOWN, 0 }, +/* d3 */ {Cache_UNKNOWN, 0 }, +/* d4 */ {Cache_UNKNOWN, 0 }, +/* d5 */ {Cache_UNKNOWN, 0 }, +/* d6 */ {Cache_UNKNOWN, 0 }, +/* d7 */ {Cache_UNKNOWN, 0 }, +/* d8 */ {Cache_UNKNOWN, 0 }, +/* d9 */ {Cache_UNKNOWN, 0 }, +/* da */ {Cache_UNKNOWN, 0 }, +/* db */ {Cache_UNKNOWN, 0 }, +/* dc */ {Cache_UNKNOWN, 0 }, +/* dd */ {Cache_UNKNOWN, 0 }, +/* de */ {Cache_UNKNOWN, 0 }, +/* df */ {Cache_UNKNOWN, 0 }, +/* e0 */ {Cache_UNKNOWN, 0 }, +/* e1 */ {Cache_UNKNOWN, 0 }, +/* e2 */ {Cache_UNKNOWN, 0 }, +/* e3 */ {Cache_UNKNOWN, 0 }, +/* e4 */ {Cache_UNKNOWN, 0 }, +/* e5 */ {Cache_UNKNOWN, 0 }, +/* e6 */ {Cache_UNKNOWN, 0 }, +/* e7 */ {Cache_UNKNOWN, 0 }, +/* e8 */ {Cache_UNKNOWN, 0 }, +/* e9 */ {Cache_UNKNOWN, 0 }, +/* ea */ {Cache_UNKNOWN, 0 }, +/* eb */ {Cache_UNKNOWN, 0 }, +/* ec */ {Cache_UNKNOWN, 0 }, +/* ed */ {Cache_UNKNOWN, 0 }, +/* ee */ {Cache_UNKNOWN, 0 }, +/* ef */ {Cache_UNKNOWN, 0 }, +/* f0 */ {Cache_UNKNOWN, 0 }, +/* f1 */ {Cache_UNKNOWN, 0 }, +/* f2 */ {Cache_UNKNOWN, 0 }, +/* f3 */ {Cache_UNKNOWN, 0 }, +/* f4 */ {Cache_UNKNOWN, 0 }, +/* f5 */ {Cache_UNKNOWN, 0 }, +/* f6 */ {Cache_UNKNOWN, 0 }, +/* f7 */ {Cache_UNKNOWN, 0 }, +/* f8 */ {Cache_UNKNOWN, 0 }, +/* f9 */ {Cache_UNKNOWN, 0 }, +/* fa */ {Cache_UNKNOWN, 0 }, +/* fb */ {Cache_UNKNOWN, 0 }, +/* fc */ {Cache_UNKNOWN, 0 }, +/* fd */ {Cache_UNKNOWN, 0 }, +/* fe */ {Cache_UNKNOWN, 0 }, +/* ff */ {Cache_UNKNOWN, 0 } +}; + + +/* + * use the above table to determine the CacheEntryLineSize. + */ +static void +getIntelCacheEntryLineSize(unsigned long val, int *level, + unsigned long *lineSize) +{ + CacheType type; + + type = CacheMap[val].type; + /* only interested in data caches */ + /* NOTE val = 0x40 is a special value that means no L2 or L3 cache. + * this data check has the side effect of rejecting that entry. If + * that wasn't the case, we could have to reject it explicitly */ + if (CacheMap[val].lineSize == 0) { + return; + } + /* look at the caches, skip types we aren't interested in. + * if we already have a value for a lower level cache, skip the + * current entry */ + if ((type == Cache_L1)|| (type == Cache_L1d)) { + *level = 1; + *lineSize = CacheMap[val].lineSize; + } else if ((*level >= 2) && ((type == Cache_L2) || (type == Cache_L2d))) { + *level = 2; + *lineSize = CacheMap[val].lineSize; + } else if ((*level >= 3) && ((type == Cache_L3) || (type == Cache_L3d))) { + *level = 3; + *lineSize = CacheMap[val].lineSize; + } + return; +} + + +static void +getIntelRegisterCacheLineSize(unsigned long val, + int *level, unsigned long *lineSize) +{ + getIntelCacheEntryLineSize(val >> 24 & 0xff, level, lineSize); + getIntelCacheEntryLineSize(val >> 16 & 0xff, level, lineSize); + getIntelCacheEntryLineSize(val >> 8 & 0xff, level, lineSize); + getIntelCacheEntryLineSize(val & 0xff, level, lineSize); +} + +/* + * returns '0' if no recognized cache is found, or if the cache + * information is supported by this processor + */ +static unsigned long +getIntelCacheLineSize(int cpuidLevel) +{ + int level = 4; + unsigned long lineSize = 0; + unsigned long eax, ebx, ecx, edx; + int repeat, count; + + if (cpuidLevel < 2) { + return 0; + } + + /* command '2' of the cpuid is intel's cache info call. Each byte of the + * 4 registers contain a potential descriptor for the cache. The CacheMap + * table maps the cache entry with the processor cache. Register 'al' + * contains a count value that cpuid '2' needs to be called in order to + * find all the cache descriptors. Only registers with the high bit set + * to 'zero' have valid descriptors. This code loops through all the + * required calls to cpuid '2' and passes any valid descriptors it finds + * to the getIntelRegisterCacheLineSize code, which breaks the registers + * down into their component descriptors. In the end the lineSize of the + * lowest level cache data cache is returned. */ + cpuid(2, &eax, &ebx, &ecx, &edx); + repeat = eax & 0xf; + for (count = 0; count < repeat; count++) { + if ((eax & 0x80000000) == 0) { + getIntelRegisterCacheLineSize(eax & 0xffffff00, &level, &lineSize); + } + if ((ebx & 0x80000000) == 0) { + getIntelRegisterCacheLineSize(ebx, &level, &lineSize); + } + if ((ecx & 0x80000000) == 0) { + getIntelRegisterCacheLineSize(ecx, &level, &lineSize); + } + if ((edx & 0x80000000) == 0) { + getIntelRegisterCacheLineSize(edx, &level, &lineSize); + } + if (count+1 != repeat) { + cpuid(2, &eax, &ebx, &ecx, &edx); + } + } + return lineSize; +} + +/* + * returns '0' if the cache info is not supported by this processor. + * This is based on the AMD extended cache commands for cpuid. + * (see "AMD Processor Recognition Application Note" Publication 20734). + * Some other processors use the identical scheme. + * (see "Processor Recognition, Transmeta Corporation"). + */ +static unsigned long +getOtherCacheLineSize(unsigned long cpuidLevel) +{ + unsigned long lineSize = 0; + unsigned long eax, ebx, ecx, edx; + + /* get the Extended CPUID level */ + cpuid(0x80000000, &eax, &ebx, &ecx, &edx); + cpuidLevel = eax; + + if (cpuidLevel >= 0x80000005) { + cpuid(0x80000005, &eax, &ebx, &ecx, &edx); + lineSize = ecx & 0xff; /* line Size, L1 Data Cache */ + } + return lineSize; +} + +static const char * const manMap[] = { +#define INTEL 0 + "GenuineIntel", +#define AMD 1 + "AuthenticAMD", +#define CYRIX 2 + "CyrixInstead", +#define CENTAUR 2 + "CentaurHauls", +#define NEXGEN 3 + "NexGenDriven", +#define TRANSMETA 4 + "GenuineTMx86", +#define RISE 5 + "RiseRiseRise", +#define UMC 6 + "UMC UMC UMC ", +#define SIS 7 + "Sis Sis Sis ", +#define NATIONAL 8 + "Geode by NSC", +}; + +static const int n_manufacturers = sizeof(manMap)/sizeof(manMap[0]); + + +#define MAN_UNKNOWN 9 + +#if !defined(AMD_64) +#define SSE2_FLAG (1<<26) +unsigned long +s_mpi_is_sse2() +{ + unsigned long eax, ebx, ecx, edx; + int manufacturer = MAN_UNKNOWN; + int i; + char string[13]; + + if (is386() || is486()) { + return 0; + } + cpuid(0, &eax, &ebx, &ecx, &edx); + *(int *)string = ebx; + *(int *)&string[4] = edx; + *(int *)&string[8] = ecx; + string[12] = 0; + + /* has no SSE2 extensions */ + if (eax == 0) { + return 0; + } + + for (i=0; i < n_manufacturers; i++) { + if ( strcmp(manMap[i],string) == 0) { + manufacturer = i; + break; + } + } + + /* only use sse2 on intel */ + if (manufacturer != INTEL) { + return 0; + } + + cpuid(1,&eax,&ebx,&ecx,&edx); + return (edx & SSE2_FLAG) == SSE2_FLAG; +} +#endif + +unsigned long +s_mpi_getProcessorLineSize() +{ + unsigned long eax, ebx, ecx, edx; + unsigned long cpuidLevel; + unsigned long cacheLineSize = 0; + int manufacturer = MAN_UNKNOWN; + int i; + char string[65]; + +#if !defined(AMD_64) + if (is386()) { + return 0; /* 386 had no cache */ + } if (is486()) { + return 32; /* really? need more info */ + } +#endif + + /* Pentium, cpuid command is available */ + cpuid(0, &eax, &ebx, &ecx, &edx); + cpuidLevel = eax; + *(int *)string = ebx; + *(int *)&string[4] = edx; + *(int *)&string[8] = ecx; + string[12] = 0; + + manufacturer = MAN_UNKNOWN; + for (i=0; i < n_manufacturers; i++) { + if ( strcmp(manMap[i],string) == 0) { + manufacturer = i; + } + } + + if (manufacturer == INTEL) { + cacheLineSize = getIntelCacheLineSize(cpuidLevel); + } else { + cacheLineSize = getOtherCacheLineSize(cpuidLevel); + } + /* doesn't support cache info based on cpuid. This means + * an old pentium class processor, which have cache lines of + * 32. If we learn differently, we can use a switch based on + * the Manufacturer id */ + if (cacheLineSize == 0) { + cacheLineSize = 32; + } + return cacheLineSize; +} +#define MPI_GET_PROCESSOR_LINE_SIZE_DEFINED 1 +#endif + +#if defined(__ppc64__) +/* + * Sigh, The PPC has some really nice features to help us determine cache + * size, since it had lots of direct control functions to do so. The POWER + * processor even has an instruction to do this, but it was dropped in + * PowerPC. Unfortunately most of them are not available in user mode. + * + * The dcbz function would be a great way to determine cache line size except + * 1) it only works on write-back memory (it throws an exception otherwise), + * and 2) because so many mac programs 'knew' the processor cache size was + * 32 bytes, they used this instruction as a fast 'zero 32 bytes'. Now the new + * G5 processor has 128 byte cache, but dcbz only clears 32 bytes to keep + * these programs happy. dcbzl work if 64 bit instructions are supported. + * If you know 64 bit instructions are supported, and that stack is + * write-back, you can use this code. + */ +#include "memory.h" + +/* clear the cache line that contains 'array' */ +static inline void dcbzl(char *array) +{ + register char *a asm("r2") = array; + __asm__ __volatile__( "dcbzl %0,r0" : "=r" (a): "0"(a) ); +} + + +#define PPC_DO_ALIGN(x,y) ((char *)\ + ((((long long) (x))+((y)-1))&~((y)-1))) + +#define PPC_MAX_LINE_SIZE 256 +unsigned long +s_mpi_getProcessorLineSize() +{ + char testArray[2*PPC_MAX_LINE_SIZE+1]; + char *test; + int i; + + /* align the array on a maximum line size boundary, so we + * know we are starting to clear from the first address */ + test = PPC_DO_ALIGN(testArray, PPC_MAX_LINE_SIZE); + /* set all the values to 1's */ + memset(test, 0xff, PPC_MAX_LINE_SIZE); + /* clear one cache block starting at 'test' */ + dcbzl(test); + + /* find the size of the cleared area, that's our block size */ + for (i=PPC_MAX_LINE_SIZE; i != 0; i = i/2) { + if (test[i-1] == 0) { + return i; + } + } + return 0; +} + +#define MPI_GET_PROCESSOR_LINE_SIZE_DEFINED 1 +#endif + + +/* + * put other processor and platform specific cache code here + * return the smallest cache line size in bytes on the processor + * (usually the L1 cache). If the OS has a call, this would be + * a greate place to put it. + * + * If there is no cache, return 0; + * + * define MPI_GET_PROCESSOR_LINE_SIZE_DEFINED so the generic functions + * below aren't compiled. + * + */ + + +/* target.mk can define MPI_CACHE_LINE_SIZE if it's common for the family or + * OS */ +#if defined(MPI_CACHE_LINE_SIZE) && !defined(MPI_GET_PROCESSOR_LINE_SIZE_DEFINED) + +unsigned long +s_mpi_getProcessorLineSize() +{ + return MPI_CACHE_LINE_SIZE; +} +#define MPI_GET_PROCESSOR_LINE_SIZE_DEFINED 1 +#endif + + +/* If no way to get the processor cache line size has been defined, assume + * it's 32 bytes (most common value, does not significantly impact performance) + */ +#ifndef MPI_GET_PROCESSOR_LINE_SIZE_DEFINED +unsigned long +s_mpi_getProcessorLineSize() +{ + return 32; +} +#endif + +#ifdef TEST_IT +#include <stdio.h> + +main() +{ + printf("line size = %d\n", s_mpi_getProcessorLineSize()); +} +#endif diff --git a/usr/src/common/mpi/mpi-config.h b/usr/src/common/mpi/mpi-config.h new file mode 100644 index 0000000000..641cd6b2cf --- /dev/null +++ b/usr/src/common/mpi/mpi-config.h @@ -0,0 +1,119 @@ +/* Default configuration for MPI library + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the MPI Arbitrary Precision Integer Arithmetic library. + * + * The Initial Developer of the Original Code is + * Michael J. Fromberger. + * Portions created by the Initial Developer are Copyright (C) 1997 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Netscape Communications Corporation + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _MPI_CONFIG_H +#define _MPI_CONFIG_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* $Id: mpi-config.h,v 1.5 2004/04/25 15:03:10 gerv%gerv.net Exp $ */ + +/* + For boolean options, + 0 = no + 1 = yes + + Other options are documented individually. + + */ + +#ifndef MP_IOFUNC +#define MP_IOFUNC 0 /* include mp_print() ? */ +#endif + +#ifndef MP_MODARITH +#define MP_MODARITH 1 /* include modular arithmetic ? */ +#endif + +#ifndef MP_NUMTH +#define MP_NUMTH 1 /* include number theoretic functions? */ +#endif + +#ifndef MP_LOGTAB +#define MP_LOGTAB 1 /* use table of logs instead of log()? */ +#endif + +#ifndef MP_MEMSET +#define MP_MEMSET 1 /* use memset() to zero buffers? */ +#endif + +#ifndef MP_MEMCPY +#define MP_MEMCPY 1 /* use memcpy() to copy buffers? */ +#endif + +#ifndef MP_CRYPTO +#define MP_CRYPTO 1 /* erase memory on free? */ +#endif + +#ifndef MP_ARGCHK +/* + 0 = no parameter checks + 1 = runtime checks, continue execution and return an error to caller + 2 = assertions; dump core on parameter errors + */ +#ifdef DEBUG +#define MP_ARGCHK 2 /* how to check input arguments */ +#else +#define MP_ARGCHK 1 /* how to check input arguments */ +#endif +#endif + +#ifndef MP_DEBUG +#define MP_DEBUG 0 /* print diagnostic output? */ +#endif + +#ifndef MP_DEFPREC +#define MP_DEFPREC 64 /* default precision, in digits */ +#endif + +#ifndef MP_MACRO +#define MP_MACRO 0 /* use macros for frequent calls? */ +#endif + +#ifndef MP_SQUARE +#define MP_SQUARE 1 /* use separate squaring code? */ +#endif + +#endif /* _MPI_CONFIG_H */ diff --git a/usr/src/common/mpi/mpi-priv.h b/usr/src/common/mpi/mpi-priv.h new file mode 100644 index 0000000000..fa6af6d661 --- /dev/null +++ b/usr/src/common/mpi/mpi-priv.h @@ -0,0 +1,329 @@ +/* + * mpi-priv.h - Private header file for MPI + * Arbitrary precision integer arithmetic library + * + * NOTE WELL: the content of this header file is NOT part of the "public" + * API for the MPI library, and may change at any time. + * Application programs that use libmpi should NOT include this header file. + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the MPI Arbitrary Precision Integer Arithmetic library. + * + * The Initial Developer of the Original Code is + * Michael J. Fromberger. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Netscape Communications Corporation + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _MPI_PRIV_H +#define _MPI_PRIV_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* $Id: mpi-priv.h,v 1.20 2005/11/22 07:16:43 relyea%netscape.com Exp $ */ + +#include "mpi.h" +#ifndef _KERNEL +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#endif /* _KERNEL */ + +#if MP_DEBUG +#include <stdio.h> + +#define DIAG(T,V) {fprintf(stderr,T);mp_print(V,stderr);fputc('\n',stderr);} +#else +#define DIAG(T,V) +#endif + +/* If we aren't using a wired-in logarithm table, we need to include + the math library to get the log() function + */ + +/* {{{ s_logv_2[] - log table for 2 in various bases */ + +#if MP_LOGTAB +/* + A table of the logs of 2 for various bases (the 0 and 1 entries of + this table are meaningless and should not be referenced). + + This table is used to compute output lengths for the mp_toradix() + function. Since a number n in radix r takes up about log_r(n) + digits, we estimate the output size by taking the least integer + greater than log_r(n), where: + + log_r(n) = log_2(n) * log_r(2) + + This table, therefore, is a table of log_r(2) for 2 <= r <= 36, + which are the output bases supported. + */ + +extern const float s_logv_2[]; +#define LOG_V_2(R) s_logv_2[(R)] + +#else + +/* + If MP_LOGTAB is not defined, use the math library to compute the + logarithms on the fly. Otherwise, use the table. + Pick which works best for your system. + */ + +#include <math.h> +#define LOG_V_2(R) (log(2.0)/log(R)) + +#endif /* if MP_LOGTAB */ + +/* }}} */ + +/* {{{ Digit arithmetic macros */ + +/* + When adding and multiplying digits, the results can be larger than + can be contained in an mp_digit. Thus, an mp_word is used. These + macros mask off the upper and lower digits of the mp_word (the + mp_word may be more than 2 mp_digits wide, but we only concern + ourselves with the low-order 2 mp_digits) + */ + +#define CARRYOUT(W) (mp_digit)((W)>>DIGIT_BIT) +#define ACCUM(W) (mp_digit)(W) + +#define MP_MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MP_MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MP_HOWMANY(a,b) (((a) + (b) - 1)/(b)) +#define MP_ROUNDUP(a,b) (MP_HOWMANY(a,b) * (b)) + +/* }}} */ + +/* {{{ Comparison constants */ + +#define MP_LT -1 +#define MP_EQ 0 +#define MP_GT 1 + +/* }}} */ + +/* {{{ private function declarations */ + +/* + If MP_MACRO is false, these will be defined as actual functions; + otherwise, suitable macro definitions will be used. This works + around the fact that ANSI C89 doesn't support an 'inline' keyword + (although I hear C9x will ... about bloody time). At present, the + macro definitions are identical to the function bodies, but they'll + expand in place, instead of generating a function call. + + I chose these particular functions to be made into macros because + some profiling showed they are called a lot on a typical workload, + and yet they are primarily housekeeping. + */ +#if MP_MACRO == 0 + void s_mp_setz(mp_digit *dp, mp_size count); /* zero digits */ + void s_mp_copy(const mp_digit *sp, mp_digit *dp, mp_size count); /* copy */ + void *s_mp_alloc(size_t nb, size_t ni, int flag); /* general allocator */ + void s_mp_free(void *ptr, mp_size); /* general free function */ +extern unsigned long mp_allocs; +extern unsigned long mp_frees; +extern unsigned long mp_copies; +#else + + /* Even if these are defined as macros, we need to respect the settings + of the MP_MEMSET and MP_MEMCPY configuration options... + */ + #if MP_MEMSET == 0 + #define s_mp_setz(dp, count) \ + {int ix;for(ix=0;ix<(count);ix++)(dp)[ix]=0;} + #else + #define s_mp_setz(dp, count) memset(dp, 0, (count) * sizeof(mp_digit)) + #endif /* MP_MEMSET */ + + #if MP_MEMCPY == 0 + #define s_mp_copy(sp, dp, count) \ + {int ix;for(ix=0;ix<(count);ix++)(dp)[ix]=(sp)[ix];} + #else + #define s_mp_copy(sp, dp, count) memcpy(dp, sp, (count) * sizeof(mp_digit)) + #endif /* MP_MEMCPY */ + + #define s_mp_alloc(nb, ni) calloc(nb, ni) + #define s_mp_free(ptr) {if(ptr) free(ptr);} +#endif /* MP_MACRO */ + +mp_err s_mp_grow(mp_int *mp, mp_size min); /* increase allocated size */ +mp_err s_mp_pad(mp_int *mp, mp_size min); /* left pad with zeroes */ + +#if MP_MACRO == 0 + void s_mp_clamp(mp_int *mp); /* clip leading zeroes */ +#else + #define s_mp_clamp(mp)\ + { mp_size used = MP_USED(mp); \ + while (used > 1 && DIGIT(mp, used - 1) == 0) --used; \ + MP_USED(mp) = used; \ + } +#endif /* MP_MACRO */ + +void s_mp_exch(mp_int *a, mp_int *b); /* swap a and b in place */ + +mp_err s_mp_lshd(mp_int *mp, mp_size p); /* left-shift by p digits */ +void s_mp_rshd(mp_int *mp, mp_size p); /* right-shift by p digits */ +mp_err s_mp_mul_2d(mp_int *mp, mp_digit d); /* multiply by 2^d in place */ +void s_mp_div_2d(mp_int *mp, mp_digit d); /* divide by 2^d in place */ +void s_mp_mod_2d(mp_int *mp, mp_digit d); /* modulo 2^d in place */ +void s_mp_div_2(mp_int *mp); /* divide by 2 in place */ +mp_err s_mp_mul_2(mp_int *mp); /* multiply by 2 in place */ +mp_err s_mp_norm(mp_int *a, mp_int *b, mp_digit *pd); + /* normalize for division */ +mp_err s_mp_add_d(mp_int *mp, mp_digit d); /* unsigned digit addition */ +mp_err s_mp_sub_d(mp_int *mp, mp_digit d); /* unsigned digit subtract */ +mp_err s_mp_mul_d(mp_int *mp, mp_digit d); /* unsigned digit multiply */ +mp_err s_mp_div_d(mp_int *mp, mp_digit d, mp_digit *r); + /* unsigned digit divide */ +mp_err s_mp_reduce(mp_int *x, const mp_int *m, const mp_int *mu); + /* Barrett reduction */ +mp_err s_mp_add(mp_int *a, const mp_int *b); /* magnitude addition */ +mp_err s_mp_add_3arg(const mp_int *a, const mp_int *b, mp_int *c); +mp_err s_mp_sub(mp_int *a, const mp_int *b); /* magnitude subtract */ +mp_err s_mp_sub_3arg(const mp_int *a, const mp_int *b, mp_int *c); +mp_err s_mp_add_offset(mp_int *a, mp_int *b, mp_size offset); + /* a += b * RADIX^offset */ +mp_err s_mp_mul(mp_int *a, const mp_int *b); /* magnitude multiply */ +#if MP_SQUARE +mp_err s_mp_sqr(mp_int *a); /* magnitude square */ +#else +#define s_mp_sqr(a) s_mp_mul(a, a) +#endif +mp_err s_mp_div(mp_int *rem, mp_int *div, mp_int *quot); /* magnitude div */ +mp_err s_mp_exptmod(const mp_int *a, const mp_int *b, const mp_int *m, mp_int *c); +mp_err s_mp_2expt(mp_int *a, mp_digit k); /* a = 2^k */ +int s_mp_cmp(const mp_int *a, const mp_int *b); /* magnitude comparison */ +int s_mp_cmp_d(const mp_int *a, mp_digit d); /* magnitude digit compare */ +int s_mp_ispow2(const mp_int *v); /* is v a power of 2? */ +int s_mp_ispow2d(mp_digit d); /* is d a power of 2? */ + +int s_mp_tovalue(char ch, int r); /* convert ch to value */ +char s_mp_todigit(mp_digit val, int r, int low); /* convert val to digit */ +int s_mp_outlen(int bits, int r); /* output length in bytes */ +mp_digit s_mp_invmod_radix(mp_digit P); /* returns (P ** -1) mod RADIX */ +mp_err s_mp_invmod_odd_m( const mp_int *a, const mp_int *m, mp_int *c); +mp_err s_mp_invmod_2d( const mp_int *a, mp_size k, mp_int *c); +mp_err s_mp_invmod_even_m(const mp_int *a, const mp_int *m, mp_int *c); + +#ifdef NSS_USE_COMBA + +#define IS_POWER_OF_2(a) ((a) && !((a) & ((a)-1))) + +void s_mp_mul_comba_4(const mp_int *A, const mp_int *B, mp_int *C); +void s_mp_mul_comba_8(const mp_int *A, const mp_int *B, mp_int *C); +void s_mp_mul_comba_16(const mp_int *A, const mp_int *B, mp_int *C); +void s_mp_mul_comba_32(const mp_int *A, const mp_int *B, mp_int *C); + +void s_mp_sqr_comba_4(const mp_int *A, mp_int *B); +void s_mp_sqr_comba_8(const mp_int *A, mp_int *B); +void s_mp_sqr_comba_16(const mp_int *A, mp_int *B); +void s_mp_sqr_comba_32(const mp_int *A, mp_int *B); + +#endif /* end NSS_USE_COMBA */ + +/* ------ mpv functions, operate on arrays of digits, not on mp_int's ------ */ +#if defined (__OS2__) && defined (__IBMC__) +#define MPI_ASM_DECL __cdecl +#else +#define MPI_ASM_DECL +#endif + +#ifdef MPI_AMD64 + +mp_digit MPI_ASM_DECL s_mpv_mul_set_vec64(mp_digit*, mp_digit *, mp_size, mp_digit); +mp_digit MPI_ASM_DECL s_mpv_mul_add_vec64(mp_digit*, const mp_digit*, mp_size, mp_digit); + +/* c = a * b */ +#define s_mpv_mul_d(a, a_len, b, c) \ + ((unsigned long*)c)[a_len] = s_mpv_mul_set_vec64(c, a, a_len, b) + +/* c += a * b */ +#define s_mpv_mul_d_add(a, a_len, b, c) \ + ((unsigned long*)c)[a_len] = s_mpv_mul_add_vec64(c, a, a_len, b) + +#else + +void MPI_ASM_DECL s_mpv_mul_d(const mp_digit *a, mp_size a_len, + mp_digit b, mp_digit *c); +void MPI_ASM_DECL s_mpv_mul_d_add(const mp_digit *a, mp_size a_len, + mp_digit b, mp_digit *c); + +#endif + +void MPI_ASM_DECL s_mpv_mul_d_add_prop(const mp_digit *a, + mp_size a_len, mp_digit b, + mp_digit *c); +void MPI_ASM_DECL s_mpv_sqr_add_prop(const mp_digit *a, + mp_size a_len, + mp_digit *sqrs); + +mp_err MPI_ASM_DECL s_mpv_div_2dx1d(mp_digit Nhi, mp_digit Nlo, + mp_digit divisor, mp_digit *quot, mp_digit *rem); + +/* c += a * b * (MP_RADIX ** offset); */ +#define s_mp_mul_d_add_offset(a, b, c, off) \ +(s_mpv_mul_d_add_prop(MP_DIGITS(a), MP_USED(a), b, MP_DIGITS(c) + off), MP_OKAY) + +typedef struct { + mp_int N; /* modulus N */ + mp_digit n0prime; /* n0' = - (n0 ** -1) mod MP_RADIX */ + mp_size b; /* R == 2 ** b, also b = # significant bits in N */ +} mp_mont_modulus; + +mp_err s_mp_mul_mont(const mp_int *a, const mp_int *b, mp_int *c, + mp_mont_modulus *mmm); +mp_err s_mp_redc(mp_int *T, mp_mont_modulus *mmm); + +/* + * s_mpi_getProcessorLineSize() returns the size in bytes of the cache line + * if a cache exists, or zero if there is no cache. If more than one + * cache line exists, it should return the smallest line size (which is + * usually the L1 cache). + * + * mp_modexp uses this information to make sure that private key information + * isn't being leaked through the cache. + * + * see mpcpucache.c for the implementation. + */ +unsigned long s_mpi_getProcessorLineSize(); + +/* }}} */ +#endif /* _MPI_PRIV_H */ diff --git a/usr/src/common/mpi/mpi.c b/usr/src/common/mpi/mpi.c new file mode 100644 index 0000000000..b16c711e34 --- /dev/null +++ b/usr/src/common/mpi/mpi.c @@ -0,0 +1,4872 @@ +/* + * mpi.c + * + * Arbitrary precision integer arithmetic library + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the MPI Arbitrary Precision Integer Arithmetic library. + * + * The Initial Developer of the Original Code is + * Michael J. Fromberger. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Netscape Communications Corporation + * Douglas Stebila <douglas@stebila.ca> of Sun Laboratories. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* $Id: mpi.c,v 1.45 2006/09/29 20:12:21 alexei.volkov.bugs%sun.com Exp $ */ + +#include "mpi-priv.h" +#if defined(OSF1) +#include <c_asm.h> +#endif + +#if MP_LOGTAB +/* + A table of the logs of 2 for various bases (the 0 and 1 entries of + this table are meaningless and should not be referenced). + + This table is used to compute output lengths for the mp_toradix() + function. Since a number n in radix r takes up about log_r(n) + digits, we estimate the output size by taking the least integer + greater than log_r(n), where: + + log_r(n) = log_2(n) * log_r(2) + + This table, therefore, is a table of log_r(2) for 2 <= r <= 36, + which are the output bases supported. + */ +#include "logtab.h" +#endif + +/* {{{ Constant strings */ + +/* Constant strings returned by mp_strerror() */ +static const char *mp_err_string[] = { + "unknown result code", /* say what? */ + "boolean true", /* MP_OKAY, MP_YES */ + "boolean false", /* MP_NO */ + "out of memory", /* MP_MEM */ + "argument out of range", /* MP_RANGE */ + "invalid input parameter", /* MP_BADARG */ + "result is undefined" /* MP_UNDEF */ +}; + +/* Value to digit maps for radix conversion */ + +/* s_dmap_1 - standard digits and letters */ +static const char *s_dmap_1 = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; + +/* }}} */ + +unsigned long mp_allocs; +unsigned long mp_frees; +unsigned long mp_copies; + +/* {{{ Default precision manipulation */ + +/* Default precision for newly created mp_int's */ +static mp_size s_mp_defprec = MP_DEFPREC; + +mp_size mp_get_prec(void) +{ + return s_mp_defprec; + +} /* end mp_get_prec() */ + +void mp_set_prec(mp_size prec) +{ + if(prec == 0) + s_mp_defprec = MP_DEFPREC; + else + s_mp_defprec = prec; + +} /* end mp_set_prec() */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ mp_init(mp, kmflag) */ + +/* + mp_init(mp, kmflag) + + Initialize a new zero-valued mp_int. Returns MP_OKAY if successful, + MP_MEM if memory could not be allocated for the structure. + */ + +mp_err mp_init(mp_int *mp, int kmflag) +{ + return mp_init_size(mp, s_mp_defprec, kmflag); + +} /* end mp_init() */ + +/* }}} */ + +/* {{{ mp_init_size(mp, prec, kmflag) */ + +/* + mp_init_size(mp, prec, kmflag) + + Initialize a new zero-valued mp_int with at least the given + precision; returns MP_OKAY if successful, or MP_MEM if memory could + not be allocated for the structure. + */ + +mp_err mp_init_size(mp_int *mp, mp_size prec, int kmflag) +{ + ARGCHK(mp != NULL && prec > 0, MP_BADARG); + + prec = MP_ROUNDUP(prec, s_mp_defprec); + if((DIGITS(mp) = s_mp_alloc(prec, sizeof(mp_digit), kmflag)) == NULL) + return MP_MEM; + + SIGN(mp) = ZPOS; + USED(mp) = 1; + ALLOC(mp) = prec; + + return MP_OKAY; + +} /* end mp_init_size() */ + +/* }}} */ + +/* {{{ mp_init_copy(mp, from) */ + +/* + mp_init_copy(mp, from) + + Initialize mp as an exact copy of from. Returns MP_OKAY if + successful, MP_MEM if memory could not be allocated for the new + structure. + */ + +mp_err mp_init_copy(mp_int *mp, const mp_int *from) +{ + ARGCHK(mp != NULL && from != NULL, MP_BADARG); + + if(mp == from) + return MP_OKAY; + + if((DIGITS(mp) = s_mp_alloc(ALLOC(from), sizeof(mp_digit), FLAG(from))) == NULL) + return MP_MEM; + + s_mp_copy(DIGITS(from), DIGITS(mp), USED(from)); + USED(mp) = USED(from); + ALLOC(mp) = ALLOC(from); + SIGN(mp) = SIGN(from); + FLAG(mp) = FLAG(from); + + return MP_OKAY; + +} /* end mp_init_copy() */ + +/* }}} */ + +/* {{{ mp_copy(from, to) */ + +/* + mp_copy(from, to) + + Copies the mp_int 'from' to the mp_int 'to'. It is presumed that + 'to' has already been initialized (if not, use mp_init_copy() + instead). If 'from' and 'to' are identical, nothing happens. + */ + +mp_err mp_copy(const mp_int *from, mp_int *to) +{ + ARGCHK(from != NULL && to != NULL, MP_BADARG); + + if(from == to) + return MP_OKAY; + + ++mp_copies; + { /* copy */ + mp_digit *tmp; + + /* + If the allocated buffer in 'to' already has enough space to hold + all the used digits of 'from', we'll re-use it to avoid hitting + the memory allocater more than necessary; otherwise, we'd have + to grow anyway, so we just allocate a hunk and make the copy as + usual + */ + if(ALLOC(to) >= USED(from)) { + s_mp_setz(DIGITS(to) + USED(from), ALLOC(to) - USED(from)); + s_mp_copy(DIGITS(from), DIGITS(to), USED(from)); + + } else { + if((tmp = s_mp_alloc(ALLOC(from), sizeof(mp_digit), FLAG(from))) == NULL) + return MP_MEM; + + s_mp_copy(DIGITS(from), tmp, USED(from)); + + if(DIGITS(to) != NULL) { +#if MP_CRYPTO + s_mp_setz(DIGITS(to), ALLOC(to)); +#endif + s_mp_free(DIGITS(to), ALLOC(to)); + } + + DIGITS(to) = tmp; + ALLOC(to) = ALLOC(from); + } + + /* Copy the precision and sign from the original */ + USED(to) = USED(from); + SIGN(to) = SIGN(from); + } /* end copy */ + + return MP_OKAY; + +} /* end mp_copy() */ + +/* }}} */ + +/* {{{ mp_exch(mp1, mp2) */ + +/* + mp_exch(mp1, mp2) + + Exchange mp1 and mp2 without allocating any intermediate memory + (well, unless you count the stack space needed for this call and the + locals it creates...). This cannot fail. + */ + +void mp_exch(mp_int *mp1, mp_int *mp2) +{ +#if MP_ARGCHK == 2 + assert(mp1 != NULL && mp2 != NULL); +#else + if(mp1 == NULL || mp2 == NULL) + return; +#endif + + s_mp_exch(mp1, mp2); + +} /* end mp_exch() */ + +/* }}} */ + +/* {{{ mp_clear(mp) */ + +/* + mp_clear(mp) + + Release the storage used by an mp_int, and void its fields so that + if someone calls mp_clear() again for the same int later, we won't + get tollchocked. + */ + +void mp_clear(mp_int *mp) +{ + if(mp == NULL) + return; + + if(DIGITS(mp) != NULL) { +#if MP_CRYPTO + s_mp_setz(DIGITS(mp), ALLOC(mp)); +#endif + s_mp_free(DIGITS(mp), ALLOC(mp)); + DIGITS(mp) = NULL; + } + + USED(mp) = 0; + ALLOC(mp) = 0; + +} /* end mp_clear() */ + +/* }}} */ + +/* {{{ mp_zero(mp) */ + +/* + mp_zero(mp) + + Set mp to zero. Does not change the allocated size of the structure, + and therefore cannot fail (except on a bad argument, which we ignore) + */ +void mp_zero(mp_int *mp) +{ + if(mp == NULL) + return; + + s_mp_setz(DIGITS(mp), ALLOC(mp)); + USED(mp) = 1; + SIGN(mp) = ZPOS; + +} /* end mp_zero() */ + +/* }}} */ + +/* {{{ mp_set(mp, d) */ + +void mp_set(mp_int *mp, mp_digit d) +{ + if(mp == NULL) + return; + + mp_zero(mp); + DIGIT(mp, 0) = d; + +} /* end mp_set() */ + +/* }}} */ + +/* {{{ mp_set_int(mp, z) */ + +mp_err mp_set_int(mp_int *mp, long z) +{ + int ix; + unsigned long v = labs(z); + mp_err res; + + ARGCHK(mp != NULL, MP_BADARG); + + mp_zero(mp); + if(z == 0) + return MP_OKAY; /* shortcut for zero */ + + if (sizeof v <= sizeof(mp_digit)) { + DIGIT(mp,0) = v; + } else { + for (ix = sizeof(long) - 1; ix >= 0; ix--) { + if ((res = s_mp_mul_d(mp, (UCHAR_MAX + 1))) != MP_OKAY) + return res; + + res = s_mp_add_d(mp, (mp_digit)((v >> (ix * CHAR_BIT)) & UCHAR_MAX)); + if (res != MP_OKAY) + return res; + } + } + if(z < 0) + SIGN(mp) = NEG; + + return MP_OKAY; + +} /* end mp_set_int() */ + +/* }}} */ + +/* {{{ mp_set_ulong(mp, z) */ + +mp_err mp_set_ulong(mp_int *mp, unsigned long z) +{ + int ix; + mp_err res; + + ARGCHK(mp != NULL, MP_BADARG); + + mp_zero(mp); + if(z == 0) + return MP_OKAY; /* shortcut for zero */ + + if (sizeof z <= sizeof(mp_digit)) { + DIGIT(mp,0) = z; + } else { + for (ix = sizeof(long) - 1; ix >= 0; ix--) { + if ((res = s_mp_mul_d(mp, (UCHAR_MAX + 1))) != MP_OKAY) + return res; + + res = s_mp_add_d(mp, (mp_digit)((z >> (ix * CHAR_BIT)) & UCHAR_MAX)); + if (res != MP_OKAY) + return res; + } + } + return MP_OKAY; +} /* end mp_set_ulong() */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Digit arithmetic */ + +/* {{{ mp_add_d(a, d, b) */ + +/* + mp_add_d(a, d, b) + + Compute the sum b = a + d, for a single digit d. Respects the sign of + its primary addend (single digits are unsigned anyway). + */ + +mp_err mp_add_d(const mp_int *a, mp_digit d, mp_int *b) +{ + mp_int tmp; + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_init_copy(&tmp, a)) != MP_OKAY) + return res; + + if(SIGN(&tmp) == ZPOS) { + if((res = s_mp_add_d(&tmp, d)) != MP_OKAY) + goto CLEANUP; + } else if(s_mp_cmp_d(&tmp, d) >= 0) { + if((res = s_mp_sub_d(&tmp, d)) != MP_OKAY) + goto CLEANUP; + } else { + mp_neg(&tmp, &tmp); + + DIGIT(&tmp, 0) = d - DIGIT(&tmp, 0); + } + + if(s_mp_cmp_d(&tmp, 0) == 0) + SIGN(&tmp) = ZPOS; + + s_mp_exch(&tmp, b); + +CLEANUP: + mp_clear(&tmp); + return res; + +} /* end mp_add_d() */ + +/* }}} */ + +/* {{{ mp_sub_d(a, d, b) */ + +/* + mp_sub_d(a, d, b) + + Compute the difference b = a - d, for a single digit d. Respects the + sign of its subtrahend (single digits are unsigned anyway). + */ + +mp_err mp_sub_d(const mp_int *a, mp_digit d, mp_int *b) +{ + mp_int tmp; + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_init_copy(&tmp, a)) != MP_OKAY) + return res; + + if(SIGN(&tmp) == NEG) { + if((res = s_mp_add_d(&tmp, d)) != MP_OKAY) + goto CLEANUP; + } else if(s_mp_cmp_d(&tmp, d) >= 0) { + if((res = s_mp_sub_d(&tmp, d)) != MP_OKAY) + goto CLEANUP; + } else { + mp_neg(&tmp, &tmp); + + DIGIT(&tmp, 0) = d - DIGIT(&tmp, 0); + SIGN(&tmp) = NEG; + } + + if(s_mp_cmp_d(&tmp, 0) == 0) + SIGN(&tmp) = ZPOS; + + s_mp_exch(&tmp, b); + +CLEANUP: + mp_clear(&tmp); + return res; + +} /* end mp_sub_d() */ + +/* }}} */ + +/* {{{ mp_mul_d(a, d, b) */ + +/* + mp_mul_d(a, d, b) + + Compute the product b = a * d, for a single digit d. Respects the sign + of its multiplicand (single digits are unsigned anyway) + */ + +mp_err mp_mul_d(const mp_int *a, mp_digit d, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if(d == 0) { + mp_zero(b); + return MP_OKAY; + } + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + res = s_mp_mul_d(b, d); + + return res; + +} /* end mp_mul_d() */ + +/* }}} */ + +/* {{{ mp_mul_2(a, c) */ + +mp_err mp_mul_2(const mp_int *a, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + return s_mp_mul_2(c); + +} /* end mp_mul_2() */ + +/* }}} */ + +/* {{{ mp_div_d(a, d, q, r) */ + +/* + mp_div_d(a, d, q, r) + + Compute the quotient q = a / d and remainder r = a mod d, for a + single digit d. Respects the sign of its divisor (single digits are + unsigned anyway). + */ + +mp_err mp_div_d(const mp_int *a, mp_digit d, mp_int *q, mp_digit *r) +{ + mp_err res; + mp_int qp; + mp_digit rem; + int pow; + + ARGCHK(a != NULL, MP_BADARG); + + if(d == 0) + return MP_RANGE; + + /* Shortcut for powers of two ... */ + if((pow = s_mp_ispow2d(d)) >= 0) { + mp_digit mask; + + mask = ((mp_digit)1 << pow) - 1; + rem = DIGIT(a, 0) & mask; + + if(q) { + mp_copy(a, q); + s_mp_div_2d(q, pow); + } + + if(r) + *r = rem; + + return MP_OKAY; + } + + if((res = mp_init_copy(&qp, a)) != MP_OKAY) + return res; + + res = s_mp_div_d(&qp, d, &rem); + + if(s_mp_cmp_d(&qp, 0) == 0) + SIGN(q) = ZPOS; + + if(r) + *r = rem; + + if(q) + s_mp_exch(&qp, q); + + mp_clear(&qp); + return res; + +} /* end mp_div_d() */ + +/* }}} */ + +/* {{{ mp_div_2(a, c) */ + +/* + mp_div_2(a, c) + + Compute c = a / 2, disregarding the remainder. + */ + +mp_err mp_div_2(const mp_int *a, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + s_mp_div_2(c); + + return MP_OKAY; + +} /* end mp_div_2() */ + +/* }}} */ + +/* {{{ mp_expt_d(a, d, b) */ + +mp_err mp_expt_d(const mp_int *a, mp_digit d, mp_int *c) +{ + mp_int s, x; + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_init(&s, FLAG(a))) != MP_OKAY) + return res; + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + DIGIT(&s, 0) = 1; + + while(d != 0) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + } + + d /= 2; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + } + + s_mp_exch(&s, c); + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&s); + + return res; + +} /* end mp_expt_d() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Full arithmetic */ + +/* {{{ mp_abs(a, b) */ + +/* + mp_abs(a, b) + + Compute b = |a|. 'a' and 'b' may be identical. + */ + +mp_err mp_abs(const mp_int *a, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + SIGN(b) = ZPOS; + + return MP_OKAY; + +} /* end mp_abs() */ + +/* }}} */ + +/* {{{ mp_neg(a, b) */ + +/* + mp_neg(a, b) + + Compute b = -a. 'a' and 'b' may be identical. + */ + +mp_err mp_neg(const mp_int *a, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + if(s_mp_cmp_d(b, 0) == MP_EQ) + SIGN(b) = ZPOS; + else + SIGN(b) = (SIGN(b) == NEG) ? ZPOS : NEG; + + return MP_OKAY; + +} /* end mp_neg() */ + +/* }}} */ + +/* {{{ mp_add(a, b, c) */ + +/* + mp_add(a, b, c) + + Compute c = a + b. All parameters may be identical. + */ + +mp_err mp_add(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(SIGN(a) == SIGN(b)) { /* same sign: add values, keep sign */ + MP_CHECKOK( s_mp_add_3arg(a, b, c) ); + } else if(s_mp_cmp(a, b) >= 0) { /* different sign: |a| >= |b| */ + MP_CHECKOK( s_mp_sub_3arg(a, b, c) ); + } else { /* different sign: |a| < |b| */ + MP_CHECKOK( s_mp_sub_3arg(b, a, c) ); + } + + if (s_mp_cmp_d(c, 0) == MP_EQ) + SIGN(c) = ZPOS; + +CLEANUP: + return res; + +} /* end mp_add() */ + +/* }}} */ + +/* {{{ mp_sub(a, b, c) */ + +/* + mp_sub(a, b, c) + + Compute c = a - b. All parameters may be identical. + */ + +mp_err mp_sub(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_err res; + int magDiff; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if (a == b) { + mp_zero(c); + return MP_OKAY; + } + + if (MP_SIGN(a) != MP_SIGN(b)) { + MP_CHECKOK( s_mp_add_3arg(a, b, c) ); + } else if (!(magDiff = s_mp_cmp(a, b))) { + mp_zero(c); + res = MP_OKAY; + } else if (magDiff > 0) { + MP_CHECKOK( s_mp_sub_3arg(a, b, c) ); + } else { + MP_CHECKOK( s_mp_sub_3arg(b, a, c) ); + MP_SIGN(c) = !MP_SIGN(a); + } + + if (s_mp_cmp_d(c, 0) == MP_EQ) + MP_SIGN(c) = MP_ZPOS; + +CLEANUP: + return res; + +} /* end mp_sub() */ + +/* }}} */ + +/* {{{ mp_mul(a, b, c) */ + +/* + mp_mul(a, b, c) + + Compute c = a * b. All parameters may be identical. + */ +mp_err mp_mul(const mp_int *a, const mp_int *b, mp_int * c) +{ + mp_digit *pb; + mp_int tmp; + mp_err res; + mp_size ib; + mp_size useda, usedb; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if (a == c) { + if ((res = mp_init_copy(&tmp, a)) != MP_OKAY) + return res; + if (a == b) + b = &tmp; + a = &tmp; + } else if (b == c) { + if ((res = mp_init_copy(&tmp, b)) != MP_OKAY) + return res; + b = &tmp; + } else { + MP_DIGITS(&tmp) = 0; + } + + if (MP_USED(a) < MP_USED(b)) { + const mp_int *xch = b; /* switch a and b, to do fewer outer loops */ + b = a; + a = xch; + } + + MP_USED(c) = 1; MP_DIGIT(c, 0) = 0; + if((res = s_mp_pad(c, USED(a) + USED(b))) != MP_OKAY) + goto CLEANUP; + +#ifdef NSS_USE_COMBA + if ((MP_USED(a) == MP_USED(b)) && IS_POWER_OF_2(MP_USED(b))) { + if (MP_USED(a) == 4) { + s_mp_mul_comba_4(a, b, c); + goto CLEANUP; + } + if (MP_USED(a) == 8) { + s_mp_mul_comba_8(a, b, c); + goto CLEANUP; + } + if (MP_USED(a) == 16) { + s_mp_mul_comba_16(a, b, c); + goto CLEANUP; + } + if (MP_USED(a) == 32) { + s_mp_mul_comba_32(a, b, c); + goto CLEANUP; + } + } +#endif + + pb = MP_DIGITS(b); + s_mpv_mul_d(MP_DIGITS(a), MP_USED(a), *pb++, MP_DIGITS(c)); + + /* Outer loop: Digits of b */ + useda = MP_USED(a); + usedb = MP_USED(b); + for (ib = 1; ib < usedb; ib++) { + mp_digit b_i = *pb++; + + /* Inner product: Digits of a */ + if (b_i) + s_mpv_mul_d_add(MP_DIGITS(a), useda, b_i, MP_DIGITS(c) + ib); + else + MP_DIGIT(c, ib + useda) = b_i; + } + + s_mp_clamp(c); + + if(SIGN(a) == SIGN(b) || s_mp_cmp_d(c, 0) == MP_EQ) + SIGN(c) = ZPOS; + else + SIGN(c) = NEG; + +CLEANUP: + mp_clear(&tmp); + return res; +} /* end mp_mul() */ + +/* }}} */ + +/* {{{ mp_sqr(a, sqr) */ + +#if MP_SQUARE +/* + Computes the square of a. This can be done more + efficiently than a general multiplication, because many of the + computation steps are redundant when squaring. The inner product + step is a bit more complicated, but we save a fair number of + iterations of the multiplication loop. + */ + +/* sqr = a^2; Caller provides both a and tmp; */ +mp_err mp_sqr(const mp_int *a, mp_int *sqr) +{ + mp_digit *pa; + mp_digit d; + mp_err res; + mp_size ix; + mp_int tmp; + int count; + + ARGCHK(a != NULL && sqr != NULL, MP_BADARG); + + if (a == sqr) { + if((res = mp_init_copy(&tmp, a)) != MP_OKAY) + return res; + a = &tmp; + } else { + DIGITS(&tmp) = 0; + res = MP_OKAY; + } + + ix = 2 * MP_USED(a); + if (ix > MP_ALLOC(sqr)) { + MP_USED(sqr) = 1; + MP_CHECKOK( s_mp_grow(sqr, ix) ); + } + MP_USED(sqr) = ix; + MP_DIGIT(sqr, 0) = 0; + +#ifdef NSS_USE_COMBA + if (IS_POWER_OF_2(MP_USED(a))) { + if (MP_USED(a) == 4) { + s_mp_sqr_comba_4(a, sqr); + goto CLEANUP; + } + if (MP_USED(a) == 8) { + s_mp_sqr_comba_8(a, sqr); + goto CLEANUP; + } + if (MP_USED(a) == 16) { + s_mp_sqr_comba_16(a, sqr); + goto CLEANUP; + } + if (MP_USED(a) == 32) { + s_mp_sqr_comba_32(a, sqr); + goto CLEANUP; + } + } +#endif + + pa = MP_DIGITS(a); + count = MP_USED(a) - 1; + if (count > 0) { + d = *pa++; + s_mpv_mul_d(pa, count, d, MP_DIGITS(sqr) + 1); + for (ix = 3; --count > 0; ix += 2) { + d = *pa++; + s_mpv_mul_d_add(pa, count, d, MP_DIGITS(sqr) + ix); + } /* for(ix ...) */ + MP_DIGIT(sqr, MP_USED(sqr)-1) = 0; /* above loop stopped short of this. */ + + /* now sqr *= 2 */ + s_mp_mul_2(sqr); + } else { + MP_DIGIT(sqr, 1) = 0; + } + + /* now add the squares of the digits of a to sqr. */ + s_mpv_sqr_add_prop(MP_DIGITS(a), MP_USED(a), MP_DIGITS(sqr)); + + SIGN(sqr) = ZPOS; + s_mp_clamp(sqr); + +CLEANUP: + mp_clear(&tmp); + return res; + +} /* end mp_sqr() */ +#endif + +/* }}} */ + +/* {{{ mp_div(a, b, q, r) */ + +/* + mp_div(a, b, q, r) + + Compute q = a / b and r = a mod b. Input parameters may be re-used + as output parameters. If q or r is NULL, that portion of the + computation will be discarded (although it will still be computed) + */ +mp_err mp_div(const mp_int *a, const mp_int *b, mp_int *q, mp_int *r) +{ + mp_err res; + mp_int *pQ, *pR; + mp_int qtmp, rtmp, btmp; + int cmp; + mp_sign signA; + mp_sign signB; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + signA = MP_SIGN(a); + signB = MP_SIGN(b); + + if(mp_cmp_z(b) == MP_EQ) + return MP_RANGE; + + DIGITS(&qtmp) = 0; + DIGITS(&rtmp) = 0; + DIGITS(&btmp) = 0; + + /* Set up some temporaries... */ + if (!r || r == a || r == b) { + MP_CHECKOK( mp_init_copy(&rtmp, a) ); + pR = &rtmp; + } else { + MP_CHECKOK( mp_copy(a, r) ); + pR = r; + } + + if (!q || q == a || q == b) { + MP_CHECKOK( mp_init_size(&qtmp, MP_USED(a), FLAG(a)) ); + pQ = &qtmp; + } else { + MP_CHECKOK( s_mp_pad(q, MP_USED(a)) ); + pQ = q; + mp_zero(pQ); + } + + /* + If |a| <= |b|, we can compute the solution without division; + otherwise, we actually do the work required. + */ + if ((cmp = s_mp_cmp(a, b)) <= 0) { + if (cmp) { + /* r was set to a above. */ + mp_zero(pQ); + } else { + mp_set(pQ, 1); + mp_zero(pR); + } + } else { + MP_CHECKOK( mp_init_copy(&btmp, b) ); + MP_CHECKOK( s_mp_div(pR, &btmp, pQ) ); + } + + /* Compute the signs for the output */ + MP_SIGN(pR) = signA; /* Sr = Sa */ + /* Sq = ZPOS if Sa == Sb */ /* Sq = NEG if Sa != Sb */ + MP_SIGN(pQ) = (signA == signB) ? ZPOS : NEG; + + if(s_mp_cmp_d(pQ, 0) == MP_EQ) + SIGN(pQ) = ZPOS; + if(s_mp_cmp_d(pR, 0) == MP_EQ) + SIGN(pR) = ZPOS; + + /* Copy output, if it is needed */ + if(q && q != pQ) + s_mp_exch(pQ, q); + + if(r && r != pR) + s_mp_exch(pR, r); + +CLEANUP: + mp_clear(&btmp); + mp_clear(&rtmp); + mp_clear(&qtmp); + + return res; + +} /* end mp_div() */ + +/* }}} */ + +/* {{{ mp_div_2d(a, d, q, r) */ + +mp_err mp_div_2d(const mp_int *a, mp_digit d, mp_int *q, mp_int *r) +{ + mp_err res; + + ARGCHK(a != NULL, MP_BADARG); + + if(q) { + if((res = mp_copy(a, q)) != MP_OKAY) + return res; + } + if(r) { + if((res = mp_copy(a, r)) != MP_OKAY) + return res; + } + if(q) { + s_mp_div_2d(q, d); + } + if(r) { + s_mp_mod_2d(r, d); + } + + return MP_OKAY; + +} /* end mp_div_2d() */ + +/* }}} */ + +/* {{{ mp_expt(a, b, c) */ + +/* + mp_expt(a, b, c) + + Compute c = a ** b, that is, raise a to the b power. Uses a + standard iterative square-and-multiply technique. + */ + +mp_err mp_expt(mp_int *a, mp_int *b, mp_int *c) +{ + mp_int s, x; + mp_err res; + mp_digit d; + int dig, bit; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(mp_cmp_z(b) < 0) + return MP_RANGE; + + if((res = mp_init(&s, FLAG(a))) != MP_OKAY) + return res; + + mp_set(&s, 1); + + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + /* Loop over low-order digits in ascending order */ + for(dig = 0; dig < (USED(b) - 1); dig++) { + d = DIGIT(b, dig); + + /* Loop over bits of each non-maximal digit */ + for(bit = 0; bit < DIGIT_BIT; bit++) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + } + } + + /* Consider now the last digit... */ + d = DIGIT(b, dig); + + while(d) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + } + + if(mp_iseven(b)) + SIGN(&s) = SIGN(a); + + res = mp_copy(&s, c); + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&s); + + return res; + +} /* end mp_expt() */ + +/* }}} */ + +/* {{{ mp_2expt(a, k) */ + +/* Compute a = 2^k */ + +mp_err mp_2expt(mp_int *a, mp_digit k) +{ + ARGCHK(a != NULL, MP_BADARG); + + return s_mp_2expt(a, k); + +} /* end mp_2expt() */ + +/* }}} */ + +/* {{{ mp_mod(a, m, c) */ + +/* + mp_mod(a, m, c) + + Compute c = a (mod m). Result will always be 0 <= c < m. + */ + +mp_err mp_mod(const mp_int *a, const mp_int *m, mp_int *c) +{ + mp_err res; + int mag; + + ARGCHK(a != NULL && m != NULL && c != NULL, MP_BADARG); + + if(SIGN(m) == NEG) + return MP_RANGE; + + /* + If |a| > m, we need to divide to get the remainder and take the + absolute value. + + If |a| < m, we don't need to do any division, just copy and adjust + the sign (if a is negative). + + If |a| == m, we can simply set the result to zero. + + This order is intended to minimize the average path length of the + comparison chain on common workloads -- the most frequent cases are + that |a| != m, so we do those first. + */ + if((mag = s_mp_cmp(a, m)) > 0) { + if((res = mp_div(a, m, NULL, c)) != MP_OKAY) + return res; + + if(SIGN(c) == NEG) { + if((res = mp_add(c, m, c)) != MP_OKAY) + return res; + } + + } else if(mag < 0) { + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + if(mp_cmp_z(a) < 0) { + if((res = mp_add(c, m, c)) != MP_OKAY) + return res; + + } + + } else { + mp_zero(c); + + } + + return MP_OKAY; + +} /* end mp_mod() */ + +/* }}} */ + +/* {{{ mp_mod_d(a, d, c) */ + +/* + mp_mod_d(a, d, c) + + Compute c = a (mod d). Result will always be 0 <= c < d + */ +mp_err mp_mod_d(const mp_int *a, mp_digit d, mp_digit *c) +{ + mp_err res; + mp_digit rem; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if(s_mp_cmp_d(a, d) > 0) { + if((res = mp_div_d(a, d, NULL, &rem)) != MP_OKAY) + return res; + + } else { + if(SIGN(a) == NEG) + rem = d - DIGIT(a, 0); + else + rem = DIGIT(a, 0); + } + + if(c) + *c = rem; + + return MP_OKAY; + +} /* end mp_mod_d() */ + +/* }}} */ + +/* {{{ mp_sqrt(a, b) */ + +/* + mp_sqrt(a, b) + + Compute the integer square root of a, and store the result in b. + Uses an integer-arithmetic version of Newton's iterative linear + approximation technique to determine this value; the result has the + following two properties: + + b^2 <= a + (b+1)^2 >= a + + It is a range error to pass a negative value. + */ +mp_err mp_sqrt(const mp_int *a, mp_int *b) +{ + mp_int x, t; + mp_err res; + mp_size used; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + /* Cannot take square root of a negative value */ + if(SIGN(a) == NEG) + return MP_RANGE; + + /* Special cases for zero and one, trivial */ + if(mp_cmp_d(a, 1) <= 0) + return mp_copy(a, b); + + /* Initialize the temporaries we'll use below */ + if((res = mp_init_size(&t, USED(a), FLAG(a))) != MP_OKAY) + return res; + + /* Compute an initial guess for the iteration as a itself */ + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + used = MP_USED(&x); + if (used > 1) { + s_mp_rshd(&x, used / 2); + } + + for(;;) { + /* t = (x * x) - a */ + mp_copy(&x, &t); /* can't fail, t is big enough for original x */ + if((res = mp_sqr(&t, &t)) != MP_OKAY || + (res = mp_sub(&t, a, &t)) != MP_OKAY) + goto CLEANUP; + + /* t = t / 2x */ + s_mp_mul_2(&x); + if((res = mp_div(&t, &x, &t, NULL)) != MP_OKAY) + goto CLEANUP; + s_mp_div_2(&x); + + /* Terminate the loop, if the quotient is zero */ + if(mp_cmp_z(&t) == MP_EQ) + break; + + /* x = x - t */ + if((res = mp_sub(&x, &t, &x)) != MP_OKAY) + goto CLEANUP; + + } + + /* Copy result to output parameter */ + mp_sub_d(&x, 1, &x); + s_mp_exch(&x, b); + + CLEANUP: + mp_clear(&x); + X: + mp_clear(&t); + + return res; + +} /* end mp_sqrt() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Modular arithmetic */ + +#if MP_MODARITH +/* {{{ mp_addmod(a, b, m, c) */ + +/* + mp_addmod(a, b, m, c) + + Compute c = (a + b) mod m + */ + +mp_err mp_addmod(const mp_int *a, const mp_int *b, const mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_add(a, b, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} + +/* }}} */ + +/* {{{ mp_submod(a, b, m, c) */ + +/* + mp_submod(a, b, m, c) + + Compute c = (a - b) mod m + */ + +mp_err mp_submod(const mp_int *a, const mp_int *b, const mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_sub(a, b, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} + +/* }}} */ + +/* {{{ mp_mulmod(a, b, m, c) */ + +/* + mp_mulmod(a, b, m, c) + + Compute c = (a * b) mod m + */ + +mp_err mp_mulmod(const mp_int *a, const mp_int *b, const mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_mul(a, b, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} + +/* }}} */ + +/* {{{ mp_sqrmod(a, m, c) */ + +#if MP_SQUARE +mp_err mp_sqrmod(const mp_int *a, const mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_sqr(a, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} /* end mp_sqrmod() */ +#endif + +/* }}} */ + +/* {{{ s_mp_exptmod(a, b, m, c) */ + +/* + s_mp_exptmod(a, b, m, c) + + Compute c = (a ** b) mod m. Uses a standard square-and-multiply + method with modular reductions at each step. (This is basically the + same code as mp_expt(), except for the addition of the reductions) + + The modular reductions are done using Barrett's algorithm (see + s_mp_reduce() below for details) + */ + +mp_err s_mp_exptmod(const mp_int *a, const mp_int *b, const mp_int *m, mp_int *c) +{ + mp_int s, x, mu; + mp_err res; + mp_digit d; + int dig, bit; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(mp_cmp_z(b) < 0 || mp_cmp_z(m) <= 0) + return MP_RANGE; + + if((res = mp_init(&s, FLAG(a))) != MP_OKAY) + return res; + if((res = mp_init_copy(&x, a)) != MP_OKAY || + (res = mp_mod(&x, m, &x)) != MP_OKAY) + goto X; + if((res = mp_init(&mu, FLAG(a))) != MP_OKAY) + goto MU; + + mp_set(&s, 1); + + /* mu = b^2k / m */ + s_mp_add_d(&mu, 1); + s_mp_lshd(&mu, 2 * USED(m)); + if((res = mp_div(&mu, m, &mu, NULL)) != MP_OKAY) + goto CLEANUP; + + /* Loop over digits of b in ascending order, except highest order */ + for(dig = 0; dig < (USED(b) - 1); dig++) { + d = DIGIT(b, dig); + + /* Loop over the bits of the lower-order digits */ + for(bit = 0; bit < DIGIT_BIT; bit++) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&s, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&x, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + } + + /* Now do the last digit... */ + d = DIGIT(b, dig); + + while(d) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&s, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&x, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + + s_mp_exch(&s, c); + + CLEANUP: + mp_clear(&mu); + MU: + mp_clear(&x); + X: + mp_clear(&s); + + return res; + +} /* end s_mp_exptmod() */ + +/* }}} */ + +/* {{{ mp_exptmod_d(a, d, m, c) */ + +mp_err mp_exptmod_d(const mp_int *a, mp_digit d, const mp_int *m, mp_int *c) +{ + mp_int s, x; + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_init(&s, FLAG(a))) != MP_OKAY) + return res; + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + mp_set(&s, 1); + + while(d != 0) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY || + (res = mp_mod(&s, m, &s)) != MP_OKAY) + goto CLEANUP; + } + + d /= 2; + + if((res = s_mp_sqr(&x)) != MP_OKAY || + (res = mp_mod(&x, m, &x)) != MP_OKAY) + goto CLEANUP; + } + + s_mp_exch(&s, c); + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&s); + + return res; + +} /* end mp_exptmod_d() */ + +/* }}} */ +#endif /* if MP_MODARITH */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Comparison functions */ + +/* {{{ mp_cmp_z(a) */ + +/* + mp_cmp_z(a) + + Compare a <=> 0. Returns <0 if a<0, 0 if a=0, >0 if a>0. + */ + +int mp_cmp_z(const mp_int *a) +{ + if(SIGN(a) == NEG) + return MP_LT; + else if(USED(a) == 1 && DIGIT(a, 0) == 0) + return MP_EQ; + else + return MP_GT; + +} /* end mp_cmp_z() */ + +/* }}} */ + +/* {{{ mp_cmp_d(a, d) */ + +/* + mp_cmp_d(a, d) + + Compare a <=> d. Returns <0 if a<d, 0 if a=d, >0 if a>d + */ + +int mp_cmp_d(const mp_int *a, mp_digit d) +{ + ARGCHK(a != NULL, MP_EQ); + + if(SIGN(a) == NEG) + return MP_LT; + + return s_mp_cmp_d(a, d); + +} /* end mp_cmp_d() */ + +/* }}} */ + +/* {{{ mp_cmp(a, b) */ + +int mp_cmp(const mp_int *a, const mp_int *b) +{ + ARGCHK(a != NULL && b != NULL, MP_EQ); + + if(SIGN(a) == SIGN(b)) { + int mag; + + if((mag = s_mp_cmp(a, b)) == MP_EQ) + return MP_EQ; + + if(SIGN(a) == ZPOS) + return mag; + else + return -mag; + + } else if(SIGN(a) == ZPOS) { + return MP_GT; + } else { + return MP_LT; + } + +} /* end mp_cmp() */ + +/* }}} */ + +/* {{{ mp_cmp_mag(a, b) */ + +/* + mp_cmp_mag(a, b) + + Compares |a| <=> |b|, and returns an appropriate comparison result + */ + +int mp_cmp_mag(mp_int *a, mp_int *b) +{ + ARGCHK(a != NULL && b != NULL, MP_EQ); + + return s_mp_cmp(a, b); + +} /* end mp_cmp_mag() */ + +/* }}} */ + +/* {{{ mp_cmp_int(a, z, kmflag) */ + +/* + This just converts z to an mp_int, and uses the existing comparison + routines. This is sort of inefficient, but it's not clear to me how + frequently this wil get used anyway. For small positive constants, + you can always use mp_cmp_d(), and for zero, there is mp_cmp_z(). + */ +int mp_cmp_int(const mp_int *a, long z, int kmflag) +{ + mp_int tmp; + int out; + + ARGCHK(a != NULL, MP_EQ); + + mp_init(&tmp, kmflag); mp_set_int(&tmp, z); + out = mp_cmp(a, &tmp); + mp_clear(&tmp); + + return out; + +} /* end mp_cmp_int() */ + +/* }}} */ + +/* {{{ mp_isodd(a) */ + +/* + mp_isodd(a) + + Returns a true (non-zero) value if a is odd, false (zero) otherwise. + */ +int mp_isodd(const mp_int *a) +{ + ARGCHK(a != NULL, 0); + + return (int)(DIGIT(a, 0) & 1); + +} /* end mp_isodd() */ + +/* }}} */ + +/* {{{ mp_iseven(a) */ + +int mp_iseven(const mp_int *a) +{ + return !mp_isodd(a); + +} /* end mp_iseven() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Number theoretic functions */ + +#if MP_NUMTH +/* {{{ mp_gcd(a, b, c) */ + +/* + Like the old mp_gcd() function, except computes the GCD using the + binary algorithm due to Josef Stein in 1961 (via Knuth). + */ +mp_err mp_gcd(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + mp_int u, v, t; + mp_size k = 0; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(mp_cmp_z(a) == MP_EQ && mp_cmp_z(b) == MP_EQ) + return MP_RANGE; + if(mp_cmp_z(a) == MP_EQ) { + return mp_copy(b, c); + } else if(mp_cmp_z(b) == MP_EQ) { + return mp_copy(a, c); + } + + if((res = mp_init(&t, FLAG(a))) != MP_OKAY) + return res; + if((res = mp_init_copy(&u, a)) != MP_OKAY) + goto U; + if((res = mp_init_copy(&v, b)) != MP_OKAY) + goto V; + + SIGN(&u) = ZPOS; + SIGN(&v) = ZPOS; + + /* Divide out common factors of 2 until at least 1 of a, b is even */ + while(mp_iseven(&u) && mp_iseven(&v)) { + s_mp_div_2(&u); + s_mp_div_2(&v); + ++k; + } + + /* Initialize t */ + if(mp_isodd(&u)) { + if((res = mp_copy(&v, &t)) != MP_OKAY) + goto CLEANUP; + + /* t = -v */ + if(SIGN(&v) == ZPOS) + SIGN(&t) = NEG; + else + SIGN(&t) = ZPOS; + + } else { + if((res = mp_copy(&u, &t)) != MP_OKAY) + goto CLEANUP; + + } + + for(;;) { + while(mp_iseven(&t)) { + s_mp_div_2(&t); + } + + if(mp_cmp_z(&t) == MP_GT) { + if((res = mp_copy(&t, &u)) != MP_OKAY) + goto CLEANUP; + + } else { + if((res = mp_copy(&t, &v)) != MP_OKAY) + goto CLEANUP; + + /* v = -t */ + if(SIGN(&t) == ZPOS) + SIGN(&v) = NEG; + else + SIGN(&v) = ZPOS; + } + + if((res = mp_sub(&u, &v, &t)) != MP_OKAY) + goto CLEANUP; + + if(s_mp_cmp_d(&t, 0) == MP_EQ) + break; + } + + s_mp_2expt(&v, k); /* v = 2^k */ + res = mp_mul(&u, &v, c); /* c = u * v */ + + CLEANUP: + mp_clear(&v); + V: + mp_clear(&u); + U: + mp_clear(&t); + + return res; + +} /* end mp_gcd() */ + +/* }}} */ + +/* {{{ mp_lcm(a, b, c) */ + +/* We compute the least common multiple using the rule: + + ab = [a, b](a, b) + + ... by computing the product, and dividing out the gcd. + */ + +mp_err mp_lcm(mp_int *a, mp_int *b, mp_int *c) +{ + mp_int gcd, prod; + mp_err res; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + /* Set up temporaries */ + if((res = mp_init(&gcd, FLAG(a))) != MP_OKAY) + return res; + if((res = mp_init(&prod, FLAG(a))) != MP_OKAY) + goto GCD; + + if((res = mp_mul(a, b, &prod)) != MP_OKAY) + goto CLEANUP; + if((res = mp_gcd(a, b, &gcd)) != MP_OKAY) + goto CLEANUP; + + res = mp_div(&prod, &gcd, c, NULL); + + CLEANUP: + mp_clear(&prod); + GCD: + mp_clear(&gcd); + + return res; + +} /* end mp_lcm() */ + +/* }}} */ + +/* {{{ mp_xgcd(a, b, g, x, y) */ + +/* + mp_xgcd(a, b, g, x, y) + + Compute g = (a, b) and values x and y satisfying Bezout's identity + (that is, ax + by = g). This uses the binary extended GCD algorithm + based on the Stein algorithm used for mp_gcd() + See algorithm 14.61 in Handbook of Applied Cryptogrpahy. + */ + +mp_err mp_xgcd(const mp_int *a, const mp_int *b, mp_int *g, mp_int *x, mp_int *y) +{ + mp_int gx, xc, yc, u, v, A, B, C, D; + mp_int *clean[9]; + mp_err res; + int last = -1; + + if(mp_cmp_z(b) == 0) + return MP_RANGE; + + /* Initialize all these variables we need */ + MP_CHECKOK( mp_init(&u, FLAG(a)) ); + clean[++last] = &u; + MP_CHECKOK( mp_init(&v, FLAG(a)) ); + clean[++last] = &v; + MP_CHECKOK( mp_init(&gx, FLAG(a)) ); + clean[++last] = &gx; + MP_CHECKOK( mp_init(&A, FLAG(a)) ); + clean[++last] = &A; + MP_CHECKOK( mp_init(&B, FLAG(a)) ); + clean[++last] = &B; + MP_CHECKOK( mp_init(&C, FLAG(a)) ); + clean[++last] = &C; + MP_CHECKOK( mp_init(&D, FLAG(a)) ); + clean[++last] = &D; + MP_CHECKOK( mp_init_copy(&xc, a) ); + clean[++last] = &xc; + mp_abs(&xc, &xc); + MP_CHECKOK( mp_init_copy(&yc, b) ); + clean[++last] = &yc; + mp_abs(&yc, &yc); + + mp_set(&gx, 1); + + /* Divide by two until at least one of them is odd */ + while(mp_iseven(&xc) && mp_iseven(&yc)) { + mp_size nx = mp_trailing_zeros(&xc); + mp_size ny = mp_trailing_zeros(&yc); + mp_size n = MP_MIN(nx, ny); + s_mp_div_2d(&xc,n); + s_mp_div_2d(&yc,n); + MP_CHECKOK( s_mp_mul_2d(&gx,n) ); + } + + mp_copy(&xc, &u); + mp_copy(&yc, &v); + mp_set(&A, 1); mp_set(&D, 1); + + /* Loop through binary GCD algorithm */ + do { + while(mp_iseven(&u)) { + s_mp_div_2(&u); + + if(mp_iseven(&A) && mp_iseven(&B)) { + s_mp_div_2(&A); s_mp_div_2(&B); + } else { + MP_CHECKOK( mp_add(&A, &yc, &A) ); + s_mp_div_2(&A); + MP_CHECKOK( mp_sub(&B, &xc, &B) ); + s_mp_div_2(&B); + } + } + + while(mp_iseven(&v)) { + s_mp_div_2(&v); + + if(mp_iseven(&C) && mp_iseven(&D)) { + s_mp_div_2(&C); s_mp_div_2(&D); + } else { + MP_CHECKOK( mp_add(&C, &yc, &C) ); + s_mp_div_2(&C); + MP_CHECKOK( mp_sub(&D, &xc, &D) ); + s_mp_div_2(&D); + } + } + + if(mp_cmp(&u, &v) >= 0) { + MP_CHECKOK( mp_sub(&u, &v, &u) ); + MP_CHECKOK( mp_sub(&A, &C, &A) ); + MP_CHECKOK( mp_sub(&B, &D, &B) ); + } else { + MP_CHECKOK( mp_sub(&v, &u, &v) ); + MP_CHECKOK( mp_sub(&C, &A, &C) ); + MP_CHECKOK( mp_sub(&D, &B, &D) ); + } + } while (mp_cmp_z(&u) != 0); + + /* copy results to output */ + if(x) + MP_CHECKOK( mp_copy(&C, x) ); + + if(y) + MP_CHECKOK( mp_copy(&D, y) ); + + if(g) + MP_CHECKOK( mp_mul(&gx, &v, g) ); + + CLEANUP: + while(last >= 0) + mp_clear(clean[last--]); + + return res; + +} /* end mp_xgcd() */ + +/* }}} */ + +mp_size mp_trailing_zeros(const mp_int *mp) +{ + mp_digit d; + mp_size n = 0; + int ix; + + if (!mp || !MP_DIGITS(mp) || !mp_cmp_z(mp)) + return n; + + for (ix = 0; !(d = MP_DIGIT(mp,ix)) && (ix < MP_USED(mp)); ++ix) + n += MP_DIGIT_BIT; + if (!d) + return 0; /* shouldn't happen, but ... */ +#if !defined(MP_USE_UINT_DIGIT) + if (!(d & 0xffffffffU)) { + d >>= 32; + n += 32; + } +#endif + if (!(d & 0xffffU)) { + d >>= 16; + n += 16; + } + if (!(d & 0xffU)) { + d >>= 8; + n += 8; + } + if (!(d & 0xfU)) { + d >>= 4; + n += 4; + } + if (!(d & 0x3U)) { + d >>= 2; + n += 2; + } + if (!(d & 0x1U)) { + d >>= 1; + n += 1; + } +#if MP_ARGCHK == 2 + assert(0 != (d & 1)); +#endif + return n; +} + +/* Given a and prime p, computes c and k such that a*c == 2**k (mod p). +** Returns k (positive) or error (negative). +** This technique from the paper "Fast Modular Reciprocals" (unpublished) +** by Richard Schroeppel (a.k.a. Captain Nemo). +*/ +mp_err s_mp_almost_inverse(const mp_int *a, const mp_int *p, mp_int *c) +{ + mp_err res; + mp_err k = 0; + mp_int d, f, g; + + ARGCHK(a && p && c, MP_BADARG); + + MP_DIGITS(&d) = 0; + MP_DIGITS(&f) = 0; + MP_DIGITS(&g) = 0; + MP_CHECKOK( mp_init(&d, FLAG(a)) ); + MP_CHECKOK( mp_init_copy(&f, a) ); /* f = a */ + MP_CHECKOK( mp_init_copy(&g, p) ); /* g = p */ + + mp_set(c, 1); + mp_zero(&d); + + if (mp_cmp_z(&f) == 0) { + res = MP_UNDEF; + } else + for (;;) { + int diff_sign; + while (mp_iseven(&f)) { + mp_size n = mp_trailing_zeros(&f); + if (!n) { + res = MP_UNDEF; + goto CLEANUP; + } + s_mp_div_2d(&f, n); + MP_CHECKOK( s_mp_mul_2d(&d, n) ); + k += n; + } + if (mp_cmp_d(&f, 1) == MP_EQ) { /* f == 1 */ + res = k; + break; + } + diff_sign = mp_cmp(&f, &g); + if (diff_sign < 0) { /* f < g */ + s_mp_exch(&f, &g); + s_mp_exch(c, &d); + } else if (diff_sign == 0) { /* f == g */ + res = MP_UNDEF; /* a and p are not relatively prime */ + break; + } + if ((MP_DIGIT(&f,0) % 4) == (MP_DIGIT(&g,0) % 4)) { + MP_CHECKOK( mp_sub(&f, &g, &f) ); /* f = f - g */ + MP_CHECKOK( mp_sub(c, &d, c) ); /* c = c - d */ + } else { + MP_CHECKOK( mp_add(&f, &g, &f) ); /* f = f + g */ + MP_CHECKOK( mp_add(c, &d, c) ); /* c = c + d */ + } + } + if (res >= 0) { + while (MP_SIGN(c) != MP_ZPOS) { + MP_CHECKOK( mp_add(c, p, c) ); + } + res = k; + } + +CLEANUP: + mp_clear(&d); + mp_clear(&f); + mp_clear(&g); + return res; +} + +/* Compute T = (P ** -1) mod MP_RADIX. Also works for 16-bit mp_digits. +** This technique from the paper "Fast Modular Reciprocals" (unpublished) +** by Richard Schroeppel (a.k.a. Captain Nemo). +*/ +mp_digit s_mp_invmod_radix(mp_digit P) +{ + mp_digit T = P; + T *= 2 - (P * T); + T *= 2 - (P * T); + T *= 2 - (P * T); + T *= 2 - (P * T); +#if !defined(MP_USE_UINT_DIGIT) + T *= 2 - (P * T); + T *= 2 - (P * T); +#endif + return T; +} + +/* Given c, k, and prime p, where a*c == 2**k (mod p), +** Compute x = (a ** -1) mod p. This is similar to Montgomery reduction. +** This technique from the paper "Fast Modular Reciprocals" (unpublished) +** by Richard Schroeppel (a.k.a. Captain Nemo). +*/ +mp_err s_mp_fixup_reciprocal(const mp_int *c, const mp_int *p, int k, mp_int *x) +{ + int k_orig = k; + mp_digit r; + mp_size ix; + mp_err res; + + if (mp_cmp_z(c) < 0) { /* c < 0 */ + MP_CHECKOK( mp_add(c, p, x) ); /* x = c + p */ + } else { + MP_CHECKOK( mp_copy(c, x) ); /* x = c */ + } + + /* make sure x is large enough */ + ix = MP_HOWMANY(k, MP_DIGIT_BIT) + MP_USED(p) + 1; + ix = MP_MAX(ix, MP_USED(x)); + MP_CHECKOK( s_mp_pad(x, ix) ); + + r = 0 - s_mp_invmod_radix(MP_DIGIT(p,0)); + + for (ix = 0; k > 0; ix++) { + int j = MP_MIN(k, MP_DIGIT_BIT); + mp_digit v = r * MP_DIGIT(x, ix); + if (j < MP_DIGIT_BIT) { + v &= ((mp_digit)1 << j) - 1; /* v = v mod (2 ** j) */ + } + s_mp_mul_d_add_offset(p, v, x, ix); /* x += p * v * (RADIX ** ix) */ + k -= j; + } + s_mp_clamp(x); + s_mp_div_2d(x, k_orig); + res = MP_OKAY; + +CLEANUP: + return res; +} + +/* compute mod inverse using Schroeppel's method, only if m is odd */ +mp_err s_mp_invmod_odd_m(const mp_int *a, const mp_int *m, mp_int *c) +{ + int k; + mp_err res; + mp_int x; + + ARGCHK(a && m && c, MP_BADARG); + + if(mp_cmp_z(a) == 0 || mp_cmp_z(m) == 0) + return MP_RANGE; + if (mp_iseven(m)) + return MP_UNDEF; + + MP_DIGITS(&x) = 0; + + if (a == c) { + if ((res = mp_init_copy(&x, a)) != MP_OKAY) + return res; + if (a == m) + m = &x; + a = &x; + } else if (m == c) { + if ((res = mp_init_copy(&x, m)) != MP_OKAY) + return res; + m = &x; + } else { + MP_DIGITS(&x) = 0; + } + + MP_CHECKOK( s_mp_almost_inverse(a, m, c) ); + k = res; + MP_CHECKOK( s_mp_fixup_reciprocal(c, m, k, c) ); +CLEANUP: + mp_clear(&x); + return res; +} + +/* Known good algorithm for computing modular inverse. But slow. */ +mp_err mp_invmod_xgcd(const mp_int *a, const mp_int *m, mp_int *c) +{ + mp_int g, x; + mp_err res; + + ARGCHK(a && m && c, MP_BADARG); + + if(mp_cmp_z(a) == 0 || mp_cmp_z(m) == 0) + return MP_RANGE; + + MP_DIGITS(&g) = 0; + MP_DIGITS(&x) = 0; + MP_CHECKOK( mp_init(&x, FLAG(a)) ); + MP_CHECKOK( mp_init(&g, FLAG(a)) ); + + MP_CHECKOK( mp_xgcd(a, m, &g, &x, NULL) ); + + if (mp_cmp_d(&g, 1) != MP_EQ) { + res = MP_UNDEF; + goto CLEANUP; + } + + res = mp_mod(&x, m, c); + SIGN(c) = SIGN(a); + +CLEANUP: + mp_clear(&x); + mp_clear(&g); + + return res; +} + +/* modular inverse where modulus is 2**k. */ +/* c = a**-1 mod 2**k */ +mp_err s_mp_invmod_2d(const mp_int *a, mp_size k, mp_int *c) +{ + mp_err res; + mp_size ix = k + 4; + mp_int t0, t1, val, tmp, two2k; + + static const mp_digit d2 = 2; + static const mp_int two = { 0, MP_ZPOS, 1, 1, (mp_digit *)&d2 }; + + if (mp_iseven(a)) + return MP_UNDEF; + if (k <= MP_DIGIT_BIT) { + mp_digit i = s_mp_invmod_radix(MP_DIGIT(a,0)); + if (k < MP_DIGIT_BIT) + i &= ((mp_digit)1 << k) - (mp_digit)1; + mp_set(c, i); + return MP_OKAY; + } + MP_DIGITS(&t0) = 0; + MP_DIGITS(&t1) = 0; + MP_DIGITS(&val) = 0; + MP_DIGITS(&tmp) = 0; + MP_DIGITS(&two2k) = 0; + MP_CHECKOK( mp_init_copy(&val, a) ); + s_mp_mod_2d(&val, k); + MP_CHECKOK( mp_init_copy(&t0, &val) ); + MP_CHECKOK( mp_init_copy(&t1, &t0) ); + MP_CHECKOK( mp_init(&tmp, FLAG(a)) ); + MP_CHECKOK( mp_init(&two2k, FLAG(a)) ); + MP_CHECKOK( s_mp_2expt(&two2k, k) ); + do { + MP_CHECKOK( mp_mul(&val, &t1, &tmp) ); + MP_CHECKOK( mp_sub(&two, &tmp, &tmp) ); + MP_CHECKOK( mp_mul(&t1, &tmp, &t1) ); + s_mp_mod_2d(&t1, k); + while (MP_SIGN(&t1) != MP_ZPOS) { + MP_CHECKOK( mp_add(&t1, &two2k, &t1) ); + } + if (mp_cmp(&t1, &t0) == MP_EQ) + break; + MP_CHECKOK( mp_copy(&t1, &t0) ); + } while (--ix > 0); + if (!ix) { + res = MP_UNDEF; + } else { + mp_exch(c, &t1); + } + +CLEANUP: + mp_clear(&t0); + mp_clear(&t1); + mp_clear(&val); + mp_clear(&tmp); + mp_clear(&two2k); + return res; +} + +mp_err s_mp_invmod_even_m(const mp_int *a, const mp_int *m, mp_int *c) +{ + mp_err res; + mp_size k; + mp_int oddFactor, evenFactor; /* factors of the modulus */ + mp_int oddPart, evenPart; /* parts to combine via CRT. */ + mp_int C2, tmp1, tmp2; + + /*static const mp_digit d1 = 1; */ + /*static const mp_int one = { MP_ZPOS, 1, 1, (mp_digit *)&d1 }; */ + + if ((res = s_mp_ispow2(m)) >= 0) { + k = res; + return s_mp_invmod_2d(a, k, c); + } + MP_DIGITS(&oddFactor) = 0; + MP_DIGITS(&evenFactor) = 0; + MP_DIGITS(&oddPart) = 0; + MP_DIGITS(&evenPart) = 0; + MP_DIGITS(&C2) = 0; + MP_DIGITS(&tmp1) = 0; + MP_DIGITS(&tmp2) = 0; + + MP_CHECKOK( mp_init_copy(&oddFactor, m) ); /* oddFactor = m */ + MP_CHECKOK( mp_init(&evenFactor, FLAG(m)) ); + MP_CHECKOK( mp_init(&oddPart, FLAG(m)) ); + MP_CHECKOK( mp_init(&evenPart, FLAG(m)) ); + MP_CHECKOK( mp_init(&C2, FLAG(m)) ); + MP_CHECKOK( mp_init(&tmp1, FLAG(m)) ); + MP_CHECKOK( mp_init(&tmp2, FLAG(m)) ); + + k = mp_trailing_zeros(m); + s_mp_div_2d(&oddFactor, k); + MP_CHECKOK( s_mp_2expt(&evenFactor, k) ); + + /* compute a**-1 mod oddFactor. */ + MP_CHECKOK( s_mp_invmod_odd_m(a, &oddFactor, &oddPart) ); + /* compute a**-1 mod evenFactor, where evenFactor == 2**k. */ + MP_CHECKOK( s_mp_invmod_2d( a, k, &evenPart) ); + + /* Use Chinese Remainer theorem to compute a**-1 mod m. */ + /* let m1 = oddFactor, v1 = oddPart, + * let m2 = evenFactor, v2 = evenPart. + */ + + /* Compute C2 = m1**-1 mod m2. */ + MP_CHECKOK( s_mp_invmod_2d(&oddFactor, k, &C2) ); + + /* compute u = (v2 - v1)*C2 mod m2 */ + MP_CHECKOK( mp_sub(&evenPart, &oddPart, &tmp1) ); + MP_CHECKOK( mp_mul(&tmp1, &C2, &tmp2) ); + s_mp_mod_2d(&tmp2, k); + while (MP_SIGN(&tmp2) != MP_ZPOS) { + MP_CHECKOK( mp_add(&tmp2, &evenFactor, &tmp2) ); + } + + /* compute answer = v1 + u*m1 */ + MP_CHECKOK( mp_mul(&tmp2, &oddFactor, c) ); + MP_CHECKOK( mp_add(&oddPart, c, c) ); + /* not sure this is necessary, but it's low cost if not. */ + MP_CHECKOK( mp_mod(c, m, c) ); + +CLEANUP: + mp_clear(&oddFactor); + mp_clear(&evenFactor); + mp_clear(&oddPart); + mp_clear(&evenPart); + mp_clear(&C2); + mp_clear(&tmp1); + mp_clear(&tmp2); + return res; +} + + +/* {{{ mp_invmod(a, m, c) */ + +/* + mp_invmod(a, m, c) + + Compute c = a^-1 (mod m), if there is an inverse for a (mod m). + This is equivalent to the question of whether (a, m) = 1. If not, + MP_UNDEF is returned, and there is no inverse. + */ + +mp_err mp_invmod(const mp_int *a, const mp_int *m, mp_int *c) +{ + + ARGCHK(a && m && c, MP_BADARG); + + if(mp_cmp_z(a) == 0 || mp_cmp_z(m) == 0) + return MP_RANGE; + + if (mp_isodd(m)) { + return s_mp_invmod_odd_m(a, m, c); + } + if (mp_iseven(a)) + return MP_UNDEF; /* not invertable */ + + return s_mp_invmod_even_m(a, m, c); + +} /* end mp_invmod() */ + +/* }}} */ +#endif /* if MP_NUMTH */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ mp_print(mp, ofp) */ + +#if MP_IOFUNC +/* + mp_print(mp, ofp) + + Print a textual representation of the given mp_int on the output + stream 'ofp'. Output is generated using the internal radix. + */ + +void mp_print(mp_int *mp, FILE *ofp) +{ + int ix; + + if(mp == NULL || ofp == NULL) + return; + + fputc((SIGN(mp) == NEG) ? '-' : '+', ofp); + + for(ix = USED(mp) - 1; ix >= 0; ix--) { + fprintf(ofp, DIGIT_FMT, DIGIT(mp, ix)); + } + +} /* end mp_print() */ + +#endif /* if MP_IOFUNC */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ More I/O Functions */ + +/* {{{ mp_read_raw(mp, str, len) */ + +/* + mp_read_raw(mp, str, len) + + Read in a raw value (base 256) into the given mp_int + */ + +mp_err mp_read_raw(mp_int *mp, char *str, int len) +{ + int ix; + mp_err res; + unsigned char *ustr = (unsigned char *)str; + + ARGCHK(mp != NULL && str != NULL && len > 0, MP_BADARG); + + mp_zero(mp); + + /* Get sign from first byte */ + if(ustr[0]) + SIGN(mp) = NEG; + else + SIGN(mp) = ZPOS; + + /* Read the rest of the digits */ + for(ix = 1; ix < len; ix++) { + if((res = mp_mul_d(mp, 256, mp)) != MP_OKAY) + return res; + if((res = mp_add_d(mp, ustr[ix], mp)) != MP_OKAY) + return res; + } + + return MP_OKAY; + +} /* end mp_read_raw() */ + +/* }}} */ + +/* {{{ mp_raw_size(mp) */ + +int mp_raw_size(mp_int *mp) +{ + ARGCHK(mp != NULL, 0); + + return (USED(mp) * sizeof(mp_digit)) + 1; + +} /* end mp_raw_size() */ + +/* }}} */ + +/* {{{ mp_toraw(mp, str) */ + +mp_err mp_toraw(mp_int *mp, char *str) +{ + int ix, jx, pos = 1; + + ARGCHK(mp != NULL && str != NULL, MP_BADARG); + + str[0] = (char)SIGN(mp); + + /* Iterate over each digit... */ + for(ix = USED(mp) - 1; ix >= 0; ix--) { + mp_digit d = DIGIT(mp, ix); + + /* Unpack digit bytes, high order first */ + for(jx = sizeof(mp_digit) - 1; jx >= 0; jx--) { + str[pos++] = (char)(d >> (jx * CHAR_BIT)); + } + } + + return MP_OKAY; + +} /* end mp_toraw() */ + +/* }}} */ + +/* {{{ mp_read_radix(mp, str, radix) */ + +/* + mp_read_radix(mp, str, radix) + + Read an integer from the given string, and set mp to the resulting + value. The input is presumed to be in base 10. Leading non-digit + characters are ignored, and the function reads until a non-digit + character or the end of the string. + */ + +mp_err mp_read_radix(mp_int *mp, const char *str, int radix) +{ + int ix = 0, val = 0; + mp_err res; + mp_sign sig = ZPOS; + + ARGCHK(mp != NULL && str != NULL && radix >= 2 && radix <= MAX_RADIX, + MP_BADARG); + + mp_zero(mp); + + /* Skip leading non-digit characters until a digit or '-' or '+' */ + while(str[ix] && + (s_mp_tovalue(str[ix], radix) < 0) && + str[ix] != '-' && + str[ix] != '+') { + ++ix; + } + + if(str[ix] == '-') { + sig = NEG; + ++ix; + } else if(str[ix] == '+') { + sig = ZPOS; /* this is the default anyway... */ + ++ix; + } + + while((val = s_mp_tovalue(str[ix], radix)) >= 0) { + if((res = s_mp_mul_d(mp, radix)) != MP_OKAY) + return res; + if((res = s_mp_add_d(mp, val)) != MP_OKAY) + return res; + ++ix; + } + + if(s_mp_cmp_d(mp, 0) == MP_EQ) + SIGN(mp) = ZPOS; + else + SIGN(mp) = sig; + + return MP_OKAY; + +} /* end mp_read_radix() */ + +mp_err mp_read_variable_radix(mp_int *a, const char * str, int default_radix) +{ + int radix = default_radix; + int cx; + mp_sign sig = ZPOS; + mp_err res; + + /* Skip leading non-digit characters until a digit or '-' or '+' */ + while ((cx = *str) != 0 && + (s_mp_tovalue(cx, radix) < 0) && + cx != '-' && + cx != '+') { + ++str; + } + + if (cx == '-') { + sig = NEG; + ++str; + } else if (cx == '+') { + sig = ZPOS; /* this is the default anyway... */ + ++str; + } + + if (str[0] == '0') { + if ((str[1] | 0x20) == 'x') { + radix = 16; + str += 2; + } else { + radix = 8; + str++; + } + } + res = mp_read_radix(a, str, radix); + if (res == MP_OKAY) { + MP_SIGN(a) = (s_mp_cmp_d(a, 0) == MP_EQ) ? ZPOS : sig; + } + return res; +} + +/* }}} */ + +/* {{{ mp_radix_size(mp, radix) */ + +int mp_radix_size(mp_int *mp, int radix) +{ + int bits; + + if(!mp || radix < 2 || radix > MAX_RADIX) + return 0; + + bits = USED(mp) * DIGIT_BIT - 1; + + return s_mp_outlen(bits, radix); + +} /* end mp_radix_size() */ + +/* }}} */ + +/* {{{ mp_toradix(mp, str, radix) */ + +mp_err mp_toradix(mp_int *mp, char *str, int radix) +{ + int ix, pos = 0; + + ARGCHK(mp != NULL && str != NULL, MP_BADARG); + ARGCHK(radix > 1 && radix <= MAX_RADIX, MP_RANGE); + + if(mp_cmp_z(mp) == MP_EQ) { + str[0] = '0'; + str[1] = '\0'; + } else { + mp_err res; + mp_int tmp; + mp_sign sgn; + mp_digit rem, rdx = (mp_digit)radix; + char ch; + + if((res = mp_init_copy(&tmp, mp)) != MP_OKAY) + return res; + + /* Save sign for later, and take absolute value */ + sgn = SIGN(&tmp); SIGN(&tmp) = ZPOS; + + /* Generate output digits in reverse order */ + while(mp_cmp_z(&tmp) != 0) { + if((res = mp_div_d(&tmp, rdx, &tmp, &rem)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + /* Generate digits, use capital letters */ + ch = s_mp_todigit(rem, radix, 0); + + str[pos++] = ch; + } + + /* Add - sign if original value was negative */ + if(sgn == NEG) + str[pos++] = '-'; + + /* Add trailing NUL to end the string */ + str[pos--] = '\0'; + + /* Reverse the digits and sign indicator */ + ix = 0; + while(ix < pos) { + char tmp = str[ix]; + + str[ix] = str[pos]; + str[pos] = tmp; + ++ix; + --pos; + } + + mp_clear(&tmp); + } + + return MP_OKAY; + +} /* end mp_toradix() */ + +/* }}} */ + +/* {{{ mp_tovalue(ch, r) */ + +int mp_tovalue(char ch, int r) +{ + return s_mp_tovalue(ch, r); + +} /* end mp_tovalue() */ + +/* }}} */ + +/* }}} */ + +/* {{{ mp_strerror(ec) */ + +/* + mp_strerror(ec) + + Return a string describing the meaning of error code 'ec'. The + string returned is allocated in static memory, so the caller should + not attempt to modify or free the memory associated with this + string. + */ +const char *mp_strerror(mp_err ec) +{ + int aec = (ec < 0) ? -ec : ec; + + /* Code values are negative, so the senses of these comparisons + are accurate */ + if(ec < MP_LAST_CODE || ec > MP_OKAY) { + return mp_err_string[0]; /* unknown error code */ + } else { + return mp_err_string[aec + 1]; + } + +} /* end mp_strerror() */ + +/* }}} */ + +/*========================================================================*/ +/*------------------------------------------------------------------------*/ +/* Static function definitions (internal use only) */ + +/* {{{ Memory management */ + +/* {{{ s_mp_grow(mp, min) */ + +/* Make sure there are at least 'min' digits allocated to mp */ +mp_err s_mp_grow(mp_int *mp, mp_size min) +{ + if(min > ALLOC(mp)) { + mp_digit *tmp; + + /* Set min to next nearest default precision block size */ + min = MP_ROUNDUP(min, s_mp_defprec); + + if((tmp = s_mp_alloc(min, sizeof(mp_digit), FLAG(mp))) == NULL) + return MP_MEM; + + s_mp_copy(DIGITS(mp), tmp, USED(mp)); + +#if MP_CRYPTO + s_mp_setz(DIGITS(mp), ALLOC(mp)); +#endif + s_mp_free(DIGITS(mp), ALLOC(mp)); + DIGITS(mp) = tmp; + ALLOC(mp) = min; + } + + return MP_OKAY; + +} /* end s_mp_grow() */ + +/* }}} */ + +/* {{{ s_mp_pad(mp, min) */ + +/* Make sure the used size of mp is at least 'min', growing if needed */ +mp_err s_mp_pad(mp_int *mp, mp_size min) +{ + if(min > USED(mp)) { + mp_err res; + + /* Make sure there is room to increase precision */ + if (min > ALLOC(mp)) { + if ((res = s_mp_grow(mp, min)) != MP_OKAY) + return res; + } else { + s_mp_setz(DIGITS(mp) + USED(mp), min - USED(mp)); + } + + /* Increase precision; should already be 0-filled */ + USED(mp) = min; + } + + return MP_OKAY; + +} /* end s_mp_pad() */ + +/* }}} */ + +/* {{{ s_mp_setz(dp, count) */ + +#if MP_MACRO == 0 +/* Set 'count' digits pointed to by dp to be zeroes */ +void s_mp_setz(mp_digit *dp, mp_size count) +{ +#if MP_MEMSET == 0 + int ix; + + for(ix = 0; ix < count; ix++) + dp[ix] = 0; +#else + memset(dp, 0, count * sizeof(mp_digit)); +#endif + +} /* end s_mp_setz() */ +#endif + +/* }}} */ + +/* {{{ s_mp_copy(sp, dp, count) */ + +#if MP_MACRO == 0 +/* Copy 'count' digits from sp to dp */ +void s_mp_copy(const mp_digit *sp, mp_digit *dp, mp_size count) +{ +#if MP_MEMCPY == 0 + int ix; + + for(ix = 0; ix < count; ix++) + dp[ix] = sp[ix]; +#else + memcpy(dp, sp, count * sizeof(mp_digit)); +#endif + +} /* end s_mp_copy() */ +#endif + +/* }}} */ + +/* {{{ s_mp_alloc(nb, ni, kmflag) */ + +#if MP_MACRO == 0 +/* Allocate ni records of nb bytes each, and return a pointer to that */ +void *s_mp_alloc(size_t nb, size_t ni, int kmflag) +{ + mp_int *mp; + ++mp_allocs; +#ifdef _KERNEL + mp = kmem_zalloc(nb * ni, kmflag); + if (mp != NULL) + FLAG(mp) = kmflag; + return (mp); +#else + return calloc(nb, ni); +#endif + +} /* end s_mp_alloc() */ +#endif + +/* }}} */ + +/* {{{ s_mp_free(ptr) */ + +#if MP_MACRO == 0 +/* Free the memory pointed to by ptr */ +void s_mp_free(void *ptr, mp_size alloc) +{ + if(ptr) { + ++mp_frees; +#ifdef _KERNEL + kmem_free(ptr, alloc * sizeof (mp_digit)); +#else + free(ptr); +#endif + } +} /* end s_mp_free() */ +#endif + +/* }}} */ + +/* {{{ s_mp_clamp(mp) */ + +#if MP_MACRO == 0 +/* Remove leading zeroes from the given value */ +void s_mp_clamp(mp_int *mp) +{ + mp_size used = MP_USED(mp); + while (used > 1 && DIGIT(mp, used - 1) == 0) + --used; + MP_USED(mp) = used; +} /* end s_mp_clamp() */ +#endif + +/* }}} */ + +/* {{{ s_mp_exch(a, b) */ + +/* Exchange the data for a and b; (b, a) = (a, b) */ +void s_mp_exch(mp_int *a, mp_int *b) +{ + mp_int tmp; + + tmp = *a; + *a = *b; + *b = tmp; + +} /* end s_mp_exch() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Arithmetic helpers */ + +/* {{{ s_mp_lshd(mp, p) */ + +/* + Shift mp leftward by p digits, growing if needed, and zero-filling + the in-shifted digits at the right end. This is a convenient + alternative to multiplication by powers of the radix + The value of USED(mp) must already have been set to the value for + the shifted result. + */ + +mp_err s_mp_lshd(mp_int *mp, mp_size p) +{ + mp_err res; + mp_size pos; + int ix; + + if(p == 0) + return MP_OKAY; + + if (MP_USED(mp) == 1 && MP_DIGIT(mp, 0) == 0) + return MP_OKAY; + + if((res = s_mp_pad(mp, USED(mp) + p)) != MP_OKAY) + return res; + + pos = USED(mp) - 1; + + /* Shift all the significant figures over as needed */ + for(ix = pos - p; ix >= 0; ix--) + DIGIT(mp, ix + p) = DIGIT(mp, ix); + + /* Fill the bottom digits with zeroes */ + for(ix = 0; ix < p; ix++) + DIGIT(mp, ix) = 0; + + return MP_OKAY; + +} /* end s_mp_lshd() */ + +/* }}} */ + +/* {{{ s_mp_mul_2d(mp, d) */ + +/* + Multiply the integer by 2^d, where d is a number of bits. This + amounts to a bitwise shift of the value. + */ +mp_err s_mp_mul_2d(mp_int *mp, mp_digit d) +{ + mp_err res; + mp_digit dshift, bshift; + mp_digit mask; + + ARGCHK(mp != NULL, MP_BADARG); + + dshift = d / MP_DIGIT_BIT; + bshift = d % MP_DIGIT_BIT; + /* bits to be shifted out of the top word */ + mask = ((mp_digit)~0 << (MP_DIGIT_BIT - bshift)); + mask &= MP_DIGIT(mp, MP_USED(mp) - 1); + + if (MP_OKAY != (res = s_mp_pad(mp, MP_USED(mp) + dshift + (mask != 0) ))) + return res; + + if (dshift && MP_OKAY != (res = s_mp_lshd(mp, dshift))) + return res; + + if (bshift) { + mp_digit *pa = MP_DIGITS(mp); + mp_digit *alim = pa + MP_USED(mp); + mp_digit prev = 0; + + for (pa += dshift; pa < alim; ) { + mp_digit x = *pa; + *pa++ = (x << bshift) | prev; + prev = x >> (DIGIT_BIT - bshift); + } + } + + s_mp_clamp(mp); + return MP_OKAY; +} /* end s_mp_mul_2d() */ + +/* {{{ s_mp_rshd(mp, p) */ + +/* + Shift mp rightward by p digits. Maintains the invariant that + digits above the precision are all zero. Digits shifted off the + end are lost. Cannot fail. + */ + +void s_mp_rshd(mp_int *mp, mp_size p) +{ + mp_size ix; + mp_digit *src, *dst; + + if(p == 0) + return; + + /* Shortcut when all digits are to be shifted off */ + if(p >= USED(mp)) { + s_mp_setz(DIGITS(mp), ALLOC(mp)); + USED(mp) = 1; + SIGN(mp) = ZPOS; + return; + } + + /* Shift all the significant figures over as needed */ + dst = MP_DIGITS(mp); + src = dst + p; + for (ix = USED(mp) - p; ix > 0; ix--) + *dst++ = *src++; + + MP_USED(mp) -= p; + /* Fill the top digits with zeroes */ + while (p-- > 0) + *dst++ = 0; + +#if 0 + /* Strip off any leading zeroes */ + s_mp_clamp(mp); +#endif + +} /* end s_mp_rshd() */ + +/* }}} */ + +/* {{{ s_mp_div_2(mp) */ + +/* Divide by two -- take advantage of radix properties to do it fast */ +void s_mp_div_2(mp_int *mp) +{ + s_mp_div_2d(mp, 1); + +} /* end s_mp_div_2() */ + +/* }}} */ + +/* {{{ s_mp_mul_2(mp) */ + +mp_err s_mp_mul_2(mp_int *mp) +{ + mp_digit *pd; + int ix, used; + mp_digit kin = 0; + + /* Shift digits leftward by 1 bit */ + used = MP_USED(mp); + pd = MP_DIGITS(mp); + for (ix = 0; ix < used; ix++) { + mp_digit d = *pd; + *pd++ = (d << 1) | kin; + kin = (d >> (DIGIT_BIT - 1)); + } + + /* Deal with rollover from last digit */ + if (kin) { + if (ix >= ALLOC(mp)) { + mp_err res; + if((res = s_mp_grow(mp, ALLOC(mp) + 1)) != MP_OKAY) + return res; + } + + DIGIT(mp, ix) = kin; + USED(mp) += 1; + } + + return MP_OKAY; + +} /* end s_mp_mul_2() */ + +/* }}} */ + +/* {{{ s_mp_mod_2d(mp, d) */ + +/* + Remainder the integer by 2^d, where d is a number of bits. This + amounts to a bitwise AND of the value, and does not require the full + division code + */ +void s_mp_mod_2d(mp_int *mp, mp_digit d) +{ + mp_size ndig = (d / DIGIT_BIT), nbit = (d % DIGIT_BIT); + mp_size ix; + mp_digit dmask; + + if(ndig >= USED(mp)) + return; + + /* Flush all the bits above 2^d in its digit */ + dmask = ((mp_digit)1 << nbit) - 1; + DIGIT(mp, ndig) &= dmask; + + /* Flush all digits above the one with 2^d in it */ + for(ix = ndig + 1; ix < USED(mp); ix++) + DIGIT(mp, ix) = 0; + + s_mp_clamp(mp); + +} /* end s_mp_mod_2d() */ + +/* }}} */ + +/* {{{ s_mp_div_2d(mp, d) */ + +/* + Divide the integer by 2^d, where d is a number of bits. This + amounts to a bitwise shift of the value, and does not require the + full division code (used in Barrett reduction, see below) + */ +void s_mp_div_2d(mp_int *mp, mp_digit d) +{ + int ix; + mp_digit save, next, mask; + + s_mp_rshd(mp, d / DIGIT_BIT); + d %= DIGIT_BIT; + if (d) { + mask = ((mp_digit)1 << d) - 1; + save = 0; + for(ix = USED(mp) - 1; ix >= 0; ix--) { + next = DIGIT(mp, ix) & mask; + DIGIT(mp, ix) = (DIGIT(mp, ix) >> d) | (save << (DIGIT_BIT - d)); + save = next; + } + } + s_mp_clamp(mp); + +} /* end s_mp_div_2d() */ + +/* }}} */ + +/* {{{ s_mp_norm(a, b, *d) */ + +/* + s_mp_norm(a, b, *d) + + Normalize a and b for division, where b is the divisor. In order + that we might make good guesses for quotient digits, we want the + leading digit of b to be at least half the radix, which we + accomplish by multiplying a and b by a power of 2. The exponent + (shift count) is placed in *pd, so that the remainder can be shifted + back at the end of the division process. + */ + +mp_err s_mp_norm(mp_int *a, mp_int *b, mp_digit *pd) +{ + mp_digit d; + mp_digit mask; + mp_digit b_msd; + mp_err res = MP_OKAY; + + d = 0; + mask = DIGIT_MAX & ~(DIGIT_MAX >> 1); /* mask is msb of digit */ + b_msd = DIGIT(b, USED(b) - 1); + while (!(b_msd & mask)) { + b_msd <<= 1; + ++d; + } + + if (d) { + MP_CHECKOK( s_mp_mul_2d(a, d) ); + MP_CHECKOK( s_mp_mul_2d(b, d) ); + } + + *pd = d; +CLEANUP: + return res; + +} /* end s_mp_norm() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive digit arithmetic */ + +/* {{{ s_mp_add_d(mp, d) */ + +/* Add d to |mp| in place */ +mp_err s_mp_add_d(mp_int *mp, mp_digit d) /* unsigned digit addition */ +{ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + mp_word w, k = 0; + mp_size ix = 1; + + w = (mp_word)DIGIT(mp, 0) + d; + DIGIT(mp, 0) = ACCUM(w); + k = CARRYOUT(w); + + while(ix < USED(mp) && k) { + w = (mp_word)DIGIT(mp, ix) + k; + DIGIT(mp, ix) = ACCUM(w); + k = CARRYOUT(w); + ++ix; + } + + if(k != 0) { + mp_err res; + + if((res = s_mp_pad(mp, USED(mp) + 1)) != MP_OKAY) + return res; + + DIGIT(mp, ix) = (mp_digit)k; + } + + return MP_OKAY; +#else + mp_digit * pmp = MP_DIGITS(mp); + mp_digit sum, mp_i, carry = 0; + mp_err res = MP_OKAY; + int used = (int)MP_USED(mp); + + mp_i = *pmp; + *pmp++ = sum = d + mp_i; + carry = (sum < d); + while (carry && --used > 0) { + mp_i = *pmp; + *pmp++ = sum = carry + mp_i; + carry = !sum; + } + if (carry && !used) { + /* mp is growing */ + used = MP_USED(mp); + MP_CHECKOK( s_mp_pad(mp, used + 1) ); + MP_DIGIT(mp, used) = carry; + } +CLEANUP: + return res; +#endif +} /* end s_mp_add_d() */ + +/* }}} */ + +/* {{{ s_mp_sub_d(mp, d) */ + +/* Subtract d from |mp| in place, assumes |mp| > d */ +mp_err s_mp_sub_d(mp_int *mp, mp_digit d) /* unsigned digit subtract */ +{ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_SUB_WORD) + mp_word w, b = 0; + mp_size ix = 1; + + /* Compute initial subtraction */ + w = (RADIX + (mp_word)DIGIT(mp, 0)) - d; + b = CARRYOUT(w) ? 0 : 1; + DIGIT(mp, 0) = ACCUM(w); + + /* Propagate borrows leftward */ + while(b && ix < USED(mp)) { + w = (RADIX + (mp_word)DIGIT(mp, ix)) - b; + b = CARRYOUT(w) ? 0 : 1; + DIGIT(mp, ix) = ACCUM(w); + ++ix; + } + + /* Remove leading zeroes */ + s_mp_clamp(mp); + + /* If we have a borrow out, it's a violation of the input invariant */ + if(b) + return MP_RANGE; + else + return MP_OKAY; +#else + mp_digit *pmp = MP_DIGITS(mp); + mp_digit mp_i, diff, borrow; + mp_size used = MP_USED(mp); + + mp_i = *pmp; + *pmp++ = diff = mp_i - d; + borrow = (diff > mp_i); + while (borrow && --used) { + mp_i = *pmp; + *pmp++ = diff = mp_i - borrow; + borrow = (diff > mp_i); + } + s_mp_clamp(mp); + return (borrow && !used) ? MP_RANGE : MP_OKAY; +#endif +} /* end s_mp_sub_d() */ + +/* }}} */ + +/* {{{ s_mp_mul_d(a, d) */ + +/* Compute a = a * d, single digit multiplication */ +mp_err s_mp_mul_d(mp_int *a, mp_digit d) +{ + mp_err res; + mp_size used; + int pow; + + if (!d) { + mp_zero(a); + return MP_OKAY; + } + if (d == 1) + return MP_OKAY; + if (0 <= (pow = s_mp_ispow2d(d))) { + return s_mp_mul_2d(a, (mp_digit)pow); + } + + used = MP_USED(a); + MP_CHECKOK( s_mp_pad(a, used + 1) ); + + s_mpv_mul_d(MP_DIGITS(a), used, d, MP_DIGITS(a)); + + s_mp_clamp(a); + +CLEANUP: + return res; + +} /* end s_mp_mul_d() */ + +/* }}} */ + +/* {{{ s_mp_div_d(mp, d, r) */ + +/* + s_mp_div_d(mp, d, r) + + Compute the quotient mp = mp / d and remainder r = mp mod d, for a + single digit d. If r is null, the remainder will be discarded. + */ + +mp_err s_mp_div_d(mp_int *mp, mp_digit d, mp_digit *r) +{ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_DIV_WORD) + mp_word w = 0, q; +#else + mp_digit w, q; +#endif + int ix; + mp_err res; + mp_int quot; + mp_int rem; + + if(d == 0) + return MP_RANGE; + if (d == 1) { + if (r) + *r = 0; + return MP_OKAY; + } + /* could check for power of 2 here, but mp_div_d does that. */ + if (MP_USED(mp) == 1) { + mp_digit n = MP_DIGIT(mp,0); + mp_digit rem; + + q = n / d; + rem = n % d; + MP_DIGIT(mp,0) = q; + if (r) + *r = rem; + return MP_OKAY; + } + + MP_DIGITS(&rem) = 0; + MP_DIGITS(") = 0; + /* Make room for the quotient */ + MP_CHECKOK( mp_init_size(", USED(mp), FLAG(mp)) ); + +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_DIV_WORD) + for(ix = USED(mp) - 1; ix >= 0; ix--) { + w = (w << DIGIT_BIT) | DIGIT(mp, ix); + + if(w >= d) { + q = w / d; + w = w % d; + } else { + q = 0; + } + + s_mp_lshd(", 1); + DIGIT(", 0) = (mp_digit)q; + } +#else + { + mp_digit p; +#if !defined(MP_ASSEMBLY_DIV_2DX1D) + mp_digit norm; +#endif + + MP_CHECKOK( mp_init_copy(&rem, mp) ); + +#if !defined(MP_ASSEMBLY_DIV_2DX1D) + MP_DIGIT(", 0) = d; + MP_CHECKOK( s_mp_norm(&rem, ", &norm) ); + if (norm) + d <<= norm; + MP_DIGIT(", 0) = 0; +#endif + + p = 0; + for (ix = USED(&rem) - 1; ix >= 0; ix--) { + w = DIGIT(&rem, ix); + + if (p) { + MP_CHECKOK( s_mpv_div_2dx1d(p, w, d, &q, &w) ); + } else if (w >= d) { + q = w / d; + w = w % d; + } else { + q = 0; + } + + MP_CHECKOK( s_mp_lshd(", 1) ); + DIGIT(", 0) = q; + p = w; + } +#if !defined(MP_ASSEMBLY_DIV_2DX1D) + if (norm) + w >>= norm; +#endif + } +#endif + + /* Deliver the remainder, if desired */ + if(r) + *r = (mp_digit)w; + + s_mp_clamp("); + mp_exch(", mp); +CLEANUP: + mp_clear("); + mp_clear(&rem); + + return res; +} /* end s_mp_div_d() */ + +/* }}} */ + + +/* }}} */ + +/* {{{ Primitive full arithmetic */ + +/* {{{ s_mp_add(a, b) */ + +/* Compute a = |a| + |b| */ +mp_err s_mp_add(mp_int *a, const mp_int *b) /* magnitude addition */ +{ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + mp_word w = 0; +#else + mp_digit d, sum, carry = 0; +#endif + mp_digit *pa, *pb; + mp_size ix; + mp_size used; + mp_err res; + + /* Make sure a has enough precision for the output value */ + if((USED(b) > USED(a)) && (res = s_mp_pad(a, USED(b))) != MP_OKAY) + return res; + + /* + Add up all digits up to the precision of b. If b had initially + the same precision as a, or greater, we took care of it by the + padding step above, so there is no problem. If b had initially + less precision, we'll have to make sure the carry out is duly + propagated upward among the higher-order digits of the sum. + */ + pa = MP_DIGITS(a); + pb = MP_DIGITS(b); + used = MP_USED(b); + for(ix = 0; ix < used; ix++) { +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + w = w + *pa + *pb++; + *pa++ = ACCUM(w); + w = CARRYOUT(w); +#else + d = *pa; + sum = d + *pb++; + d = (sum < d); /* detect overflow */ + *pa++ = sum += carry; + carry = d + (sum < carry); /* detect overflow */ +#endif + } + + /* If we run out of 'b' digits before we're actually done, make + sure the carries get propagated upward... + */ + used = MP_USED(a); +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + while (w && ix < used) { + w = w + *pa; + *pa++ = ACCUM(w); + w = CARRYOUT(w); + ++ix; + } +#else + while (carry && ix < used) { + sum = carry + *pa; + *pa++ = sum; + carry = !sum; + ++ix; + } +#endif + + /* If there's an overall carry out, increase precision and include + it. We could have done this initially, but why touch the memory + allocator unless we're sure we have to? + */ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + if (w) { + if((res = s_mp_pad(a, used + 1)) != MP_OKAY) + return res; + + DIGIT(a, ix) = (mp_digit)w; + } +#else + if (carry) { + if((res = s_mp_pad(a, used + 1)) != MP_OKAY) + return res; + + DIGIT(a, used) = carry; + } +#endif + + return MP_OKAY; +} /* end s_mp_add() */ + +/* }}} */ + +/* Compute c = |a| + |b| */ /* magnitude addition */ +mp_err s_mp_add_3arg(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_digit *pa, *pb, *pc; +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + mp_word w = 0; +#else + mp_digit sum, carry = 0, d; +#endif + mp_size ix; + mp_size used; + mp_err res; + + MP_SIGN(c) = MP_SIGN(a); + if (MP_USED(a) < MP_USED(b)) { + const mp_int *xch = a; + a = b; + b = xch; + } + + /* Make sure a has enough precision for the output value */ + if (MP_OKAY != (res = s_mp_pad(c, MP_USED(a)))) + return res; + + /* + Add up all digits up to the precision of b. If b had initially + the same precision as a, or greater, we took care of it by the + exchange step above, so there is no problem. If b had initially + less precision, we'll have to make sure the carry out is duly + propagated upward among the higher-order digits of the sum. + */ + pa = MP_DIGITS(a); + pb = MP_DIGITS(b); + pc = MP_DIGITS(c); + used = MP_USED(b); + for (ix = 0; ix < used; ix++) { +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + w = w + *pa++ + *pb++; + *pc++ = ACCUM(w); + w = CARRYOUT(w); +#else + d = *pa++; + sum = d + *pb++; + d = (sum < d); /* detect overflow */ + *pc++ = sum += carry; + carry = d + (sum < carry); /* detect overflow */ +#endif + } + + /* If we run out of 'b' digits before we're actually done, make + sure the carries get propagated upward... + */ + for (used = MP_USED(a); ix < used; ++ix) { +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + w = w + *pa++; + *pc++ = ACCUM(w); + w = CARRYOUT(w); +#else + *pc++ = sum = carry + *pa++; + carry = (sum < carry); +#endif + } + + /* If there's an overall carry out, increase precision and include + it. We could have done this initially, but why touch the memory + allocator unless we're sure we have to? + */ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + if (w) { + if((res = s_mp_pad(c, used + 1)) != MP_OKAY) + return res; + + DIGIT(c, used) = (mp_digit)w; + ++used; + } +#else + if (carry) { + if((res = s_mp_pad(c, used + 1)) != MP_OKAY) + return res; + + DIGIT(c, used) = carry; + ++used; + } +#endif + MP_USED(c) = used; + return MP_OKAY; +} +/* {{{ s_mp_add_offset(a, b, offset) */ + +/* Compute a = |a| + ( |b| * (RADIX ** offset) ) */ +mp_err s_mp_add_offset(mp_int *a, mp_int *b, mp_size offset) +{ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + mp_word w, k = 0; +#else + mp_digit d, sum, carry = 0; +#endif + mp_size ib; + mp_size ia; + mp_size lim; + mp_err res; + + /* Make sure a has enough precision for the output value */ + lim = MP_USED(b) + offset; + if((lim > USED(a)) && (res = s_mp_pad(a, lim)) != MP_OKAY) + return res; + + /* + Add up all digits up to the precision of b. If b had initially + the same precision as a, or greater, we took care of it by the + padding step above, so there is no problem. If b had initially + less precision, we'll have to make sure the carry out is duly + propagated upward among the higher-order digits of the sum. + */ + lim = USED(b); + for(ib = 0, ia = offset; ib < lim; ib++, ia++) { +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + w = (mp_word)DIGIT(a, ia) + DIGIT(b, ib) + k; + DIGIT(a, ia) = ACCUM(w); + k = CARRYOUT(w); +#else + d = MP_DIGIT(a, ia); + sum = d + MP_DIGIT(b, ib); + d = (sum < d); + MP_DIGIT(a,ia) = sum += carry; + carry = d + (sum < carry); +#endif + } + + /* If we run out of 'b' digits before we're actually done, make + sure the carries get propagated upward... + */ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + for (lim = MP_USED(a); k && (ia < lim); ++ia) { + w = (mp_word)DIGIT(a, ia) + k; + DIGIT(a, ia) = ACCUM(w); + k = CARRYOUT(w); + } +#else + for (lim = MP_USED(a); carry && (ia < lim); ++ia) { + d = MP_DIGIT(a, ia); + MP_DIGIT(a,ia) = sum = d + carry; + carry = (sum < d); + } +#endif + + /* If there's an overall carry out, increase precision and include + it. We could have done this initially, but why touch the memory + allocator unless we're sure we have to? + */ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) + if(k) { + if((res = s_mp_pad(a, USED(a) + 1)) != MP_OKAY) + return res; + + DIGIT(a, ia) = (mp_digit)k; + } +#else + if (carry) { + if((res = s_mp_pad(a, lim + 1)) != MP_OKAY) + return res; + + DIGIT(a, lim) = carry; + } +#endif + s_mp_clamp(a); + + return MP_OKAY; + +} /* end s_mp_add_offset() */ + +/* }}} */ + +/* {{{ s_mp_sub(a, b) */ + +/* Compute a = |a| - |b|, assumes |a| >= |b| */ +mp_err s_mp_sub(mp_int *a, const mp_int *b) /* magnitude subtract */ +{ + mp_digit *pa, *pb, *limit; +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_SUB_WORD) + mp_sword w = 0; +#else + mp_digit d, diff, borrow = 0; +#endif + + /* + Subtract and propagate borrow. Up to the precision of b, this + accounts for the digits of b; after that, we just make sure the + carries get to the right place. This saves having to pad b out to + the precision of a just to make the loops work right... + */ + pa = MP_DIGITS(a); + pb = MP_DIGITS(b); + limit = pb + MP_USED(b); + while (pb < limit) { +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_SUB_WORD) + w = w + *pa - *pb++; + *pa++ = ACCUM(w); + w >>= MP_DIGIT_BIT; +#else + d = *pa; + diff = d - *pb++; + d = (diff > d); /* detect borrow */ + if (borrow && --diff == MP_DIGIT_MAX) + ++d; + *pa++ = diff; + borrow = d; +#endif + } + limit = MP_DIGITS(a) + MP_USED(a); +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_SUB_WORD) + while (w && pa < limit) { + w = w + *pa; + *pa++ = ACCUM(w); + w >>= MP_DIGIT_BIT; + } +#else + while (borrow && pa < limit) { + d = *pa; + *pa++ = diff = d - borrow; + borrow = (diff > d); + } +#endif + + /* Clobber any leading zeroes we created */ + s_mp_clamp(a); + + /* + If there was a borrow out, then |b| > |a| in violation + of our input invariant. We've already done the work, + but we'll at least complain about it... + */ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_SUB_WORD) + return w ? MP_RANGE : MP_OKAY; +#else + return borrow ? MP_RANGE : MP_OKAY; +#endif +} /* end s_mp_sub() */ + +/* }}} */ + +/* Compute c = |a| - |b|, assumes |a| >= |b| */ /* magnitude subtract */ +mp_err s_mp_sub_3arg(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_digit *pa, *pb, *pc; +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_SUB_WORD) + mp_sword w = 0; +#else + mp_digit d, diff, borrow = 0; +#endif + int ix, limit; + mp_err res; + + MP_SIGN(c) = MP_SIGN(a); + + /* Make sure a has enough precision for the output value */ + if (MP_OKAY != (res = s_mp_pad(c, MP_USED(a)))) + return res; + + /* + Subtract and propagate borrow. Up to the precision of b, this + accounts for the digits of b; after that, we just make sure the + carries get to the right place. This saves having to pad b out to + the precision of a just to make the loops work right... + */ + pa = MP_DIGITS(a); + pb = MP_DIGITS(b); + pc = MP_DIGITS(c); + limit = MP_USED(b); + for (ix = 0; ix < limit; ++ix) { +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_SUB_WORD) + w = w + *pa++ - *pb++; + *pc++ = ACCUM(w); + w >>= MP_DIGIT_BIT; +#else + d = *pa++; + diff = d - *pb++; + d = (diff > d); + if (borrow && --diff == MP_DIGIT_MAX) + ++d; + *pc++ = diff; + borrow = d; +#endif + } + for (limit = MP_USED(a); ix < limit; ++ix) { +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_SUB_WORD) + w = w + *pa++; + *pc++ = ACCUM(w); + w >>= MP_DIGIT_BIT; +#else + d = *pa++; + *pc++ = diff = d - borrow; + borrow = (diff > d); +#endif + } + + /* Clobber any leading zeroes we created */ + MP_USED(c) = ix; + s_mp_clamp(c); + + /* + If there was a borrow out, then |b| > |a| in violation + of our input invariant. We've already done the work, + but we'll at least complain about it... + */ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_SUB_WORD) + return w ? MP_RANGE : MP_OKAY; +#else + return borrow ? MP_RANGE : MP_OKAY; +#endif +} +/* {{{ s_mp_mul(a, b) */ + +/* Compute a = |a| * |b| */ +mp_err s_mp_mul(mp_int *a, const mp_int *b) +{ + return mp_mul(a, b, a); +} /* end s_mp_mul() */ + +/* }}} */ + +#if defined(MP_USE_UINT_DIGIT) && defined(MP_USE_LONG_LONG_MULTIPLY) +/* This trick works on Sparc V8 CPUs with the Workshop compilers. */ +#define MP_MUL_DxD(a, b, Phi, Plo) \ + { unsigned long long product = (unsigned long long)a * b; \ + Plo = (mp_digit)product; \ + Phi = (mp_digit)(product >> MP_DIGIT_BIT); } +#elif defined(OSF1) +#define MP_MUL_DxD(a, b, Phi, Plo) \ + { Plo = asm ("mulq %a0, %a1, %v0", a, b);\ + Phi = asm ("umulh %a0, %a1, %v0", a, b); } +#else +#define MP_MUL_DxD(a, b, Phi, Plo) \ + { mp_digit a0b1, a1b0; \ + Plo = (a & MP_HALF_DIGIT_MAX) * (b & MP_HALF_DIGIT_MAX); \ + Phi = (a >> MP_HALF_DIGIT_BIT) * (b >> MP_HALF_DIGIT_BIT); \ + a0b1 = (a & MP_HALF_DIGIT_MAX) * (b >> MP_HALF_DIGIT_BIT); \ + a1b0 = (a >> MP_HALF_DIGIT_BIT) * (b & MP_HALF_DIGIT_MAX); \ + a1b0 += a0b1; \ + Phi += a1b0 >> MP_HALF_DIGIT_BIT; \ + if (a1b0 < a0b1) \ + Phi += MP_HALF_RADIX; \ + a1b0 <<= MP_HALF_DIGIT_BIT; \ + Plo += a1b0; \ + if (Plo < a1b0) \ + ++Phi; \ + } +#endif + +#if !defined(MP_ASSEMBLY_MULTIPLY) +/* c = a * b */ +void s_mpv_mul_d(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) +{ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_MUL_WORD) + mp_digit d = 0; + + /* Inner product: Digits of a */ + while (a_len--) { + mp_word w = ((mp_word)b * *a++) + d; + *c++ = ACCUM(w); + d = CARRYOUT(w); + } + *c = d; +#else + mp_digit carry = 0; + while (a_len--) { + mp_digit a_i = *a++; + mp_digit a0b0, a1b1; + + MP_MUL_DxD(a_i, b, a1b1, a0b0); + + a0b0 += carry; + if (a0b0 < carry) + ++a1b1; + *c++ = a0b0; + carry = a1b1; + } + *c = carry; +#endif +} + +/* c += a * b */ +void s_mpv_mul_d_add(const mp_digit *a, mp_size a_len, mp_digit b, + mp_digit *c) +{ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_MUL_WORD) + mp_digit d = 0; + + /* Inner product: Digits of a */ + while (a_len--) { + mp_word w = ((mp_word)b * *a++) + *c + d; + *c++ = ACCUM(w); + d = CARRYOUT(w); + } + *c = d; +#else + mp_digit carry = 0; + while (a_len--) { + mp_digit a_i = *a++; + mp_digit a0b0, a1b1; + + MP_MUL_DxD(a_i, b, a1b1, a0b0); + + a0b0 += carry; + if (a0b0 < carry) + ++a1b1; + a0b0 += a_i = *c; + if (a0b0 < a_i) + ++a1b1; + *c++ = a0b0; + carry = a1b1; + } + *c = carry; +#endif +} + +/* Presently, this is only used by the Montgomery arithmetic code. */ +/* c += a * b */ +void s_mpv_mul_d_add_prop(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) +{ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_MUL_WORD) + mp_digit d = 0; + + /* Inner product: Digits of a */ + while (a_len--) { + mp_word w = ((mp_word)b * *a++) + *c + d; + *c++ = ACCUM(w); + d = CARRYOUT(w); + } + + while (d) { + mp_word w = (mp_word)*c + d; + *c++ = ACCUM(w); + d = CARRYOUT(w); + } +#else + mp_digit carry = 0; + while (a_len--) { + mp_digit a_i = *a++; + mp_digit a0b0, a1b1; + + MP_MUL_DxD(a_i, b, a1b1, a0b0); + + a0b0 += carry; + if (a0b0 < carry) + ++a1b1; + + a0b0 += a_i = *c; + if (a0b0 < a_i) + ++a1b1; + + *c++ = a0b0; + carry = a1b1; + } + while (carry) { + mp_digit c_i = *c; + carry += c_i; + *c++ = carry; + carry = carry < c_i; + } +#endif +} +#endif + +#if defined(MP_USE_UINT_DIGIT) && defined(MP_USE_LONG_LONG_MULTIPLY) +/* This trick works on Sparc V8 CPUs with the Workshop compilers. */ +#define MP_SQR_D(a, Phi, Plo) \ + { unsigned long long square = (unsigned long long)a * a; \ + Plo = (mp_digit)square; \ + Phi = (mp_digit)(square >> MP_DIGIT_BIT); } +#elif defined(OSF1) +#define MP_SQR_D(a, Phi, Plo) \ + { Plo = asm ("mulq %a0, %a0, %v0", a);\ + Phi = asm ("umulh %a0, %a0, %v0", a); } +#else +#define MP_SQR_D(a, Phi, Plo) \ + { mp_digit Pmid; \ + Plo = (a & MP_HALF_DIGIT_MAX) * (a & MP_HALF_DIGIT_MAX); \ + Phi = (a >> MP_HALF_DIGIT_BIT) * (a >> MP_HALF_DIGIT_BIT); \ + Pmid = (a & MP_HALF_DIGIT_MAX) * (a >> MP_HALF_DIGIT_BIT); \ + Phi += Pmid >> (MP_HALF_DIGIT_BIT - 1); \ + Pmid <<= (MP_HALF_DIGIT_BIT + 1); \ + Plo += Pmid; \ + if (Plo < Pmid) \ + ++Phi; \ + } +#endif + +#if !defined(MP_ASSEMBLY_SQUARE) +/* Add the squares of the digits of a to the digits of b. */ +void s_mpv_sqr_add_prop(const mp_digit *pa, mp_size a_len, mp_digit *ps) +{ +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_MUL_WORD) + mp_word w; + mp_digit d; + mp_size ix; + + w = 0; +#define ADD_SQUARE(n) \ + d = pa[n]; \ + w += (d * (mp_word)d) + ps[2*n]; \ + ps[2*n] = ACCUM(w); \ + w = (w >> DIGIT_BIT) + ps[2*n+1]; \ + ps[2*n+1] = ACCUM(w); \ + w = (w >> DIGIT_BIT) + + for (ix = a_len; ix >= 4; ix -= 4) { + ADD_SQUARE(0); + ADD_SQUARE(1); + ADD_SQUARE(2); + ADD_SQUARE(3); + pa += 4; + ps += 8; + } + if (ix) { + ps += 2*ix; + pa += ix; + switch (ix) { + case 3: ADD_SQUARE(-3); /* FALLTHRU */ + case 2: ADD_SQUARE(-2); /* FALLTHRU */ + case 1: ADD_SQUARE(-1); /* FALLTHRU */ + case 0: break; + } + } + while (w) { + w += *ps; + *ps++ = ACCUM(w); + w = (w >> DIGIT_BIT); + } +#else + mp_digit carry = 0; + while (a_len--) { + mp_digit a_i = *pa++; + mp_digit a0a0, a1a1; + + MP_SQR_D(a_i, a1a1, a0a0); + + /* here a1a1 and a0a0 constitute a_i ** 2 */ + a0a0 += carry; + if (a0a0 < carry) + ++a1a1; + + /* now add to ps */ + a0a0 += a_i = *ps; + if (a0a0 < a_i) + ++a1a1; + *ps++ = a0a0; + a1a1 += a_i = *ps; + carry = (a1a1 < a_i); + *ps++ = a1a1; + } + while (carry) { + mp_digit s_i = *ps; + carry += s_i; + *ps++ = carry; + carry = carry < s_i; + } +#endif +} +#endif + +#if (defined(MP_NO_MP_WORD) || defined(MP_NO_DIV_WORD)) \ +&& !defined(MP_ASSEMBLY_DIV_2DX1D) +/* +** Divide 64-bit (Nhi,Nlo) by 32-bit divisor, which must be normalized +** so its high bit is 1. This code is from NSPR. +*/ +mp_err s_mpv_div_2dx1d(mp_digit Nhi, mp_digit Nlo, mp_digit divisor, + mp_digit *qp, mp_digit *rp) +{ + mp_digit d1, d0, q1, q0; + mp_digit r1, r0, m; + + d1 = divisor >> MP_HALF_DIGIT_BIT; + d0 = divisor & MP_HALF_DIGIT_MAX; + r1 = Nhi % d1; + q1 = Nhi / d1; + m = q1 * d0; + r1 = (r1 << MP_HALF_DIGIT_BIT) | (Nlo >> MP_HALF_DIGIT_BIT); + if (r1 < m) { + q1--, r1 += divisor; + if (r1 >= divisor && r1 < m) { + q1--, r1 += divisor; + } + } + r1 -= m; + r0 = r1 % d1; + q0 = r1 / d1; + m = q0 * d0; + r0 = (r0 << MP_HALF_DIGIT_BIT) | (Nlo & MP_HALF_DIGIT_MAX); + if (r0 < m) { + q0--, r0 += divisor; + if (r0 >= divisor && r0 < m) { + q0--, r0 += divisor; + } + } + if (qp) + *qp = (q1 << MP_HALF_DIGIT_BIT) | q0; + if (rp) + *rp = r0 - m; + return MP_OKAY; +} +#endif + +#if MP_SQUARE +/* {{{ s_mp_sqr(a) */ + +mp_err s_mp_sqr(mp_int *a) +{ + mp_err res; + mp_int tmp; + + if((res = mp_init_size(&tmp, 2 * USED(a), FLAG(a))) != MP_OKAY) + return res; + res = mp_sqr(a, &tmp); + if (res == MP_OKAY) { + s_mp_exch(&tmp, a); + } + mp_clear(&tmp); + return res; +} + +/* }}} */ +#endif + +/* {{{ s_mp_div(a, b) */ + +/* + s_mp_div(a, b) + + Compute a = a / b and b = a mod b. Assumes b > a. + */ + +mp_err s_mp_div(mp_int *rem, /* i: dividend, o: remainder */ + mp_int *div, /* i: divisor */ + mp_int *quot) /* i: 0; o: quotient */ +{ + mp_int part, t; +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_DIV_WORD) + mp_word q_msd; +#else + mp_digit q_msd; +#endif + mp_err res; + mp_digit d; + mp_digit div_msd; + int ix; + + if(mp_cmp_z(div) == 0) + return MP_RANGE; + + /* Shortcut if divisor is power of two */ + if((ix = s_mp_ispow2(div)) >= 0) { + MP_CHECKOK( mp_copy(rem, quot) ); + s_mp_div_2d(quot, (mp_digit)ix); + s_mp_mod_2d(rem, (mp_digit)ix); + + return MP_OKAY; + } + + DIGITS(&t) = 0; + MP_SIGN(rem) = ZPOS; + MP_SIGN(div) = ZPOS; + + /* A working temporary for division */ + MP_CHECKOK( mp_init_size(&t, MP_ALLOC(rem), FLAG(rem))); + + /* Normalize to optimize guessing */ + MP_CHECKOK( s_mp_norm(rem, div, &d) ); + + part = *rem; + + /* Perform the division itself...woo! */ + MP_USED(quot) = MP_ALLOC(quot); + + /* Find a partial substring of rem which is at least div */ + /* If we didn't find one, we're finished dividing */ + while (MP_USED(rem) > MP_USED(div) || s_mp_cmp(rem, div) >= 0) { + int i; + int unusedRem; + + unusedRem = MP_USED(rem) - MP_USED(div); + MP_DIGITS(&part) = MP_DIGITS(rem) + unusedRem; + MP_ALLOC(&part) = MP_ALLOC(rem) - unusedRem; + MP_USED(&part) = MP_USED(div); + if (s_mp_cmp(&part, div) < 0) { + -- unusedRem; +#if MP_ARGCHK == 2 + assert(unusedRem >= 0); +#endif + -- MP_DIGITS(&part); + ++ MP_USED(&part); + ++ MP_ALLOC(&part); + } + + /* Compute a guess for the next quotient digit */ + q_msd = MP_DIGIT(&part, MP_USED(&part) - 1); + div_msd = MP_DIGIT(div, MP_USED(div) - 1); + if (q_msd >= div_msd) { + q_msd = 1; + } else if (MP_USED(&part) > 1) { +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_DIV_WORD) + q_msd = (q_msd << MP_DIGIT_BIT) | MP_DIGIT(&part, MP_USED(&part) - 2); + q_msd /= div_msd; + if (q_msd == RADIX) + --q_msd; +#else + mp_digit r; + MP_CHECKOK( s_mpv_div_2dx1d(q_msd, MP_DIGIT(&part, MP_USED(&part) - 2), + div_msd, &q_msd, &r) ); +#endif + } else { + q_msd = 0; + } +#if MP_ARGCHK == 2 + assert(q_msd > 0); /* This case should never occur any more. */ +#endif + if (q_msd <= 0) + break; + + /* See what that multiplies out to */ + mp_copy(div, &t); + MP_CHECKOK( s_mp_mul_d(&t, (mp_digit)q_msd) ); + + /* + If it's too big, back it off. We should not have to do this + more than once, or, in rare cases, twice. Knuth describes a + method by which this could be reduced to a maximum of once, but + I didn't implement that here. + * When using s_mpv_div_2dx1d, we may have to do this 3 times. + */ + for (i = 4; s_mp_cmp(&t, &part) > 0 && i > 0; --i) { + --q_msd; + s_mp_sub(&t, div); /* t -= div */ + } + if (i < 0) { + res = MP_RANGE; + goto CLEANUP; + } + + /* At this point, q_msd should be the right next digit */ + MP_CHECKOK( s_mp_sub(&part, &t) ); /* part -= t */ + s_mp_clamp(rem); + + /* + Include the digit in the quotient. We allocated enough memory + for any quotient we could ever possibly get, so we should not + have to check for failures here + */ + MP_DIGIT(quot, unusedRem) = (mp_digit)q_msd; + } + + /* Denormalize remainder */ + if (d) { + s_mp_div_2d(rem, d); + } + + s_mp_clamp(quot); + +CLEANUP: + mp_clear(&t); + + return res; + +} /* end s_mp_div() */ + + +/* }}} */ + +/* {{{ s_mp_2expt(a, k) */ + +mp_err s_mp_2expt(mp_int *a, mp_digit k) +{ + mp_err res; + mp_size dig, bit; + + dig = k / DIGIT_BIT; + bit = k % DIGIT_BIT; + + mp_zero(a); + if((res = s_mp_pad(a, dig + 1)) != MP_OKAY) + return res; + + DIGIT(a, dig) |= ((mp_digit)1 << bit); + + return MP_OKAY; + +} /* end s_mp_2expt() */ + +/* }}} */ + +/* {{{ s_mp_reduce(x, m, mu) */ + +/* + Compute Barrett reduction, x (mod m), given a precomputed value for + mu = b^2k / m, where b = RADIX and k = #digits(m). This should be + faster than straight division, when many reductions by the same + value of m are required (such as in modular exponentiation). This + can nearly halve the time required to do modular exponentiation, + as compared to using the full integer divide to reduce. + + This algorithm was derived from the _Handbook of Applied + Cryptography_ by Menezes, Oorschot and VanStone, Ch. 14, + pp. 603-604. + */ + +mp_err s_mp_reduce(mp_int *x, const mp_int *m, const mp_int *mu) +{ + mp_int q; + mp_err res; + + if((res = mp_init_copy(&q, x)) != MP_OKAY) + return res; + + s_mp_rshd(&q, USED(m) - 1); /* q1 = x / b^(k-1) */ + s_mp_mul(&q, mu); /* q2 = q1 * mu */ + s_mp_rshd(&q, USED(m) + 1); /* q3 = q2 / b^(k+1) */ + + /* x = x mod b^(k+1), quick (no division) */ + s_mp_mod_2d(x, DIGIT_BIT * (USED(m) + 1)); + + /* q = q * m mod b^(k+1), quick (no division) */ + s_mp_mul(&q, m); + s_mp_mod_2d(&q, DIGIT_BIT * (USED(m) + 1)); + + /* x = x - q */ + if((res = mp_sub(x, &q, x)) != MP_OKAY) + goto CLEANUP; + + /* If x < 0, add b^(k+1) to it */ + if(mp_cmp_z(x) < 0) { + mp_set(&q, 1); + if((res = s_mp_lshd(&q, USED(m) + 1)) != MP_OKAY) + goto CLEANUP; + if((res = mp_add(x, &q, x)) != MP_OKAY) + goto CLEANUP; + } + + /* Back off if it's too big */ + while(mp_cmp(x, m) >= 0) { + if((res = s_mp_sub(x, m)) != MP_OKAY) + break; + } + + CLEANUP: + mp_clear(&q); + + return res; + +} /* end s_mp_reduce() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive comparisons */ + +/* {{{ s_mp_cmp(a, b) */ + +/* Compare |a| <=> |b|, return 0 if equal, <0 if a<b, >0 if a>b */ +int s_mp_cmp(const mp_int *a, const mp_int *b) +{ + mp_size used_a = MP_USED(a); + { + mp_size used_b = MP_USED(b); + + if (used_a > used_b) + goto IS_GT; + if (used_a < used_b) + goto IS_LT; + } + { + mp_digit *pa, *pb; + mp_digit da = 0, db = 0; + +#define CMP_AB(n) if ((da = pa[n]) != (db = pb[n])) goto done + + pa = MP_DIGITS(a) + used_a; + pb = MP_DIGITS(b) + used_a; + while (used_a >= 4) { + pa -= 4; + pb -= 4; + used_a -= 4; + CMP_AB(3); + CMP_AB(2); + CMP_AB(1); + CMP_AB(0); + } + while (used_a-- > 0 && ((da = *--pa) == (db = *--pb))) + /* do nothing */; +done: + if (da > db) + goto IS_GT; + if (da < db) + goto IS_LT; + } + return MP_EQ; +IS_LT: + return MP_LT; +IS_GT: + return MP_GT; +} /* end s_mp_cmp() */ + +/* }}} */ + +/* {{{ s_mp_cmp_d(a, d) */ + +/* Compare |a| <=> d, return 0 if equal, <0 if a<d, >0 if a>d */ +int s_mp_cmp_d(const mp_int *a, mp_digit d) +{ + if(USED(a) > 1) + return MP_GT; + + if(DIGIT(a, 0) < d) + return MP_LT; + else if(DIGIT(a, 0) > d) + return MP_GT; + else + return MP_EQ; + +} /* end s_mp_cmp_d() */ + +/* }}} */ + +/* {{{ s_mp_ispow2(v) */ + +/* + Returns -1 if the value is not a power of two; otherwise, it returns + k such that v = 2^k, i.e. lg(v). + */ +int s_mp_ispow2(const mp_int *v) +{ + mp_digit d; + int extra = 0, ix; + + ix = MP_USED(v) - 1; + d = MP_DIGIT(v, ix); /* most significant digit of v */ + + extra = s_mp_ispow2d(d); + if (extra < 0 || ix == 0) + return extra; + + while (--ix >= 0) { + if (DIGIT(v, ix) != 0) + return -1; /* not a power of two */ + extra += MP_DIGIT_BIT; + } + + return extra; + +} /* end s_mp_ispow2() */ + +/* }}} */ + +/* {{{ s_mp_ispow2d(d) */ + +int s_mp_ispow2d(mp_digit d) +{ + if ((d != 0) && ((d & (d-1)) == 0)) { /* d is a power of 2 */ + int pow = 0; +#if defined (MP_USE_UINT_DIGIT) + if (d & 0xffff0000U) + pow += 16; + if (d & 0xff00ff00U) + pow += 8; + if (d & 0xf0f0f0f0U) + pow += 4; + if (d & 0xccccccccU) + pow += 2; + if (d & 0xaaaaaaaaU) + pow += 1; +#elif defined(MP_USE_LONG_LONG_DIGIT) + if (d & 0xffffffff00000000ULL) + pow += 32; + if (d & 0xffff0000ffff0000ULL) + pow += 16; + if (d & 0xff00ff00ff00ff00ULL) + pow += 8; + if (d & 0xf0f0f0f0f0f0f0f0ULL) + pow += 4; + if (d & 0xccccccccccccccccULL) + pow += 2; + if (d & 0xaaaaaaaaaaaaaaaaULL) + pow += 1; +#elif defined(MP_USE_LONG_DIGIT) + if (d & 0xffffffff00000000UL) + pow += 32; + if (d & 0xffff0000ffff0000UL) + pow += 16; + if (d & 0xff00ff00ff00ff00UL) + pow += 8; + if (d & 0xf0f0f0f0f0f0f0f0UL) + pow += 4; + if (d & 0xccccccccccccccccUL) + pow += 2; + if (d & 0xaaaaaaaaaaaaaaaaUL) + pow += 1; +#else +#error "unknown type for mp_digit" +#endif + return pow; + } + return -1; + +} /* end s_mp_ispow2d() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive I/O helpers */ + +/* {{{ s_mp_tovalue(ch, r) */ + +/* + Convert the given character to its digit value, in the given radix. + If the given character is not understood in the given radix, -1 is + returned. Otherwise the digit's numeric value is returned. + + The results will be odd if you use a radix < 2 or > 62, you are + expected to know what you're up to. + */ +int s_mp_tovalue(char ch, int r) +{ + int val, xch; + + if(r > 36) + xch = ch; + else + xch = toupper(ch); + + if(isdigit(xch)) + val = xch - '0'; + else if(isupper(xch)) + val = xch - 'A' + 10; + else if(islower(xch)) + val = xch - 'a' + 36; + else if(xch == '+') + val = 62; + else if(xch == '/') + val = 63; + else + return -1; + + if(val < 0 || val >= r) + return -1; + + return val; + +} /* end s_mp_tovalue() */ + +/* }}} */ + +/* {{{ s_mp_todigit(val, r, low) */ + +/* + Convert val to a radix-r digit, if possible. If val is out of range + for r, returns zero. Otherwise, returns an ASCII character denoting + the value in the given radix. + + The results may be odd if you use a radix < 2 or > 64, you are + expected to know what you're doing. + */ + +char s_mp_todigit(mp_digit val, int r, int low) +{ + char ch; + + if(val >= r) + return 0; + + ch = s_dmap_1[val]; + + if(r <= 36 && low) + ch = tolower(ch); + + return ch; + +} /* end s_mp_todigit() */ + +/* }}} */ + +/* {{{ s_mp_outlen(bits, radix) */ + +/* + Return an estimate for how long a string is needed to hold a radix + r representation of a number with 'bits' significant bits, plus an + extra for a zero terminator (assuming C style strings here) + */ +int s_mp_outlen(int bits, int r) +{ + return (int)((double)bits * LOG_V_2(r) + 1.5) + 1; + +} /* end s_mp_outlen() */ + +/* }}} */ + +/* }}} */ + +/* {{{ mp_read_unsigned_octets(mp, str, len) */ +/* mp_read_unsigned_octets(mp, str, len) + Read in a raw value (base 256) into the given mp_int + No sign bit, number is positive. Leading zeros ignored. + */ + +mp_err +mp_read_unsigned_octets(mp_int *mp, const unsigned char *str, mp_size len) +{ + int count; + mp_err res; + mp_digit d; + + ARGCHK(mp != NULL && str != NULL && len > 0, MP_BADARG); + + mp_zero(mp); + + count = len % sizeof(mp_digit); + if (count) { + for (d = 0; count-- > 0; --len) { + d = (d << 8) | *str++; + } + MP_DIGIT(mp, 0) = d; + } + + /* Read the rest of the digits */ + for(; len > 0; len -= sizeof(mp_digit)) { + for (d = 0, count = sizeof(mp_digit); count > 0; --count) { + d = (d << 8) | *str++; + } + if (MP_EQ == mp_cmp_z(mp)) { + if (!d) + continue; + } else { + if((res = s_mp_lshd(mp, 1)) != MP_OKAY) + return res; + } + MP_DIGIT(mp, 0) = d; + } + return MP_OKAY; +} /* end mp_read_unsigned_octets() */ +/* }}} */ + +/* {{{ mp_unsigned_octet_size(mp) */ +int +mp_unsigned_octet_size(const mp_int *mp) +{ + int bytes; + int ix; + mp_digit d = 0; + + ARGCHK(mp != NULL, MP_BADARG); + ARGCHK(MP_ZPOS == SIGN(mp), MP_BADARG); + + bytes = (USED(mp) * sizeof(mp_digit)); + + /* subtract leading zeros. */ + /* Iterate over each digit... */ + for(ix = USED(mp) - 1; ix >= 0; ix--) { + d = DIGIT(mp, ix); + if (d) + break; + bytes -= sizeof(d); + } + if (!bytes) + return 1; + + /* Have MSD, check digit bytes, high order first */ + for(ix = sizeof(mp_digit) - 1; ix >= 0; ix--) { + unsigned char x = (unsigned char)(d >> (ix * CHAR_BIT)); + if (x) + break; + --bytes; + } + return bytes; +} /* end mp_unsigned_octet_size() */ +/* }}} */ + +/* {{{ mp_to_unsigned_octets(mp, str) */ +/* output a buffer of big endian octets no longer than specified. */ +mp_err +mp_to_unsigned_octets(const mp_int *mp, unsigned char *str, mp_size maxlen) +{ + int ix, pos = 0; + int bytes; + + ARGCHK(mp != NULL && str != NULL && !SIGN(mp), MP_BADARG); + + bytes = mp_unsigned_octet_size(mp); + ARGCHK(bytes <= maxlen, MP_BADARG); + + /* Iterate over each digit... */ + for(ix = USED(mp) - 1; ix >= 0; ix--) { + mp_digit d = DIGIT(mp, ix); + int jx; + + /* Unpack digit bytes, high order first */ + for(jx = sizeof(mp_digit) - 1; jx >= 0; jx--) { + unsigned char x = (unsigned char)(d >> (jx * CHAR_BIT)); + if (!pos && !x) /* suppress leading zeros */ + continue; + str[pos++] = x; + } + } + if (!pos) + str[pos++] = 0; + return pos; +} /* end mp_to_unsigned_octets() */ +/* }}} */ + +/* {{{ mp_to_signed_octets(mp, str) */ +/* output a buffer of big endian octets no longer than specified. */ +mp_err +mp_to_signed_octets(const mp_int *mp, unsigned char *str, mp_size maxlen) +{ + int ix, pos = 0; + int bytes; + + ARGCHK(mp != NULL && str != NULL && !SIGN(mp), MP_BADARG); + + bytes = mp_unsigned_octet_size(mp); + ARGCHK(bytes <= maxlen, MP_BADARG); + + /* Iterate over each digit... */ + for(ix = USED(mp) - 1; ix >= 0; ix--) { + mp_digit d = DIGIT(mp, ix); + int jx; + + /* Unpack digit bytes, high order first */ + for(jx = sizeof(mp_digit) - 1; jx >= 0; jx--) { + unsigned char x = (unsigned char)(d >> (jx * CHAR_BIT)); + if (!pos) { + if (!x) /* suppress leading zeros */ + continue; + if (x & 0x80) { /* add one leading zero to make output positive. */ + ARGCHK(bytes + 1 <= maxlen, MP_BADARG); + if (bytes + 1 > maxlen) + return MP_BADARG; + str[pos++] = 0; + } + } + str[pos++] = x; + } + } + if (!pos) + str[pos++] = 0; + return pos; +} /* end mp_to_signed_octets() */ +/* }}} */ + +/* {{{ mp_to_fixlen_octets(mp, str) */ +/* output a buffer of big endian octets exactly as long as requested. */ +mp_err +mp_to_fixlen_octets(const mp_int *mp, unsigned char *str, mp_size length) +{ + int ix, pos = 0; + int bytes; + + ARGCHK(mp != NULL && str != NULL && !SIGN(mp), MP_BADARG); + + bytes = mp_unsigned_octet_size(mp); + ARGCHK(bytes <= length, MP_BADARG); + + /* place any needed leading zeros */ + for (;length > bytes; --length) { + *str++ = 0; + } + + /* Iterate over each digit... */ + for(ix = USED(mp) - 1; ix >= 0; ix--) { + mp_digit d = DIGIT(mp, ix); + int jx; + + /* Unpack digit bytes, high order first */ + for(jx = sizeof(mp_digit) - 1; jx >= 0; jx--) { + unsigned char x = (unsigned char)(d >> (jx * CHAR_BIT)); + if (!pos && !x) /* suppress leading zeros */ + continue; + str[pos++] = x; + } + } + if (!pos) + str[pos++] = 0; + return MP_OKAY; +} /* end mp_to_fixlen_octets() */ +/* }}} */ + + +/*------------------------------------------------------------------------*/ +/* HERE THERE BE DRAGONS */ diff --git a/usr/src/common/mpi/mpi.h b/usr/src/common/mpi/mpi.h new file mode 100644 index 0000000000..c5384df1c1 --- /dev/null +++ b/usr/src/common/mpi/mpi.h @@ -0,0 +1,394 @@ +/* + * mpi.h + * + * Arbitrary precision integer arithmetic library + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the MPI Arbitrary Precision Integer Arithmetic library. + * + * The Initial Developer of the Original Code is + * Michael J. Fromberger. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Netscape Communications Corporation + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _MPI_H +#define _MPI_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* $Id: mpi.h,v 1.22 2004/04/27 23:04:36 gerv%gerv.net Exp $ */ + +#include "mpi-config.h" +#include <sys/param.h> +#ifdef _KERNEL +#include <sys/debug.h> +#include <sys/systm.h> +#define assert ASSERT +#define labs(a) (a >= 0 ? a : -a) +#define UCHAR_MAX 255 +#define memset(s, c, n) bzero(s, n) +#define memcpy(a,b,c) bcopy((caddr_t)b, (caddr_t)a, c) +/* + * Generic #define's to cover missing things in the kernel + */ +#ifndef isdigit +#define isdigit(x) ((x) >= '0' && (x) <= '9') +#endif +#ifndef isupper +#define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z')) +#endif +#ifndef islower +#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z')) +#endif +#ifndef isalpha +#define isalpha(x) (isupper(x) || islower(x)) +#endif +#ifndef toupper +#define toupper(x) (islower(x) ? (x) - 'a' + 'A' : (x)) +#endif +#ifndef tolower +#define tolower(x) (isupper(x) ? (x) + 'a' - 'A' : (x)) +#endif +#ifndef isspace +#define isspace(x) (((x) == ' ') || ((x) == '\r') || ((x) == '\n') || \ + ((x) == '\t') || ((x) == '\b')) +#endif +#endif /* _KERNEL */ + +#if MP_DEBUG +#undef MP_IOFUNC +#define MP_IOFUNC 1 +#endif + +#if MP_IOFUNC +#include <stdio.h> +#include <ctype.h> +#endif + +#ifndef _KERNEL +#include <limits.h> +#endif + +#if defined(BSDI) +#undef ULLONG_MAX +#endif + +#if defined( macintosh ) +#include <Types.h> +#elif defined( _WIN32_WCE) +/* #include <sys/types.h> What do we need here ?? */ +#else +#include <sys/types.h> +#endif + +#define MP_NEG 1 +#define MP_ZPOS 0 + +#define MP_OKAY 0 /* no error, all is well */ +#define MP_YES 0 /* yes (boolean result) */ +#define MP_NO -1 /* no (boolean result) */ +#define MP_MEM -2 /* out of memory */ +#define MP_RANGE -3 /* argument out of range */ +#define MP_BADARG -4 /* invalid parameter */ +#define MP_UNDEF -5 /* answer is undefined */ +#define MP_LAST_CODE MP_UNDEF + +typedef unsigned int mp_sign; +typedef unsigned int mp_size; +typedef int mp_err; +typedef int mp_flag; + +#define MP_32BIT_MAX 4294967295U + +#if !defined(ULONG_MAX) +#error "ULONG_MAX not defined" +#elif !defined(UINT_MAX) +#error "UINT_MAX not defined" +#elif !defined(USHRT_MAX) +#error "USHRT_MAX not defined" +#endif + +#if defined(ULONG_LONG_MAX) /* GCC, HPUX */ +#define MP_ULONG_LONG_MAX ULONG_LONG_MAX +#elif defined(ULLONG_MAX) /* Solaris */ +#define MP_ULONG_LONG_MAX ULLONG_MAX +/* MP_ULONG_LONG_MAX was defined to be ULLONG_MAX */ +#elif defined(ULONGLONG_MAX) /* IRIX, AIX */ +#define MP_ULONG_LONG_MAX ULONGLONG_MAX +#endif + +/* We only use unsigned long for mp_digit iff long is more than 32 bits. */ +#if !defined(MP_USE_UINT_DIGIT) && ULONG_MAX > MP_32BIT_MAX +typedef unsigned long mp_digit; +#define MP_DIGIT_MAX ULONG_MAX +#define MP_DIGIT_FMT "%016lX" /* printf() format for 1 digit */ +#define MP_HALF_DIGIT_MAX UINT_MAX +#undef MP_NO_MP_WORD +#define MP_NO_MP_WORD 1 +#undef MP_USE_LONG_DIGIT +#define MP_USE_LONG_DIGIT 1 +#undef MP_USE_LONG_LONG_DIGIT + +#elif !defined(MP_USE_UINT_DIGIT) && defined(MP_ULONG_LONG_MAX) +typedef unsigned long long mp_digit; +#define MP_DIGIT_MAX MP_ULONG_LONG_MAX +#define MP_DIGIT_FMT "%016llX" /* printf() format for 1 digit */ +#define MP_HALF_DIGIT_MAX UINT_MAX +#undef MP_NO_MP_WORD +#define MP_NO_MP_WORD 1 +#undef MP_USE_LONG_LONG_DIGIT +#define MP_USE_LONG_LONG_DIGIT 1 +#undef MP_USE_LONG_DIGIT + +#else +typedef unsigned int mp_digit; +#define MP_DIGIT_MAX UINT_MAX +#define MP_DIGIT_FMT "%08X" /* printf() format for 1 digit */ +#define MP_HALF_DIGIT_MAX USHRT_MAX +#undef MP_USE_UINT_DIGIT +#define MP_USE_UINT_DIGIT 1 +#undef MP_USE_LONG_LONG_DIGIT +#undef MP_USE_LONG_DIGIT +#endif + +#if !defined(MP_NO_MP_WORD) +#if defined(MP_USE_UINT_DIGIT) && \ + (defined(MP_ULONG_LONG_MAX) || (ULONG_MAX > UINT_MAX)) + +#if (ULONG_MAX > UINT_MAX) +typedef unsigned long mp_word; +typedef long mp_sword; +#define MP_WORD_MAX ULONG_MAX + +#else +typedef unsigned long long mp_word; +typedef long long mp_sword; +#define MP_WORD_MAX MP_ULONG_LONG_MAX +#endif + +#else +#define MP_NO_MP_WORD 1 +#endif +#endif /* !defined(MP_NO_MP_WORD) */ + +#if !defined(MP_WORD_MAX) && defined(MP_DEFINE_SMALL_WORD) +typedef unsigned int mp_word; +typedef int mp_sword; +#define MP_WORD_MAX UINT_MAX +#endif + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#define MP_DIGIT_BIT (CHAR_BIT*sizeof(mp_digit)) +#define MP_WORD_BIT (CHAR_BIT*sizeof(mp_word)) +#define MP_RADIX (1+(mp_word)MP_DIGIT_MAX) + +#define MP_HALF_DIGIT_BIT (MP_DIGIT_BIT/2) +#define MP_HALF_RADIX (1+(mp_digit)MP_HALF_DIGIT_MAX) +/* MP_HALF_RADIX really ought to be called MP_SQRT_RADIX, but it's named +** MP_HALF_RADIX because it's the radix for MP_HALF_DIGITs, and it's +** consistent with the other _HALF_ names. +*/ + + +/* Macros for accessing the mp_int internals */ +#define MP_FLAG(MP) ((MP)->flag) +#define MP_SIGN(MP) ((MP)->sign) +#define MP_USED(MP) ((MP)->used) +#define MP_ALLOC(MP) ((MP)->alloc) +#define MP_DIGITS(MP) ((MP)->dp) +#define MP_DIGIT(MP,N) (MP)->dp[(N)] + +/* This defines the maximum I/O base (minimum is 2) */ +#define MP_MAX_RADIX 64 + +typedef struct { + mp_sign flag; /* KM_SLEEP/KM_NOSLEEP */ + mp_sign sign; /* sign of this quantity */ + mp_size alloc; /* how many digits allocated */ + mp_size used; /* how many digits used */ + mp_digit *dp; /* the digits themselves */ +} mp_int; + +/* Default precision */ +mp_size mp_get_prec(void); +void mp_set_prec(mp_size prec); + +/* Memory management */ +mp_err mp_init(mp_int *mp, int kmflag); +mp_err mp_init_size(mp_int *mp, mp_size prec, int kmflag); +mp_err mp_init_copy(mp_int *mp, const mp_int *from); +mp_err mp_copy(const mp_int *from, mp_int *to); +void mp_exch(mp_int *mp1, mp_int *mp2); +void mp_clear(mp_int *mp); +void mp_zero(mp_int *mp); +void mp_set(mp_int *mp, mp_digit d); +mp_err mp_set_int(mp_int *mp, long z); +#define mp_set_long(mp,z) mp_set_int(mp,z) +mp_err mp_set_ulong(mp_int *mp, unsigned long z); + +/* Single digit arithmetic */ +mp_err mp_add_d(const mp_int *a, mp_digit d, mp_int *b); +mp_err mp_sub_d(const mp_int *a, mp_digit d, mp_int *b); +mp_err mp_mul_d(const mp_int *a, mp_digit d, mp_int *b); +mp_err mp_mul_2(const mp_int *a, mp_int *c); +mp_err mp_div_d(const mp_int *a, mp_digit d, mp_int *q, mp_digit *r); +mp_err mp_div_2(const mp_int *a, mp_int *c); +mp_err mp_expt_d(const mp_int *a, mp_digit d, mp_int *c); + +/* Sign manipulations */ +mp_err mp_abs(const mp_int *a, mp_int *b); +mp_err mp_neg(const mp_int *a, mp_int *b); + +/* Full arithmetic */ +mp_err mp_add(const mp_int *a, const mp_int *b, mp_int *c); +mp_err mp_sub(const mp_int *a, const mp_int *b, mp_int *c); +mp_err mp_mul(const mp_int *a, const mp_int *b, mp_int *c); +#if MP_SQUARE +mp_err mp_sqr(const mp_int *a, mp_int *b); +#else +#define mp_sqr(a, b) mp_mul(a, a, b) +#endif +mp_err mp_div(const mp_int *a, const mp_int *b, mp_int *q, mp_int *r); +mp_err mp_div_2d(const mp_int *a, mp_digit d, mp_int *q, mp_int *r); +mp_err mp_expt(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_2expt(mp_int *a, mp_digit k); +mp_err mp_sqrt(const mp_int *a, mp_int *b); + +/* Modular arithmetic */ +#if MP_MODARITH +mp_err mp_mod(const mp_int *a, const mp_int *m, mp_int *c); +mp_err mp_mod_d(const mp_int *a, mp_digit d, mp_digit *c); +mp_err mp_addmod(const mp_int *a, const mp_int *b, const mp_int *m, mp_int *c); +mp_err mp_submod(const mp_int *a, const mp_int *b, const mp_int *m, mp_int *c); +mp_err mp_mulmod(const mp_int *a, const mp_int *b, const mp_int *m, mp_int *c); +#if MP_SQUARE +mp_err mp_sqrmod(const mp_int *a, const mp_int *m, mp_int *c); +#else +#define mp_sqrmod(a, m, c) mp_mulmod(a, a, m, c) +#endif +mp_err mp_exptmod(const mp_int *a, const mp_int *b, const mp_int *m, mp_int *c); +mp_err mp_exptmod_d(const mp_int *a, mp_digit d, const mp_int *m, mp_int *c); +#endif /* MP_MODARITH */ + +/* Comparisons */ +int mp_cmp_z(const mp_int *a); +int mp_cmp_d(const mp_int *a, mp_digit d); +int mp_cmp(const mp_int *a, const mp_int *b); +int mp_cmp_mag(mp_int *a, mp_int *b); +int mp_cmp_int(const mp_int *a, long z, int kmflag); +int mp_isodd(const mp_int *a); +int mp_iseven(const mp_int *a); + +/* Number theoretic */ +#if MP_NUMTH +mp_err mp_gcd(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_lcm(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_xgcd(const mp_int *a, const mp_int *b, mp_int *g, mp_int *x, mp_int *y); +mp_err mp_invmod(const mp_int *a, const mp_int *m, mp_int *c); +mp_err mp_invmod_xgcd(const mp_int *a, const mp_int *m, mp_int *c); +#endif /* end MP_NUMTH */ + +/* Input and output */ +#if MP_IOFUNC +void mp_print(mp_int *mp, FILE *ofp); +#endif /* end MP_IOFUNC */ + +/* Base conversion */ +mp_err mp_read_raw(mp_int *mp, char *str, int len); +int mp_raw_size(mp_int *mp); +mp_err mp_toraw(mp_int *mp, char *str); +mp_err mp_read_radix(mp_int *mp, const char *str, int radix); +mp_err mp_read_variable_radix(mp_int *a, const char * str, int default_radix); +int mp_radix_size(mp_int *mp, int radix); +mp_err mp_toradix(mp_int *mp, char *str, int radix); +int mp_tovalue(char ch, int r); + +#define mp_tobinary(M, S) mp_toradix((M), (S), 2) +#define mp_tooctal(M, S) mp_toradix((M), (S), 8) +#define mp_todecimal(M, S) mp_toradix((M), (S), 10) +#define mp_tohex(M, S) mp_toradix((M), (S), 16) + +/* Error strings */ +const char *mp_strerror(mp_err ec); + +/* Octet string conversion functions */ +mp_err mp_read_unsigned_octets(mp_int *mp, const unsigned char *str, mp_size len); +int mp_unsigned_octet_size(const mp_int *mp); +mp_err mp_to_unsigned_octets(const mp_int *mp, unsigned char *str, mp_size maxlen); +mp_err mp_to_signed_octets(const mp_int *mp, unsigned char *str, mp_size maxlen); +mp_err mp_to_fixlen_octets(const mp_int *mp, unsigned char *str, mp_size len); + +/* Miscellaneous */ +mp_size mp_trailing_zeros(const mp_int *mp); + +#define MP_CHECKOK(x) if (MP_OKAY > (res = (x))) goto CLEANUP +#define MP_CHECKERR(x) if (MP_OKAY > (res = (x))) goto CLEANUP + +#if defined(MP_API_COMPATIBLE) +#define NEG MP_NEG +#define ZPOS MP_ZPOS +#define DIGIT_MAX MP_DIGIT_MAX +#define DIGIT_BIT MP_DIGIT_BIT +#define DIGIT_FMT MP_DIGIT_FMT +#define RADIX MP_RADIX +#define MAX_RADIX MP_MAX_RADIX +#define FLAG(MP) MP_FLAG(MP) +#define SIGN(MP) MP_SIGN(MP) +#define USED(MP) MP_USED(MP) +#define ALLOC(MP) MP_ALLOC(MP) +#define DIGITS(MP) MP_DIGITS(MP) +#define DIGIT(MP,N) MP_DIGIT(MP,N) + +#if MP_ARGCHK == 1 +#define ARGCHK(X,Y) {if(!(X)){return (Y);}} +#elif MP_ARGCHK == 2 +#ifdef _KERNEL +#define ARGCHK(X,Y) ASSERT(X) +#else +#include <assert.h> +#define ARGCHK(X,Y) assert(X) +#endif +#else +#define ARGCHK(X,Y) /* */ +#endif +#endif /* defined MP_API_COMPATIBLE */ + +#endif /* _MPI_H */ diff --git a/usr/src/common/mpi/mplogic.c b/usr/src/common/mpi/mplogic.c new file mode 100644 index 0000000000..8daa7c0a08 --- /dev/null +++ b/usr/src/common/mpi/mplogic.c @@ -0,0 +1,231 @@ +/* + * mplogic.c + * + * Bitwise logical operations on MPI values + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the MPI Arbitrary Precision Integer Arithmetic library. + * + * The Initial Developer of the Original Code is + * Michael J. Fromberger. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* $Id: mplogic.c,v 1.15 2004/04/27 23:04:36 gerv%gerv.net Exp $ */ + +#include "mpi-priv.h" +#include "mplogic.h" + +/* {{{ Lookup table for population count */ + +static unsigned char bitc[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + +/* }}} */ + +/* + mpl_rsh(a, b, d) - b = a >> d + mpl_lsh(a, b, d) - b = a << d + */ + +/* {{{ mpl_rsh(a, b, d) */ + +mp_err mpl_rsh(const mp_int *a, mp_int *b, mp_digit d) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + s_mp_div_2d(b, d); + + return MP_OKAY; + +} /* end mpl_rsh() */ + +/* }}} */ + +/* {{{ mpl_lsh(a, b, d) */ + +mp_err mpl_lsh(const mp_int *a, mp_int *b, mp_digit d) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + return s_mp_mul_2d(b, d); + +} /* end mpl_lsh() */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* + mpl_set_bit + + Returns MP_OKAY or some error code. + Grows a if needed to set a bit to 1. + */ +mp_err mpl_set_bit(mp_int *a, mp_size bitNum, mp_size value) +{ + mp_size ix; + mp_err rv; + mp_digit mask; + + ARGCHK(a != NULL, MP_BADARG); + + ix = bitNum / MP_DIGIT_BIT; + if (ix + 1 > MP_USED(a)) { + rv = s_mp_pad(a, ix + 1); + if (rv != MP_OKAY) + return rv; + } + + bitNum = bitNum % MP_DIGIT_BIT; + mask = (mp_digit)1 << bitNum; + if (value) + MP_DIGIT(a,ix) |= mask; + else + MP_DIGIT(a,ix) &= ~mask; + s_mp_clamp(a); + return MP_OKAY; +} + +/* + mpl_get_bit + + returns 0 or 1 or some (negative) error code. + */ +mp_err mpl_get_bit(const mp_int *a, mp_size bitNum) +{ + mp_size bit, ix; + mp_err rv; + + ARGCHK(a != NULL, MP_BADARG); + + ix = bitNum / MP_DIGIT_BIT; + ARGCHK(ix <= MP_USED(a) - 1, MP_RANGE); + + bit = bitNum % MP_DIGIT_BIT; + rv = (mp_err)(MP_DIGIT(a, ix) >> bit) & 1; + return rv; +} + +/* + mpl_get_bits + - Extracts numBits bits from a, where the least significant extracted bit + is bit lsbNum. Returns a negative value if error occurs. + - Because sign bit is used to indicate error, maximum number of bits to + be returned is the lesser of (a) the number of bits in an mp_digit, or + (b) one less than the number of bits in an mp_err. + - lsbNum + numbits can be greater than the number of significant bits in + integer a, as long as bit lsbNum is in the high order digit of a. + */ +mp_err mpl_get_bits(const mp_int *a, mp_size lsbNum, mp_size numBits) +{ + mp_size rshift = (lsbNum % MP_DIGIT_BIT); + mp_size lsWndx = (lsbNum / MP_DIGIT_BIT); + mp_digit * digit = MP_DIGITS(a) + lsWndx; + mp_digit mask = ((1 << numBits) - 1); + + ARGCHK(numBits < CHAR_BIT * sizeof mask, MP_BADARG); + ARGCHK(MP_HOWMANY(lsbNum, MP_DIGIT_BIT) <= MP_USED(a), MP_RANGE); + + if ((numBits + lsbNum % MP_DIGIT_BIT <= MP_DIGIT_BIT) || + (lsWndx + 1 >= MP_USED(a))) { + mask &= (digit[0] >> rshift); + } else { + mask &= ((digit[0] >> rshift) | (digit[1] << (MP_DIGIT_BIT - rshift))); + } + return (mp_err)mask; +} + +/* + mpl_significant_bits + returns number of significnant bits in abs(a). + returns 1 if value is zero. + */ +mp_err mpl_significant_bits(const mp_int *a) +{ + mp_err bits = 0; + int ix; + + ARGCHK(a != NULL, MP_BADARG); + + ix = MP_USED(a); + for (ix = MP_USED(a); ix > 0; ) { + mp_digit d; + d = MP_DIGIT(a, --ix); + if (d) { + while (d) { + ++bits; + d >>= 1; + } + break; + } + } + bits += ix * MP_DIGIT_BIT; + if (!bits) + bits = 1; + return bits; +} + +/*------------------------------------------------------------------------*/ +/* HERE THERE BE DRAGONS */ diff --git a/usr/src/common/mpi/mplogic.h b/usr/src/common/mpi/mplogic.h new file mode 100644 index 0000000000..3b28a9bef6 --- /dev/null +++ b/usr/src/common/mpi/mplogic.h @@ -0,0 +1,94 @@ +/* + * mplogic.h + * + * Bitwise logical operations on MPI values + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the MPI Arbitrary Precision Integer Arithmetic library. + * + * The Initial Developer of the Original Code is + * Michael J. Fromberger. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _MPLOGIC_H +#define _MPLOGIC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* $Id: mplogic.h,v 1.7 2004/04/27 23:04:36 gerv%gerv.net Exp $ */ + +#include "mpi.h" + +/* + The logical operations treat an mp_int as if it were a bit vector, + without regard to its sign (an mp_int is represented in a signed + magnitude format). Values are treated as if they had an infinite + string of zeros left of the most-significant bit. + */ + +/* Parity results */ + +#define MP_EVEN MP_YES +#define MP_ODD MP_NO + +/* Bitwise functions */ + +mp_err mpl_not(mp_int *a, mp_int *b); /* one's complement */ +mp_err mpl_and(mp_int *a, mp_int *b, mp_int *c); /* bitwise AND */ +mp_err mpl_or(mp_int *a, mp_int *b, mp_int *c); /* bitwise OR */ +mp_err mpl_xor(mp_int *a, mp_int *b, mp_int *c); /* bitwise XOR */ + +/* Shift functions */ + +mp_err mpl_rsh(const mp_int *a, mp_int *b, mp_digit d); /* right shift */ +mp_err mpl_lsh(const mp_int *a, mp_int *b, mp_digit d); /* left shift */ + +/* Bit count and parity */ + +mp_err mpl_num_set(mp_int *a, int *num); /* count set bits */ +mp_err mpl_num_clear(mp_int *a, int *num); /* count clear bits */ +mp_err mpl_parity(mp_int *a); /* determine parity */ + +/* Get & Set the value of a bit */ + +mp_err mpl_set_bit(mp_int *a, mp_size bitNum, mp_size value); +mp_err mpl_get_bit(const mp_int *a, mp_size bitNum); +mp_err mpl_get_bits(const mp_int *a, mp_size lsbNum, mp_size numBits); +mp_err mpl_significant_bits(const mp_int *a); + +#endif /* _MPLOGIC_H */ diff --git a/usr/src/common/mpi/mpmontg.c b/usr/src/common/mpi/mpmontg.c new file mode 100644 index 0000000000..33aea8b0d6 --- /dev/null +++ b/usr/src/common/mpi/mpmontg.c @@ -0,0 +1,186 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sheueling Chang Shantz <sheueling.chang@sun.com>, + * Stephen Fung <stephen.fung@sun.com>, and + * Douglas Stebila <douglas@stebila.ca> of Sun Laboratories. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* $Id: mpmontg.c,v 1.20 2006/08/29 02:41:38 nelson%bolyard.com Exp $ */ + +/* This file implements moduluar exponentiation using Montgomery's + * method for modular reduction. This file implements the method + * described as "Improvement 1" in the paper "A Cryptogrpahic Library for + * the Motorola DSP56000" by Stephen R. Dusse' and Burton S. Kaliski Jr. + * published in "Advances in Cryptology: Proceedings of EUROCRYPT '90" + * "Lecture Notes in Computer Science" volume 473, 1991, pg 230-244, + * published by Springer Verlag. + */ + +#define MP_USING_CACHE_SAFE_MOD_EXP 1 +#ifndef _KERNEL +#include <string.h> +#include <stddef.h> /* ptrdiff_t */ +#endif +#include "mpi-priv.h" +#include "mplogic.h" +#include "mpprime.h" +#ifdef MP_USING_MONT_MULF +#include "montmulf.h" +#endif + +/* if MP_CHAR_STORE_SLOW is defined, we */ +/* need to know endianness of this platform. */ +#ifdef MP_CHAR_STORE_SLOW +#if !defined(MP_IS_BIG_ENDIAN) && !defined(MP_IS_LITTLE_ENDIAN) +#error "You must define MP_IS_BIG_ENDIAN or MP_IS_LITTLE_ENDIAN\n" \ + " if you define MP_CHAR_STORE_SLOW." +#endif +#endif + +#ifndef STATIC +#define STATIC +#endif + +#define MAX_ODD_INTS 32 /* 2 ** (WINDOW_BITS - 1) */ + +#ifndef _KERNEL +#if defined(_WIN32_WCE) +#define ABORT res = MP_UNDEF; goto CLEANUP +#else +#define ABORT abort() +#endif +#else +#define ABORT res = MP_UNDEF; goto CLEANUP +#endif /* _KERNEL */ + +/* computes T = REDC(T), 2^b == R */ +mp_err s_mp_redc(mp_int *T, mp_mont_modulus *mmm) +{ + mp_err res; + mp_size i; + + i = MP_USED(T) + MP_USED(&mmm->N) + 2; + MP_CHECKOK( s_mp_pad(T, i) ); + for (i = 0; i < MP_USED(&mmm->N); ++i ) { + mp_digit m_i = MP_DIGIT(T, i) * mmm->n0prime; + /* T += N * m_i * (MP_RADIX ** i); */ + MP_CHECKOK( s_mp_mul_d_add_offset(&mmm->N, m_i, T, i) ); + } + s_mp_clamp(T); + + /* T /= R */ + s_mp_div_2d(T, mmm->b); + + if ((res = s_mp_cmp(T, &mmm->N)) >= 0) { + /* T = T - N */ + MP_CHECKOK( s_mp_sub(T, &mmm->N) ); +#ifdef DEBUG + if ((res = mp_cmp(T, &mmm->N)) >= 0) { + res = MP_UNDEF; + goto CLEANUP; + } +#endif + } + res = MP_OKAY; +CLEANUP: + return res; +} + +#if !defined(MP_ASSEMBLY_MUL_MONT) && !defined(MP_MONT_USE_MP_MUL) +mp_err s_mp_mul_mont(const mp_int *a, const mp_int *b, mp_int *c, + mp_mont_modulus *mmm) +{ + mp_digit *pb; + mp_digit m_i; + mp_err res; + mp_size ib; + mp_size useda, usedb; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if (MP_USED(a) < MP_USED(b)) { + const mp_int *xch = b; /* switch a and b, to do fewer outer loops */ + b = a; + a = xch; + } + + MP_USED(c) = 1; MP_DIGIT(c, 0) = 0; + ib = MP_USED(a) + MP_MAX(MP_USED(b), MP_USED(&mmm->N)) + 2; + if((res = s_mp_pad(c, ib)) != MP_OKAY) + goto CLEANUP; + + useda = MP_USED(a); + pb = MP_DIGITS(b); + s_mpv_mul_d(MP_DIGITS(a), useda, *pb++, MP_DIGITS(c)); + s_mp_setz(MP_DIGITS(c) + useda + 1, ib - (useda + 1)); + m_i = MP_DIGIT(c, 0) * mmm->n0prime; + s_mp_mul_d_add_offset(&mmm->N, m_i, c, 0); + + /* Outer loop: Digits of b */ + usedb = MP_USED(b); + for (ib = 1; ib < usedb; ib++) { + mp_digit b_i = *pb++; + + /* Inner product: Digits of a */ + if (b_i) + s_mpv_mul_d_add_prop(MP_DIGITS(a), useda, b_i, MP_DIGITS(c) + ib); + m_i = MP_DIGIT(c, ib) * mmm->n0prime; + s_mp_mul_d_add_offset(&mmm->N, m_i, c, ib); + } + if (usedb < MP_USED(&mmm->N)) { + for (usedb = MP_USED(&mmm->N); ib < usedb; ++ib ) { + m_i = MP_DIGIT(c, ib) * mmm->n0prime; + s_mp_mul_d_add_offset(&mmm->N, m_i, c, ib); + } + } + s_mp_clamp(c); + s_mp_div_2d(c, mmm->b); + if (s_mp_cmp(c, &mmm->N) >= 0) { + MP_CHECKOK( s_mp_sub(c, &mmm->N) ); + } + res = MP_OKAY; + +CLEANUP: + return res; +} +#endif diff --git a/usr/src/common/mpi/mpprime.c b/usr/src/common/mpi/mpprime.c new file mode 100644 index 0000000000..4f0356dfe1 --- /dev/null +++ b/usr/src/common/mpi/mpprime.c @@ -0,0 +1,123 @@ +/* + * mpprime.c + * + * Utilities for finding and working with prime and pseudo-prime + * integers + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the MPI Arbitrary Precision Integer Arithmetic library. + * + * The Initial Developer of the Original Code is + * Michael J. Fromberger. + * Portions created by the Initial Developer are Copyright (C) 1997 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Netscape Communications Corporation + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mpi-priv.h" +#include "mpprime.h" +#include "mplogic.h" +#ifndef _KERNEL +#include <stdlib.h> +#include <string.h> +#else +#include <sys/random.h> +#endif + +#define SMALL_TABLE 0 /* determines size of hard-wired prime table */ + +#ifndef _KERNEL +#define RANDOM() rand() +#else +#define RANDOM() foo_rand() + +static int +foo_rand() +{ + int r; + random_get_pseudo_bytes((uchar_t *)&r, sizeof (r)); + return (r); +} +#endif + +/* + mpp_random(a) + + Assigns a random value to a. This value is generated using the + standard C library's rand() function, so it should not be used for + cryptographic purposes, but it should be fine for primality testing, + since all we really care about there is good statistical properties. + + As many digits as a currently has are filled with random digits. + */ + +mp_err mpp_random(mp_int *a) + +{ + mp_digit next = 0; + unsigned int ix, jx; + + ARGCHK(a != NULL, MP_BADARG); + + for(ix = 0; ix < USED(a); ix++) { + for(jx = 0; jx < sizeof(mp_digit); jx++) { + next = (next << CHAR_BIT) | (RANDOM() & UCHAR_MAX); + } + DIGIT(a, ix) = next; + } + + return MP_OKAY; + +} /* end mpp_random() */ + +/* }}} */ + +/* {{{ mpp_random_size(a, prec) */ + +mp_err mpp_random_size(mp_int *a, mp_size prec) +{ + mp_err res; + + ARGCHK(a != NULL && prec > 0, MP_BADARG); + + if((res = s_mp_pad(a, prec)) != MP_OKAY) + return res; + + return mpp_random(a); + +} /* end mpp_random_size() */ diff --git a/usr/src/common/mpi/mpprime.h b/usr/src/common/mpi/mpprime.h new file mode 100644 index 0000000000..0e330445e4 --- /dev/null +++ b/usr/src/common/mpi/mpprime.h @@ -0,0 +1,78 @@ +/* + * mpprime.h + * + * Utilities for finding and working with prime and pseudo-prime + * integers + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the MPI Arbitrary Precision Integer Arithmetic library. + * + * The Initial Developer of the Original Code is + * Michael J. Fromberger. + * Portions created by the Initial Developer are Copyright (C) 1997 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sun elects to use this software under the MPL license. + */ + +#ifndef _MP_PRIME_H +#define _MP_PRIME_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mpi.h" + +extern const int prime_tab_size; /* number of primes available */ +extern const mp_digit prime_tab[]; + +/* Tests for divisibility */ +mp_err mpp_divis(mp_int *a, mp_int *b); +mp_err mpp_divis_d(mp_int *a, mp_digit d); + +/* Random selection */ +mp_err mpp_random(mp_int *a); +mp_err mpp_random_size(mp_int *a, mp_size prec); + +/* Pseudo-primality testing */ +mp_err mpp_divis_vector(mp_int *a, const mp_digit *vec, int size, int *which); +mp_err mpp_divis_primes(mp_int *a, mp_digit *np); +mp_err mpp_fermat(mp_int *a, mp_digit w); +mp_err mpp_fermat_list(mp_int *a, const mp_digit *primes, mp_size nPrimes); +mp_err mpp_pprime(mp_int *a, int nt); +mp_err mpp_sieve(mp_int *trial, const mp_digit *primes, mp_size nPrimes, + unsigned char *sieve, mp_size nSieve); +mp_err mpp_make_prime(mp_int *start, mp_size nBits, mp_size strong, + unsigned long * nTries); + +#endif /* _MP_PRIME_H */ diff --git a/usr/src/lib/pkcs11/Makefile.softtoken.com b/usr/src/lib/pkcs11/Makefile.softtoken.com index 84a7fc7989..a8a6ace83d 100644 --- a/usr/src/lib/pkcs11/Makefile.softtoken.com +++ b/usr/src/lib/pkcs11/Makefile.softtoken.com @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -60,7 +60,8 @@ LCL_OBJECTS = \ softKeystoreUtil.o \ softSSL.o \ softASN1.o \ - softBlowfishCrypt.o + softBlowfishCrypt.o \ + softEC.o ASFLAGS = $(AS_PICFLAGS) -P -D__STDC__ -D_ASM $(CPPFLAGS) @@ -68,6 +69,16 @@ AES_COBJECTS = aes_cbc_crypt.o aes_impl.o BLOWFISH_COBJECTS = blowfish_cbc_crypt.o blowfish_impl.o ARCFOUR_COBJECTS = arcfour_crypt.o DES_COBJECTS = des_cbc_crypt.o des_impl.o des_ks.o + +ECC_COBJECTS = \ + ec.o ec2_163.o ec2_mont.o ecdecode.o ecl_mult.o ecp_384.o \ + ecp_jac.o ec2_193.o ecl.o ecp_192.o ecp_521.o \ + ecp_jm.o ec2_233.o ecl_curve.o ecp_224.o ecp_aff.o ecp_mont.o \ + ec2_aff.o ec_naf.o ecl_gf.o ecp_256.o oid.o secitem.o \ + ec2_test.o ecp_test.o + +MPI_COBJECTS = mp_gf2m.o mpi.o mpcpucache.o mplogic.o mpmontg.o mpprime.o + RSA_COBJECTS = rsa_impl.o BIGNUM_COBJECTS = bignumimpl.o @@ -75,6 +86,9 @@ AES_OBJECTS = $(AES_COBJECTS) $(AES_PSR_OBJECTS) BLOWFISH_OBJECTS = $(BLOWFISH_COBJECTS) $(BLOWFISH_PSR_OBJECTS) ARCFOUR_OBJECTS = $(ARCFOUR_COBJECTS) $(ARCFOUR_PSR_OBJECTS) DES_OBJECTS = $(DES_COBJECTS) $(DES_PSR_OBJECTS) + +ECC_OBJECTS = $(ECC_COBJECTS) $(ECC_PSR_OBJECTS) +MPI_OBJECTS = $(MPI_COBJECTS) $(MPI_PSR_OBJECTS) RSA_OBJECTS = $(RSA_COBJECTS) $(RSA_PSR_OBJECTS) SHA1_OBJECTS = $(SHA1_COBJECTS) $(SHA1_PSR_OBJECTS) SHA2_OBJECTS = $(SHA2_COBJECTS) $(SHA2_PSR_OBJECTS) @@ -111,16 +125,20 @@ OBJECTS = \ $(BLOWFISH_OBJECTS) \ $(ARCFOUR_OBJECTS) \ $(DES_OBJECTS) \ + $(MPI_OBJECTS) \ $(RSA_OBJECTS) \ $(SHA1_OBJECTS) \ $(SHA2_OBJECTS) \ $(BIGNUM_OBJECTS) \ - $(BER_OBJECTS) + $(BER_OBJECTS) \ + $(ECC_OBJECTS) AESDIR= $(SRC)/common/crypto/aes BLOWFISHDIR= $(SRC)/common/crypto/blowfish ARCFOURDIR= $(SRC)/common/crypto/arcfour DESDIR= $(SRC)/common/crypto/des +ECCDIR= $(SRC)/common/crypto/ecc +MPIDIR= $(SRC)/common/mpi RSADIR= $(SRC)/common/crypto/rsa BIGNUMDIR= $(SRC)/common/bignum BERDIR= ../../../libldap5/sources/ldap/ber @@ -138,29 +156,47 @@ SRCS = \ $(BLOWFISH_COBJECTS:%.o=$(BLOWFISHDIR)/%.c) \ $(ARCFOUR_COBJECTS:%.o=$(ARCFOURDIR)/%.c) \ $(DES_COBJECTS:%.o=$(DESDIR)/%.c) \ + $(MPI_COBJECTS:%.o=$(MPIDIR)/%.c) \ $(RSA_COBJECTS:%.o=$(RSADIR)/%.c) \ $(SHA1_COBJECTS:%.o=$(SHA1DIR)/%.c) \ $(SHA2_COBJECTS:%.o=$(SHA2DIR)/%.c) \ $(BIGNUM_COBJECTS:%.o=$(BIGNUMDIR)/%.c) \ - $(BIGNUM_PSR_SRCS) + $(BIGNUM_PSR_SRCS) \ + $(ECC_COBJECTS:%.o=$(ECCDIR)/%.c) LIBS = $(DYNLIB) LDLIBS += -lc -lmd -lcryptoutil CFLAGS += $(CCVERBOSE) CPPFLAGS += -I$(AESDIR) -I$(BLOWFISHDIR) -I$(ARCFOURDIR) -I$(DESDIR) \ - -I$(RSADIR) -I$(SRCDIR) -I$(BIGNUMDIR) -D_POSIX_PTHREAD_SEMANTICS + -I$(ECCDIR) -I$(MPIDIR) -I$(RSADIR) -I$(SRCDIR) -I$(BIGNUMDIR) \ + -D_POSIX_PTHREAD_SEMANTICS -DMP_API_COMPATIBLE \ + -DNSS_ECC_MORE_THAN_SUITE_B + LINTFLAGS64 += -errchk=longptr64 ROOTLIBDIR= $(ROOT)/usr/lib/security ROOTLIBDIR64= $(ROOT)/usr/lib/security/$(MACH64) +LINTSRC = \ + $(LCL_OBJECTS:%.o=$(SRCDIR)/%.c) \ + $(AES_COBJECTS:%.o=$(AESDIR)/%.c) \ + $(BLOWFISH_COBJECTS:%.o=$(BLOWFISHDIR)/%.c) \ + $(ARCFOUR_COBJECTS:%.o=$(ARCFOURDIR)/%.c) \ + $(DES_COBJECTS:%.o=$(DESDIR)/%.c) \ + $(RSA_COBJECTS:%.o=$(RSADIR)/%.c) \ + $(SHA1_COBJECTS:%.o=$(SHA1DIR)/%.c) \ + $(SHA2_COBJECTS:%.o=$(SHA2DIR)/%.c) \ + $(BIGNUM_COBJECTS:%.o=$(BIGNUMDIR)/%.c) \ + $(BIGNUM_PSR_SRCS) + .KEEP_STATE: all: $(LIBS) -lint: lintcheck +lint: $$(LINTSRC) + $(LINT.c) $(LINTCHECKFLAGS) $(LINTSRC) $(LDLIBS) pics/%.o: $(AESDIR)/%.c $(COMPILE.c) -o $@ $< @@ -178,6 +214,14 @@ pics/%.o: $(DESDIR)/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) +pics/%.o: $(ECCDIR)/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +pics/%.o: $(MPIDIR)/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + pics/%.o: $(RSADIR)/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) diff --git a/usr/src/lib/pkcs11/Makefile.softtoken.i386 b/usr/src/lib/pkcs11/Makefile.softtoken.i386 index cfc86deae2..184fb4dd6b 100644 --- a/usr/src/lib/pkcs11/Makefile.softtoken.i386 +++ b/usr/src/lib/pkcs11/Makefile.softtoken.i386 @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# 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. @@ -20,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -42,6 +41,8 @@ BIGNUM_PSR_SRCS = \ include ../Makefile.com +CPPFLAGS += -DMP_USE_UINT_DIGIT + install: all $(ROOTLIBS) $(ROOTLINKS) DYNFLAGS += -M $(BIGNUMDIR)/i386/cap_mapfile diff --git a/usr/src/lib/pkcs11/Makefile.softtoken.sparc b/usr/src/lib/pkcs11/Makefile.softtoken.sparc index a8045257e2..b6f7143d1d 100644 --- a/usr/src/lib/pkcs11/Makefile.softtoken.sparc +++ b/usr/src/lib/pkcs11/Makefile.softtoken.sparc @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -36,7 +36,8 @@ BIGNUM_PSR_OBJECTS = mont_mulf_sparc.o BIGNUM_CFG = -DUSE_FLOATING_POINT include ../Makefile.com -CFLAGS += -Dsun4u +CFLAGS += -Dsun4u +CPPFLAGS += -DMP_USE_UINT_DIGIT install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/pkcs11/pkcs11_kernel/Makefile.com b/usr/src/lib/pkcs11/pkcs11_kernel/Makefile.com index 5ab68ffb12..4e71cf6202 100644 --- a/usr/src/lib/pkcs11/pkcs11_kernel/Makefile.com +++ b/usr/src/lib/pkcs11/pkcs11_kernel/Makefile.com @@ -57,14 +57,18 @@ OBJECTS= \ $(ST_OBJECTS) AESDIR= $(SRC)/common/crypto/aes -BLOWFISHDIR= $(SRC)/common/crypto/blowfish ARCFOURDIR= $(SRC)/common/crypto/arcfour +BLOWFISHDIR= $(SRC)/common/crypto/blowfish DESDIR= $(SRC)/common/crypto/des +ECCDIR= $(SRC)/common/crypto/ecc ST_DIR= $(SRC)/lib/pkcs11/pkcs11_softtoken/common lint \ pics/kernelAttributeUtil.o := \ - CPPFLAGS += -I$(AESDIR) -I$(BLOWFISHDIR) -I$(ARCFOURDIR) -I$(DESDIR) + CPPFLAGS += -I$(AESDIR) -I$(BLOWFISHDIR) -I$(ARCFOURDIR) -I$(DESDIR) \ + -I$(ECCDIR) +pics/kernelKeys.o := \ + CPPFLAGS += -I$(ECCDIR) pics/kernelSoftCommon.o := \ CPPFLAGS = -I$(ST_DIR) $(CPPFLAGS.master) diff --git a/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelKeys.c b/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelKeys.c index 9c8de70926..91fc62cbee 100644 --- a/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelKeys.c +++ b/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelKeys.c @@ -27,6 +27,7 @@ #include <strings.h> #include <errno.h> +#include <ecc_impl.h> #include <security/cryptoki.h> #include <sys/crypto/ioctl.h> #include "kernelGlobal.h" @@ -197,7 +198,7 @@ get_key_len_from_template(CK_MECHANISM_PTR pMechanism, } *key_len = tmp.ulValueLen; } else if (pMechanism->mechanism == CKM_ECDH1_DERIVE) { - *key_len = 72; + *key_len = EC_MAX_VALUE_LEN; } else { return (CKR_ARGUMENTS_BAD); } @@ -1145,8 +1146,8 @@ key_gen_ec_by_value(CK_MECHANISM_PTR pMechanism, CK_BBOOL is_token_obj2 = FALSE; uint_t pub_attr_count, pri_attr_count; uint_t pub_out_attr_count = 0, pri_out_attr_count = 0; - char value[72]; - char point[145]; + char value[EC_MAX_VALUE_LEN]; + char point[EC_MAX_POINT_LEN]; CK_ULONG pub_class = CKO_PUBLIC_KEY; CK_ULONG pri_class = CKO_PRIVATE_KEY; CK_ULONG key_type; diff --git a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softAttributeUtil.c b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softAttributeUtil.c index 271270b49e..6ec00bd851 100644 --- a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softAttributeUtil.c +++ b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softAttributeUtil.c @@ -364,7 +364,7 @@ copy_cert_attr(cert_attr_t *src_attr, cert_attr_t **dest_attr) } (void) memcpy((*dest_attr)->value, src_attr->value, - src_attr->length); + src_attr->length); (*dest_attr)->length = src_attr->length; } @@ -504,21 +504,21 @@ soft_copy_certificate(certificate_obj_t *oldcert, certificate_obj_t **newcert, x509 = oldcert->cert_type_u.x509; if (x509.subject) if ((rv = copy_cert_attr(x509.subject, - &cert->cert_type_u.x509.subject))) + &cert->cert_type_u.x509.subject))) return (rv); if (x509.value) if ((rv = copy_cert_attr(x509.value, - &cert->cert_type_u.x509.value))) + &cert->cert_type_u.x509.value))) return (rv); } else if (type == CKC_X_509_ATTR_CERT) { x509_attr = oldcert->cert_type_u.x509_attr; if (x509_attr.owner) if ((rv = copy_cert_attr(x509_attr.owner, - &cert->cert_type_u.x509_attr.owner))) + &cert->cert_type_u.x509_attr.owner))) return (rv); if (x509_attr.value) if ((rv = copy_cert_attr(x509_attr.value, - &cert->cert_type_u.x509_attr.value))) + &cert->cert_type_u.x509_attr.value))) return (rv); } else { /* wrong certificate type */ @@ -549,7 +549,7 @@ soft_copy_extra_attr(CK_ATTRIBUTE_INFO_PTR old_attrp, soft_object_t *object_p) attrp->attr.ulValueLen = old_attrp->attr.ulValueLen; if ((old_attrp->attr.pValue != NULL) && - (old_attrp->attr.ulValueLen > 0)) { + (old_attrp->attr.ulValueLen > 0)) { attrp->attr.pValue = malloc(old_attrp->attr.ulValueLen); if (attrp->attr.pValue == NULL) { free(attrp); @@ -927,7 +927,7 @@ get_cert_attr_from_template(cert_attr_t **dest, CK_ATTRIBUTE_PTR src) if (*dest != NULL) { if ((*dest)->value != NULL) { (void) memset((*dest)->value, 0, - (*dest)->length); + (*dest)->length); free((*dest)->value); } } else { @@ -1065,8 +1065,6 @@ soft_cleanup_object_bigint_attrs(soft_object_t *object_p) object_p)); break; case CKK_EC: - bigint_attr_cleanup(OBJ_PUB_EC_PARAM( - object_p)); bigint_attr_cleanup(OBJ_PUB_EC_POINT( object_p)); break; @@ -1132,8 +1130,6 @@ soft_cleanup_object_bigint_attrs(soft_object_t *object_p) break; case CKK_EC: - bigint_attr_cleanup(OBJ_PRI_EC_PARAM( - object_p)); bigint_attr_cleanup(OBJ_PRI_EC_VALUE( object_p)); break; @@ -1151,14 +1147,14 @@ soft_cleanup_object_bigint_attrs(soft_object_t *object_p) if (OBJ_SEC_VALUE(object_p) != NULL && OBJ_SEC_VALUE_LEN(object_p) > 0) { (void) memset(OBJ_SEC_VALUE(object_p), 0, - OBJ_SEC_VALUE_LEN(object_p)); + OBJ_SEC_VALUE_LEN(object_p)); free(OBJ_SEC_VALUE(object_p)); } /* cleanup key schedule data area */ if (OBJ_KEY_SCHED(object_p) != NULL && OBJ_KEY_SCHED_LEN(object_p) > 0) { (void) memset(OBJ_KEY_SCHED(object_p), 0, - OBJ_KEY_SCHED_LEN(object_p)); + OBJ_KEY_SCHED_LEN(object_p)); free(OBJ_KEY_SCHED(object_p)); } @@ -1304,11 +1300,13 @@ soft_build_public_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, biginteger_t modulus; biginteger_t pubexpo; - biginteger_t prime; /* Shared with CKA_EC_PARAMS */ - biginteger_t subprime; /* Shared with CKA_EC_POINT */ + biginteger_t prime; + biginteger_t subprime; biginteger_t base; biginteger_t value; + biginteger_t point; CK_ATTRIBUTE string_tmp; + CK_ATTRIBUTE param_tmp; public_key_obj_t *pbk; uchar_t object_type = 0; @@ -1320,7 +1318,9 @@ soft_build_public_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, (void) memset(&subprime, 0x0, sizeof (biginteger_t)); (void) memset(&base, 0x0, sizeof (biginteger_t)); (void) memset(&value, 0x0, sizeof (biginteger_t)); + (void) memset(&point, 0x0, sizeof (biginteger_t)); string_tmp.pValue = NULL; + param_tmp.pValue = NULL; for (i = 0; i < ulAttrNum; i++) { @@ -1472,7 +1472,7 @@ soft_build_public_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, (template[i].pValue == NULL)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; - } + } } rv = get_bigint_attr_from_template(&value, @@ -1497,17 +1497,14 @@ soft_build_public_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, case CKA_EC_PARAMS: isECParam = 1; - /* use prime variable for ec_param */ - rv = get_bigint_attr_from_template(&prime, - &template[i]); + rv = get_string_from_template(¶m_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EC_POINT: isECPoint = 1; - /* use subprime variable for ec_point */ - rv = get_bigint_attr_from_template(&subprime, + rv = get_bigint_attr_from_template(&point, &template[i]); if (rv != CKR_OK) goto fail_cleanup; @@ -1705,18 +1702,35 @@ soft_build_public_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, break; case CKK_EC: - if (isModulusBits || isModulus || isPubExpo || isPrime || - isSubprime || isBase || isValue) { - rv = CKR_TEMPLATE_INCONSISTENT; - goto fail_cleanup; + if (mode == SOFT_CREATE_OBJ) { + if (isModulusBits || isModulus || isPubExpo || + isPrime || isSubprime || isBase || isValue) { + rv = CKR_TEMPLATE_INCONSISTENT; + goto fail_cleanup; - } else if (!isECParam && !isECPoint) { - rv = CKR_TEMPLATE_INCOMPLETE; - goto fail_cleanup; + } else if (!isECParam || !isECPoint) { + rv = CKR_TEMPLATE_INCOMPLETE; + goto fail_cleanup; + } + } else { + if (isModulusBits || isModulus || isPubExpo || + isPrime || isSubprime || isBase || isValue) { + rv = CKR_TEMPLATE_INCONSISTENT; + goto fail_cleanup; + + } else if (!isECParam) { + rv = CKR_TEMPLATE_INCOMPLETE; + goto fail_cleanup; + } } - copy_bigint_attr(&prime, KEY_PUB_EC_PARAM(pbk)); - copy_bigint_attr(&subprime, KEY_PUB_EC_POINT(pbk)); + if (isECPoint) { + copy_bigint_attr(&point, KEY_PUB_EC_POINT(pbk)); + } + rv = soft_add_extra_attr(¶m_tmp, new_object); + if (rv != CKR_OK) + goto fail_cleanup; + string_attr_cleanup(¶m_tmp); break; default: @@ -1746,7 +1760,9 @@ fail_cleanup: bigint_attr_cleanup(&subprime); bigint_attr_cleanup(&base); bigint_attr_cleanup(&value); + bigint_attr_cleanup(&point); string_attr_cleanup(&string_tmp); + string_attr_cleanup(¶m_tmp); /* * cleanup the storage allocated inside the object itself. @@ -1818,6 +1834,7 @@ soft_build_private_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, biginteger_t expo2; biginteger_t coef; CK_ATTRIBUTE string_tmp; + CK_ATTRIBUTE param_tmp; BIGNUM x, q; private_key_obj_t *pvk; @@ -1837,6 +1854,7 @@ soft_build_private_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, (void) memset(&expo2, 0x0, sizeof (biginteger_t)); (void) memset(&coef, 0x0, sizeof (biginteger_t)); string_tmp.pValue = NULL; + param_tmp.pValue = NULL; x.malloced = 0; q.malloced = 0; @@ -2082,8 +2100,7 @@ soft_build_private_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, case CKA_EC_PARAMS: isECParam = 1; - /* use prime variable for ec_param */ - rv = get_bigint_attr_from_template(&prime, + rv = get_string_from_template(¶m_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; @@ -2236,22 +2253,26 @@ soft_build_private_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, #ifdef __sparcv9 /* LINTED */ if (big_init(&x, ((int)value.big_value_len + + (int)sizeof (uint32_t) - 1) / + (int)sizeof (uint32_t)) != BIG_OK) { #else /* !__sparcv9 */ if (big_init(&x, (value.big_value_len + -#endif /* __sparcv9 */ (int)sizeof (uint32_t) - 1) / (int)sizeof (uint32_t)) != BIG_OK) { +#endif /* __sparcv9 */ rv = CKR_HOST_MEMORY; goto fail_cleanup; } #ifdef __sparcv9 /* LINTED */ if (big_init(&q, ((int)subprime.big_value_len + + (int)sizeof (uint32_t) - 1) / + (int)sizeof (uint32_t)) != BIG_OK) { #else /* !__sparcv9 */ if (big_init(&q, (subprime.big_value_len + -#endif /* __sparcv9 */ (int)sizeof (uint32_t) - 1) / (int)sizeof (uint32_t)) != BIG_OK) { +#endif /* __sparcv9 */ rv = CKR_HOST_MEMORY; goto fail_cleanup; } @@ -2344,7 +2365,7 @@ soft_build_private_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, copy_bigint_attr(&base, KEY_PRI_DH942_BASE(pvk)); copy_bigint_attr(&subprime, - KEY_PRI_DH942_SUBPRIME(pvk)); + KEY_PRI_DH942_SUBPRIME(pvk)); copy_bigint_attr(&value, KEY_PRI_DH942_VALUE(pvk)); } else { @@ -2356,17 +2377,19 @@ soft_build_private_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, case CKK_EC: if (isModulus || isPubExpo || isPrime || isPrime1 || isPrime2 || isExpo1 || isExpo2 || isCoef || - isValueBits || isBase) { + isValueBits || isBase) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; - } else if (!isECParam && !isValue) { - rv = CKR_TEMPLATE_INCOMPLETE; - goto fail_cleanup; + } else if (isECParam) { + rv = soft_add_extra_attr(¶m_tmp, new_object); + if (rv != CKR_OK) + goto fail_cleanup; + string_attr_cleanup(¶m_tmp); + } + if (isValue) { + copy_bigint_attr(&value, KEY_PRI_EC_VALUE(pvk)); } - - copy_bigint_attr(&prime, KEY_PRI_EC_PARAM(pvk)); - copy_bigint_attr(&value, KEY_PRI_EC_VALUE(pvk)); break; default: @@ -2405,6 +2428,7 @@ fail_cleanup: bigint_attr_cleanup(&expo2); bigint_attr_cleanup(&coef); string_attr_cleanup(&string_tmp); + string_attr_cleanup(¶m_tmp); big_finish(&x); big_finish(&q); @@ -2814,7 +2838,7 @@ soft_build_secret_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, } if ((sck->sk_value_len < ARCFOUR_MIN_KEY_BYTES) || - (sck->sk_value_len > ARCFOUR_MAX_KEY_BYTES)) { + (sck->sk_value_len > ARCFOUR_MAX_KEY_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } @@ -2942,12 +2966,10 @@ soft_build_secret_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, case CKK_BLOWFISH: if (isValueLen && ((sck->sk_value_len < BLOWFISH_MINBYTES) || - (sck->sk_value_len > BLOWFISH_MAXBYTES))) { - rv = CKR_ATTRIBUTE_VALUE_INVALID; - goto fail_cleanup; - } - - + (sck->sk_value_len > BLOWFISH_MAXBYTES))) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + goto fail_cleanup; + } break; case CKK_DES: @@ -3016,12 +3038,10 @@ soft_build_secret_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, case CKK_BLOWFISH: if (isValueLen && ((sck->sk_value_len < BLOWFISH_MINBYTES) || - (sck->sk_value_len > BLOWFISH_MAXBYTES))) { - rv = CKR_ATTRIBUTE_VALUE_INVALID; - goto fail_cleanup; - } - - + (sck->sk_value_len > BLOWFISH_MAXBYTES))) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + goto fail_cleanup; + } break; case CKK_DES: @@ -3298,7 +3318,7 @@ soft_build_domain_parameters_object(CK_ATTRIBUTE_PTR template, copy_bigint_attr(&base, KEY_DOM_DH942_BASE(dom)); copy_bigint_attr(&subprime, - KEY_DOM_DH942_SUBPRIME(dom)); + KEY_DOM_DH942_SUBPRIME(dom)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; @@ -3421,19 +3441,19 @@ soft_build_certificate_object(CK_ATTRIBUTE_PTR template, switch (template[i].type) { case CKA_SUBJECT: rv = get_cert_attr_from_template( - &cert->cert_type_u.x509.subject, - &template[i]); + &cert->cert_type_u.x509.subject, + &template[i]); break; case CKA_VALUE: rv = get_cert_attr_from_template( - &cert->cert_type_u.x509.value, - &template[i]); + &cert->cert_type_u.x509.value, + &template[i]); break; case CKA_LABEL: isLabel = 1; rv = get_string_from_template( - &string_tmp, - &template[i]); + &string_tmp, + &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; @@ -3441,7 +3461,7 @@ soft_build_certificate_object(CK_ATTRIBUTE_PTR template, case CKA_ISSUER: case CKA_SERIAL_NUMBER: rv = soft_add_extra_attr(&template[i], - new_object); + new_object); break; case CKA_MODIFIABLE: if ((*(CK_BBOOL *)template[i].pValue) == @@ -3452,10 +3472,10 @@ soft_build_certificate_object(CK_ATTRIBUTE_PTR template, case CKA_CERTIFICATE_TYPE: break; default: - rv = soft_parse_common_attrs(&template[i], - &object_type); - if (rv != CKR_OK) - goto fail_cleanup; + rv = soft_parse_common_attrs( + &template[i], &object_type); + if (rv != CKR_OK) + goto fail_cleanup; } break; case CKC_X_509_ATTR_CERT: @@ -3473,7 +3493,7 @@ soft_build_certificate_object(CK_ATTRIBUTE_PTR template, case CKA_LABEL: isLabel = 1; rv = get_string_from_template( - &string_tmp, &template[i]); + &string_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; @@ -3481,7 +3501,7 @@ soft_build_certificate_object(CK_ATTRIBUTE_PTR template, case CKA_AC_ISSUER: case CKA_ATTR_TYPES: rv = soft_add_extra_attr(&template[i], - new_object); + new_object); break; case CKA_MODIFIABLE: @@ -3493,11 +3513,11 @@ soft_build_certificate_object(CK_ATTRIBUTE_PTR template, case CKA_CERTIFICATE_TYPE: break; default: - rv = soft_parse_common_attrs(&template[i], - &object_type); - if (rv != CKR_OK) - goto fail_cleanup; - break; + rv = soft_parse_common_attrs( + &template[i], &object_type); + if (rv != CKR_OK) + goto fail_cleanup; + break; } break; default: @@ -3590,7 +3610,7 @@ soft_build_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, case CKO_CERTIFICATE: rv = soft_build_certificate_object(template, ulAttrNum, - new_object, (CK_CERTIFICATE_TYPE)~0UL); + new_object, (CK_CERTIFICATE_TYPE)~0UL); break; case CKO_DATA: @@ -3823,6 +3843,7 @@ soft_get_public_key_attribute(soft_object_t *object_p, switch (template->type) { case CKA_SUBJECT: + case CKA_EC_PARAMS: /* * The above extra attributes have byte array type. */ @@ -3934,13 +3955,9 @@ soft_get_public_key_attribute(soft_object_t *object_p, return (CKR_ATTRIBUTE_TYPE_INVALID); } - case CKA_EC_PARAMS: - return (get_bigint_attr_from_object(OBJ_PUB_EC_PARAM( - object_p), template)); - case CKA_EC_POINT: - return (get_bigint_attr_from_object(OBJ_PUB_EC_POINT( - object_p), template)); + return (get_bigint_attr_from_object( + OBJ_PUB_EC_POINT(object_p), template)); case CKA_VALUE: switch (keytype) { @@ -4021,6 +4038,7 @@ soft_get_private_key_attribute(soft_object_t *object_p, switch (template->type) { case CKA_SUBJECT: + case CKA_EC_PARAMS: /* * The above extra attributes have byte array type. */ @@ -4207,10 +4225,6 @@ soft_get_private_key_attribute(soft_object_t *object_p, return (CKR_ATTRIBUTE_TYPE_INVALID); } - case CKA_EC_PARAMS: - return (get_bigint_attr_from_object(OBJ_PRI_EC_PARAM( - object_p), template)); - case CKA_VALUE: switch (keytype) { case CKK_DSA: @@ -4504,24 +4518,22 @@ soft_get_certificate_attribute(soft_object_t *object_p, case CKA_SUBJECT: if (certtype == CKC_X_509) { return (get_cert_attr_from_object( - X509_CERT_SUBJECT(object_p), template)); + X509_CERT_SUBJECT(object_p), template)); } break; case CKA_VALUE: if (certtype == CKC_X_509) { - return (get_cert_attr_from_object( - X509_CERT_VALUE(object_p), template)); + return (get_cert_attr_from_object( + X509_CERT_VALUE(object_p), template)); } else if (certtype == CKC_X_509_ATTR_CERT) { - return (get_cert_attr_from_object( - X509_ATTR_CERT_VALUE(object_p), - template)); + return (get_cert_attr_from_object( + X509_ATTR_CERT_VALUE(object_p), template)); } break; case CKA_OWNER: if (certtype == CKC_X_509_ATTR_CERT) { return (get_cert_attr_from_object( - X509_ATTR_CERT_OWNER(object_p), - template)); + X509_ATTR_CERT_OWNER(object_p), template)); } break; case CKA_CERTIFICATE_TYPE: @@ -4531,7 +4543,7 @@ soft_get_certificate_attribute(soft_object_t *object_p, break; case CKA_TRUSTED: return (get_bool_attr_from_object(object_p, - TRUSTED_BOOL_ON, template)); + TRUSTED_BOOL_ON, template)); case CKA_ID: case CKA_ISSUER: case CKA_SERIAL_NUMBER: @@ -4542,7 +4554,7 @@ soft_get_certificate_attribute(soft_object_t *object_p, break; default: return (soft_get_common_attrs(object_p, template, - object_p->object_type)); + object_p->object_type)); break; } @@ -4580,20 +4592,20 @@ soft_set_certificate_attribute(soft_object_t *object_p, case CKA_ISSUER: if (certtype == CKC_X_509) { return (set_extra_attr_to_object(object_p, - template->type, template)); + template->type, template)); } break; case CKA_AC_ISSUER: case CKA_ATTR_TYPES: if (certtype == CKC_X_509_ATTR_CERT) { return (set_extra_attr_to_object(object_p, - template->type, template)); + template->type, template)); } break; case CKA_SERIAL_NUMBER: case CKA_LABEL: return (set_extra_attr_to_object(object_p, - template->type, template)); + template->type, template)); break; default: return (soft_set_common_storage_attribute( @@ -5711,6 +5723,9 @@ free_public_key_attr(public_key_obj_t *pbk, CK_KEY_TYPE key_type) bigint_attr_cleanup(KEY_PUB_DH_BASE(pbk)); bigint_attr_cleanup(KEY_PUB_DH_VALUE(pbk)); break; + case CKK_EC: + bigint_attr_cleanup(KEY_PUB_EC_POINT(pbk)); + break; case CKK_X9_42_DH: bigint_attr_cleanup(KEY_PUB_DH942_PRIME(pbk)); bigint_attr_cleanup(KEY_PUB_DH942_SUBPRIME(pbk)); @@ -5739,7 +5754,7 @@ soft_copy_public_key_attr(public_key_obj_t *old_pub_key_obj_p, switch (key_type) { case CKK_RSA: (void) memcpy(KEY_PUB_RSA(pbk), - KEY_PUB_RSA(old_pub_key_obj_p), + KEY_PUB_RSA(old_pub_key_obj_p), sizeof (rsa_pub_key_t)); /* copy modulus */ rv = copy_bigint(KEY_PUB_RSA_MOD(pbk), @@ -5822,6 +5837,19 @@ soft_copy_public_key_attr(public_key_obj_t *old_pub_key_obj_p, return (rv); } break; + case CKK_EC: + (void) memcpy(KEY_PUB_EC(pbk), + KEY_PUB_EC(old_pub_key_obj_p), + sizeof (ec_pub_key_t)); + + /* copy point */ + rv = copy_bigint(KEY_PUB_EC_POINT(pbk), + KEY_PUB_EC_POINT(old_pub_key_obj_p)); + if (rv != CKR_OK) { + free_public_key_attr(pbk, key_type); + return (rv); + } + break; case CKK_X9_42_DH: (void) memcpy(KEY_PUB_DH942(pbk), KEY_PUB_DH942(old_pub_key_obj_p), @@ -5895,6 +5923,9 @@ free_private_key_attr(private_key_obj_t *pbk, CK_KEY_TYPE key_type) bigint_attr_cleanup(KEY_PRI_DH_BASE(pbk)); bigint_attr_cleanup(KEY_PRI_DH_VALUE(pbk)); break; + case CKK_EC: + bigint_attr_cleanup(KEY_PRI_EC_VALUE(pbk)); + break; case CKK_X9_42_DH: bigint_attr_cleanup(KEY_PRI_DH942_PRIME(pbk)); bigint_attr_cleanup(KEY_PRI_DH942_SUBPRIME(pbk)); @@ -6047,6 +6078,19 @@ soft_copy_private_key_attr(private_key_obj_t *old_pri_key_obj_p, return (rv); } break; + case CKK_EC: + (void) memcpy(KEY_PRI_EC(pbk), + KEY_PRI_EC(old_pri_key_obj_p), + sizeof (ec_pri_key_t)); + + /* copy value */ + rv = copy_bigint(KEY_PRI_EC_VALUE(pbk), + KEY_PRI_EC_VALUE(old_pri_key_obj_p)); + if (rv != CKR_OK) { + free_private_key_attr(pbk, key_type); + return (rv); + } + break; case CKK_X9_42_DH: (void) memcpy(KEY_PRI_DH942(pbk), KEY_PRI_DH942(old_pri_key_obj_p), @@ -6254,7 +6298,7 @@ soft_copy_secret_key_attr(secret_key_obj_t *old_secret_key_obj_p, } sk->keysched_len = old_secret_key_obj_p->keysched_len; (void) memcpy(sk->key_sched, old_secret_key_obj_p->key_sched, - sk->keysched_len); + sk->keysched_len); } *new_secret_key_obj_p = sk; diff --git a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c new file mode 100644 index 0000000000..e57fb014dd --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c @@ -0,0 +1,635 @@ +/* + * 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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/crypto/common.h> +#include <security/cryptoki.h> +#include <bignum.h> +#include <des_impl.h> +#include "softGlobal.h" +#include "softSession.h" +#include "softObject.h" +#include "softEC.h" +#include "softRandom.h" +#include "softCrypt.h" +#include "softOps.h" +#include "softMAC.h" + +void +soft_free_ecparams(ECParams *params, boolean_t freeit) +{ + SECITEM_FreeItem(¶ms->fieldID.u.prime, B_FALSE); + SECITEM_FreeItem(¶ms->curve.a, B_FALSE); + SECITEM_FreeItem(¶ms->curve.b, B_FALSE); + SECITEM_FreeItem(¶ms->curve.seed, B_FALSE); + SECITEM_FreeItem(¶ms->base, B_FALSE); + SECITEM_FreeItem(¶ms->order, B_FALSE); + SECITEM_FreeItem(¶ms->DEREncoding, B_FALSE); + SECITEM_FreeItem(¶ms->curveOID, B_FALSE); + if (freeit) + free(params); +} + +static void +soft_free_ecc_context(soft_ecc_ctx_t *ecc_ctx) +{ + if (ecc_ctx != NULL) { + if (ecc_ctx->key != NULL) { + soft_cleanup_object(ecc_ctx->key); + free(ecc_ctx->key); + } + + soft_free_ecparams(&ecc_ctx->ecparams, B_FALSE); + free(ecc_ctx); + } +} + +void +soft_free_ecprivkey(ECPrivateKey *key) +{ + soft_free_ecparams(&key->ecParams, B_FALSE); + /* + * Don't free publicValue or privateValue + * as these values are copied into objects. + */ + SECITEM_FreeItem(&key->version, B_FALSE); + free(key); +} + +/* + * Called from init routines to do basic sanity checks. Init routines, + * e.g. sign_init should fail rather than subsequent operations. + */ +static int +check_key(soft_object_t *key_p, boolean_t sign) +{ + biginteger_t *p; + ulong_t len; + + if (sign) { + if ((key_p->class != CKO_PRIVATE_KEY) || + (key_p->key_type != CKK_EC)) + return (CKR_KEY_TYPE_INCONSISTENT); + + p = OBJ_PRI_EC_VALUE(key_p); + len = p->big_value_len; + if (p->big_value == NULL) + len = 0; + + if (len < CRYPTO_BITS2BYTES(EC_MIN_KEY_LEN) || + len > CRYPTO_BITS2BYTES(EC_MAX_KEY_LEN)) + return (CKR_KEY_SIZE_RANGE); + } else { + if ((key_p->class != CKO_PUBLIC_KEY) || + (key_p->key_type != CKK_EC)) + return (CKR_KEY_TYPE_INCONSISTENT); + + p = OBJ_PUB_EC_POINT(key_p); + len = p->big_value_len; + if (p->big_value == NULL) + len = 0; + + if (len < CRYPTO_BITS2BYTES(EC_MIN_KEY_LEN) * 2 + 1 || + len > CRYPTO_BITS2BYTES(EC_MAX_KEY_LEN) * 2 + 1) + return (CKR_KEY_SIZE_RANGE); + } + + return (CKR_OK); +} + +/* + * This function places the octet string of the specified attribute + * into the corresponding key object. + */ +static void +soft_genECkey_set_attribute(soft_object_t *key, biginteger_t *bi, + CK_ATTRIBUTE_TYPE type) +{ + biginteger_t *dst; + + switch (type) { + case CKA_VALUE: + dst = OBJ_PRI_EC_VALUE(key); + break; + + case CKA_EC_POINT: + dst = OBJ_PUB_EC_POINT(key); + break; + } + copy_bigint_attr(bi, dst); +} + +CK_RV +soft_ec_genkey_pair(soft_object_t *pubkey, soft_object_t *prikey) +{ + CK_RV rv; + CK_ATTRIBUTE template; + ECPrivateKey *privKey; /* contains both public and private values */ + ECParams *ecparams; + SECKEYECParams params_item; + biginteger_t bi; + uchar_t param_buffer[EC_MAX_OID_LEN]; + uint_t paramlen; + + if ((pubkey->class != CKO_PUBLIC_KEY) || + (pubkey->key_type != CKK_EC)) + return (CKR_KEY_TYPE_INCONSISTENT); + + if ((prikey->class != CKO_PRIVATE_KEY) || + (prikey->key_type != CKK_EC)) + return (CKR_KEY_TYPE_INCONSISTENT); + + template.type = CKA_EC_PARAMS; + template.pValue = param_buffer; + template.ulValueLen = sizeof (param_buffer); + rv = soft_get_public_key_attribute(pubkey, &template); + if (rv != CKR_OK) { + return (rv); + } + paramlen = template.ulValueLen; + + /* private key also has CKA_EC_PARAMS attribute */ + rv = set_extra_attr_to_object(prikey, CKA_EC_PARAMS, &template); + if (rv != CKR_OK) { + return (rv); + } + + /* ASN1 check */ + if (param_buffer[0] != 0x06 || + param_buffer[1] != paramlen - 2) { + return (CKR_ATTRIBUTE_VALUE_INVALID); + } + params_item.len = paramlen; + params_item.data = param_buffer; + if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) { + /* bad curve OID */ + return (CKR_ARGUMENTS_BAD); + } + + if (EC_NewKey(ecparams, &privKey, 0) != SECSuccess) { + soft_free_ecparams(ecparams, B_TRUE); + return (CKR_FUNCTION_FAILED); + } + + bi.big_value = privKey->privateValue.data; + bi.big_value_len = privKey->privateValue.len; + soft_genECkey_set_attribute(prikey, &bi, CKA_VALUE); + + bi.big_value = privKey->publicValue.data; + bi.big_value_len = privKey->publicValue.len; + soft_genECkey_set_attribute(pubkey, &bi, CKA_EC_POINT); + + soft_free_ecprivkey(privKey); + soft_free_ecparams(ecparams, B_TRUE); + + return (CKR_OK); +} + +CK_RV +soft_ec_key_derive(soft_object_t *basekey, soft_object_t *secretkey, + void *mech_params, size_t mech_params_len) +{ + CK_RV rv; + CK_ATTRIBUTE template; + CK_ECDH1_DERIVE_PARAMS *ecdh1_derive_params = mech_params; + uchar_t value[EC_MAX_VALUE_LEN]; + uint32_t value_len = sizeof (value); + uchar_t params[EC_MAX_OID_LEN]; + uint32_t params_len = sizeof (params); + uint32_t keylen; + ECParams *ecparams; + SECKEYECParams params_item; + SECItem public_value_item, private_value_item, secret_item; + uchar_t *buf; + + if (mech_params_len != sizeof (CK_ECDH1_DERIVE_PARAMS) || + ecdh1_derive_params->kdf != CKD_NULL) { + return (CKR_MECHANISM_PARAM_INVALID); + } + + template.type = CKA_VALUE; + template.pValue = value; + template.ulValueLen = value_len; + rv = soft_get_private_key_attribute(basekey, &template); + if (rv != CKR_OK) { + return (rv); + } + value_len = template.ulValueLen; + private_value_item.data = value; + private_value_item.len = value_len; + + template.type = CKA_EC_PARAMS; + template.pValue = params; + template.ulValueLen = params_len; + rv = soft_get_private_key_attribute(basekey, &template); + if (rv != CKR_OK) { + return (rv); + } + params_len = template.ulValueLen; + + switch (secretkey->key_type) { + case CKK_DES: + keylen = DES_KEYSIZE; + break; + case CKK_DES2: + keylen = DES2_KEYSIZE; + break; + case CKK_DES3: + keylen = DES3_KEYSIZE; + break; + case CKK_RC4: + case CKK_AES: + case CKK_GENERIC_SECRET: +#ifdef __sparcv9 + /* LINTED */ + keylen = (uint32_t)OBJ_SEC_VALUE_LEN(secretkey); +#else /* !__sparcv9 */ + keylen = OBJ_SEC_VALUE_LEN(secretkey); +#endif /* __sparcv9 */ + break; + } + + /* ASN1 check */ + if (params[0] != 0x06 || + params[1] != params_len - 2) { + return (CKR_ATTRIBUTE_VALUE_INVALID); + } + params_item.data = params; + params_item.len = params_len; + if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) { + /* bad curve OID */ + return (CKR_ARGUMENTS_BAD); + } + + public_value_item.data = ecdh1_derive_params->pPublicData; + public_value_item.len = ecdh1_derive_params->ulPublicDataLen; + + secret_item.data = NULL; + secret_item.len = 0; + + if (ECDH_Derive(&public_value_item, ecparams, &private_value_item, + B_FALSE, &secret_item, 0) != SECSuccess) { + soft_free_ecparams(ecparams, B_TRUE); + return (CKR_FUNCTION_FAILED); + } else { + rv = CKR_OK; + } + + if (keylen == 0) + keylen = secret_item.len; + + if (keylen > secret_item.len) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + goto out; + } + buf = malloc(keylen); + if (buf == NULL) { + rv = CKR_HOST_MEMORY; + goto out; + } + bcopy(secret_item.data + secret_item.len - keylen, buf, keylen); + OBJ_SEC_VALUE_LEN(secretkey) = keylen; + OBJ_SEC_VALUE(secretkey) = buf; + +out: + soft_free_ecparams(ecparams, B_TRUE); + SECITEM_FreeItem(&secret_item, B_FALSE); + + return (rv); +} + +/* + * Allocate a ECC context for the active sign or verify operation. + * This function is called without the session lock held. + */ +CK_RV +soft_ecc_sign_verify_init_common(soft_session_t *session_p, + CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, + boolean_t sign) +{ + CK_RV rv; + CK_ATTRIBUTE template; + CK_MECHANISM digest_mech; + soft_ecc_ctx_t *ecc_ctx; + soft_object_t *tmp_key = NULL; + uchar_t params[EC_MAX_OID_LEN]; + ECParams *ecparams; + SECKEYECParams params_item; + + if ((rv = check_key(key_p, sign)) != CKR_OK) + return (rv); + + if (pMechanism->mechanism == CKM_ECDSA_SHA1) { + digest_mech.mechanism = CKM_SHA_1; + rv = soft_digest_init_internal(session_p, &digest_mech); + if (rv != CKR_OK) + return (rv); + } + + ecc_ctx = malloc(sizeof (soft_ecc_ctx_t)); + if (ecc_ctx == NULL) { + return (CKR_HOST_MEMORY); + } + + /* + * Make a copy of the signature or verification key, and save it + * in the ECC crypto context since it will be used later for + * signing/verification. We don't want to hold any object reference + * on this original key while doing signing/verification. + */ + (void) pthread_mutex_lock(&key_p->object_mutex); + rv = soft_copy_object(key_p, &tmp_key, SOFT_COPY_OBJ_ORIG_SH, NULL); + if ((rv != CKR_OK) || (tmp_key == NULL)) { + /* Most likely we ran out of space. */ + (void) pthread_mutex_unlock(&key_p->object_mutex); + free(ecc_ctx); + return (rv); + } + + + template.type = CKA_EC_PARAMS; + template.pValue = params; + template.ulValueLen = sizeof (params); + rv = soft_get_private_key_attribute(key_p, &template); + (void) pthread_mutex_unlock(&key_p->object_mutex); + if (rv != CKR_OK) { + goto out; + } + + /* ASN1 check */ + if (params[0] != 0x06 || + params[1] != template.ulValueLen - 2) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + goto out; + } + params_item.data = params; + params_item.len = template.ulValueLen; + + ecc_ctx->key = tmp_key; + + if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) { + /* bad curve OID */ + rv = CKR_ARGUMENTS_BAD; + goto out; + } + ecc_ctx->ecparams = *ecparams; + free(ecparams); + + (void) pthread_mutex_lock(&session_p->session_mutex); + + if (sign) { + session_p->sign.context = ecc_ctx; + session_p->sign.mech.mechanism = pMechanism->mechanism; + } else { + session_p->verify.context = ecc_ctx; + session_p->verify.mech.mechanism = pMechanism->mechanism; + } + + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (CKR_OK); + +out: + soft_cleanup_object(tmp_key); + free(tmp_key); + free(ecc_ctx); + + return (rv); +} + +CK_RV +soft_ecc_digest_sign_common(soft_session_t *session_p, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, + CK_ULONG_PTR pulSignedLen, boolean_t Final) +{ + CK_RV rv = CKR_OK; + CK_BYTE hash[SHA1_HASH_SIZE]; + CK_ULONG hash_len = SHA1_HASH_SIZE; + + if (pSigned != NULL) { + if (Final) { + rv = soft_digest_final(session_p, hash, &hash_len); + } else { + rv = soft_digest(session_p, pData, ulDataLen, hash, + &hash_len); + } + + if (rv != CKR_OK) { + (void) pthread_mutex_lock(&session_p->session_mutex); + soft_free_ecc_context(session_p->sign.context); + session_p->sign.context = NULL; + session_p->digest.flags = 0; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); + } + } + + rv = soft_ecc_sign(session_p, hash, hash_len, pSigned, pulSignedLen); + +clean_exit: + (void) pthread_mutex_lock(&session_p->session_mutex); + /* soft_digest_common() has freed the digest context */ + session_p->digest.flags = 0; + (void) pthread_mutex_unlock(&session_p->session_mutex); + +clean1: + return (rv); +} + +CK_RV +soft_ecc_sign(soft_session_t *session_p, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, + CK_ULONG_PTR pulSignedLen) +{ + CK_RV rv = CKR_OK; + SECStatus ss; + soft_ecc_ctx_t *ecc_ctx = session_p->sign.context; + soft_object_t *key = ecc_ctx->key; + uchar_t value[EC_MAX_VALUE_LEN]; + CK_ATTRIBUTE template; + ECPrivateKey ECkey; + SECItem signature_item; + SECItem digest_item; + + if ((key->class != CKO_PRIVATE_KEY) || (key->key_type != CKK_EC)) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto clean_exit; + } + + if (ulDataLen > EC_MAX_DIGEST_LEN) { + rv = CKR_DATA_LEN_RANGE; + goto clean_exit; + } + + /* structure assignment */ + ECkey.ecParams = ecc_ctx->ecparams; + + template.type = CKA_VALUE; + template.pValue = value; + template.ulValueLen = sizeof (value); + rv = soft_get_private_key_attribute(key, &template); + if (rv != CKR_OK) { + goto clean_exit; + } + + ECkey.privateValue.data = value; + ECkey.privateValue.len = template.ulValueLen; + + signature_item.data = pSigned; + signature_item.len = *pulSignedLen; + + digest_item.data = pData; + digest_item.len = ulDataLen; + + if ((ss = ECDSA_SignDigest(&ECkey, &signature_item, &digest_item, 0)) + != SECSuccess) { + if (ss == SECBufferTooSmall) + return (CKR_BUFFER_TOO_SMALL); + + rv = CKR_FUNCTION_FAILED; + goto clean_exit; + } + + if (rv == CKR_OK) { + *pulSignedLen = signature_item.len; + if (pSigned == NULL) + return (rv); + } + +clean_exit: + (void) pthread_mutex_lock(&session_p->session_mutex); + soft_free_ecc_context(session_p->sign.context); + session_p->sign.context = NULL; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); +} + +CK_RV +soft_ecc_verify(soft_session_t *session_p, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen) +{ + CK_RV rv = CKR_OK; + soft_ecc_ctx_t *ecc_ctx = session_p->verify.context; + soft_object_t *key = ecc_ctx->key; + uchar_t point[EC_MAX_POINT_LEN]; + CK_ATTRIBUTE template; + ECPublicKey ECkey; + SECItem signature_item; + SECItem digest_item; + + if ((key->class != CKO_PUBLIC_KEY) ||(key->key_type != CKK_EC)) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto clean_exit; + } + + if (ulSignatureLen > EC_MAX_SIG_LEN) { + rv = CKR_SIGNATURE_LEN_RANGE; + goto clean_exit; + } + + if (ulDataLen > EC_MAX_DIGEST_LEN) { + rv = CKR_DATA_LEN_RANGE; + goto clean_exit; + } + + /* structure assignment */ + ECkey.ecParams = ecc_ctx->ecparams; + + template.type = CKA_EC_POINT; + template.pValue = point; + template.ulValueLen = sizeof (point); + rv = soft_get_public_key_attribute(key, &template); + if (rv != CKR_OK) { + goto clean_exit; + } + + ECkey.publicValue.data = point; + ECkey.publicValue.len = template.ulValueLen; + + signature_item.data = pSignature; + signature_item.len = ulSignatureLen; + + digest_item.data = pData; + digest_item.len = ulDataLen; + + if (ECDSA_VerifyDigest(&ECkey, &signature_item, &digest_item, 0) + != SECSuccess) { + rv = CKR_SIGNATURE_INVALID; + } else { + rv = CKR_OK; + } + +clean_exit: + (void) pthread_mutex_lock(&session_p->session_mutex); + soft_free_ecc_context(session_p->verify.context); + session_p->verify.context = NULL; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); +} + + +CK_RV +soft_ecc_digest_verify_common(soft_session_t *session_p, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, + CK_ULONG ulSignedLen, boolean_t Final) +{ + CK_RV rv; + CK_BYTE hash[SHA1_HASH_SIZE]; + CK_ULONG hash_len = SHA1_HASH_SIZE; + + if (Final) { + rv = soft_digest_final(session_p, hash, &hash_len); + } else { + rv = soft_digest(session_p, pData, ulDataLen, hash, &hash_len); + } + + if (rv != CKR_OK) { + (void) pthread_mutex_lock(&session_p->session_mutex); + soft_free_ecc_context(session_p->verify.context); + session_p->verify.context = NULL; + session_p->digest.flags = 0; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); + } + + /* + * Now, we are ready to verify the data using signature. + * soft_ecc_verify() will free the verification key. + */ + rv = soft_ecc_verify(session_p, hash, hash_len, + pSigned, ulSignedLen); + +clean_exit: + (void) pthread_mutex_lock(&session_p->session_mutex); + /* soft_digest_common() has freed the digest context */ + session_p->digest.flags = 0; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); +} diff --git a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.h b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.h new file mode 100644 index 0000000000..c4be7e972f --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.h @@ -0,0 +1,68 @@ +/* + * 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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SOFTEC_H +#define _SOFTEC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <security/pkcs11t.h> +#include <ecc_impl.h> +#include "softObject.h" +#include "softSession.h" + +typedef struct soft_ecc_ctx { + soft_object_t *key; + ECParams ecparams; +} soft_ecc_ctx_t; + +extern CK_RV soft_get_public_key_attribute(soft_object_t *, CK_ATTRIBUTE_PTR); +extern CK_RV soft_get_private_key_attribute(soft_object_t *, CK_ATTRIBUTE_PTR); +extern CK_RV set_extra_attr_to_object(soft_object_t *, CK_ATTRIBUTE_TYPE, + CK_ATTRIBUTE_PTR); +extern CK_RV soft_ec_genkey_pair(soft_object_t *, soft_object_t *); +extern CK_RV soft_ec_key_derive(soft_object_t *, soft_object_t *, void *, + size_t); +extern CK_RV soft_ecc_sign_verify_init_common(soft_session_t *, + CK_MECHANISM_PTR, soft_object_t *, boolean_t); +extern CK_RV soft_ecc_sign(soft_session_t *, CK_BYTE_PTR, CK_ULONG, + CK_BYTE_PTR, CK_ULONG_PTR); +extern CK_RV soft_ecc_verify(soft_session_t *, CK_BYTE_PTR, CK_ULONG, + CK_BYTE_PTR, CK_ULONG); +extern CK_RV soft_ecc_digest_sign_common(soft_session_t *, CK_BYTE_PTR, + CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); +extern CK_RV soft_ecc_digest_verify_common(soft_session_t *, CK_BYTE_PTR, + CK_ULONG, CK_BYTE_PTR, CK_ULONG, boolean_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _SOFTEC_H */ diff --git a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softKeysUtil.c b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softKeysUtil.c index ad1e35a4d7..237e99bd2c 100644 --- a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softKeysUtil.c +++ b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softKeysUtil.c @@ -41,6 +41,7 @@ #include "softDSA.h" #include "softRSA.h" #include "softDH.h" +#include "softEC.h" #include "softRandom.h" #include "softMAC.h" #include "softOps.h" @@ -212,7 +213,7 @@ soft_genkey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, */ if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != - sizeof (CK_PKCS5_PBKD2_PARAMS)) + sizeof (CK_PKCS5_PBKD2_PARAMS)) return (CKR_TEMPLATE_INCOMPLETE); break; @@ -245,7 +246,7 @@ soft_genkey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, */ if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != - sizeof (CK_PBE_PARAMS)) + sizeof (CK_PBE_PARAMS)) return (CKR_TEMPLATE_INCOMPLETE); break; default: @@ -331,7 +332,7 @@ soft_genkey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, case CKM_PKCS5_PBKD2: /* Generate keys using PKCS#5 PBKD2 algorithm */ rv = soft_generate_pkcs5_pbkdf2_key(session_p, pMechanism, - secret_key); + secret_key); if (rv == CKR_OK && des_strength > 0) { /* Perform weak key checking for DES and DES3. */ if (des_keycheck(OBJ_SEC_VALUE(secret_key), @@ -345,7 +346,7 @@ soft_genkey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, default: do { rv = soft_random_generator( - OBJ_SEC_VALUE(secret_key), keylen, B_FALSE); + OBJ_SEC_VALUE(secret_key), keylen, B_FALSE); /* If this fails, bail out */ if (rv != CKR_OK) @@ -423,6 +424,7 @@ soft_genkey_pair(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, case CKM_EC_KEY_PAIR_GEN: key_type = CKK_EC; + break; default: return (CKR_MECHANISM_INVALID); @@ -479,6 +481,10 @@ soft_genkey_pair(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, rv = soft_dh_genkey_pair(public_key, private_key); private_key->bool_attr_mask |= DERIVE_BOOL_ON; break; + case CKK_EC: + rv = soft_ec_genkey_pair(public_key, private_key); + private_key->bool_attr_mask |= DERIVE_BOOL_ON; + break; } if (rv != CKR_OK) { @@ -536,7 +542,7 @@ soft_key_derive_check_length(soft_object_t *secret_key, CK_ULONG max_keylen) case CKK_AES: case CKK_BLOWFISH: if ((OBJ_SEC_VALUE_LEN(secret_key) == 0) || - (OBJ_SEC_VALUE_LEN(secret_key) > max_keylen)) { + (OBJ_SEC_VALUE_LEN(secret_key) > max_keylen)) { /* RC4 and AES has variable key length */ return (CKR_ATTRIBUTE_VALUE_INVALID); } @@ -670,8 +676,8 @@ soft_pkcs12_pbe(soft_session_t *session_p, */ for (i = 0; i < Slen; i += params->ulSaltLen) { (void) memcpy(S+i, params->pSalt, - ((Slen - i) > params->ulSaltLen ? - params->ulSaltLen : (Slen - i))); + ((Slen - i) > params->ulSaltLen ? + params->ulSaltLen : (Slen - i))); } /* @@ -681,8 +687,8 @@ soft_pkcs12_pbe(soft_session_t *session_p, */ for (i = 0; i < Plen; i += params->ulPasswordLen) { (void) memcpy(P+i, params->pPassword, - ((Plen - i) > params->ulPasswordLen ? - params->ulPasswordLen : (Plen - i))); + ((Plen - i) > params->ulPasswordLen ? + params->ulPasswordLen : (Plen - i))); } /* @@ -733,16 +739,13 @@ soft_pkcs12_pbe(soft_session_t *session_p, goto digest_done; if (j == 0) { - rv = soft_digest_update(session_p, D, - Dlen); + rv = soft_digest_update(session_p, D, Dlen); if (rv != CKR_OK) goto digest_done; - rv = soft_digest_update(session_p, I, - Ilen); + rv = soft_digest_update(session_p, I, Ilen); } else { - rv = soft_digest_update(session_p, - Ai, AiLen); + rv = soft_digest_update(session_p, Ai, AiLen); } if (rv != CKR_OK) goto digest_done; @@ -764,7 +767,7 @@ digest_done: */ for (j = 0; j < Blen; j += hashSize) { (void) memcpy(B+j, Ai, ((Blen - j > hashSize) ? - hashSize : Blen - j)); + hashSize : Blen - j)); } /* @@ -869,6 +872,38 @@ soft_derivekey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, break; + case CKM_ECDH1_DERIVE: + /* + * Create a new object for secret key. The key type should + * be provided in the template. + */ + rv = soft_gen_keyobject(pTemplate, ulAttributeCount, + phKey, session_p, CKO_SECRET_KEY, (CK_KEY_TYPE)~0UL, 0, + SOFT_DERIVE_KEY_DH, B_FALSE); + + if (rv != CKR_OK) { + return (rv); + } + + /* Obtain the secret object pointer. */ + secret_key = (soft_object_t *)*phKey; + + rv = soft_ec_key_derive(basekey_p, secret_key, + (CK_BYTE *)pMechanism->pParameter, + pMechanism->ulParameterLen); + + if (rv != CKR_OK) { + if (IS_TOKEN_OBJECT(secret_key)) + soft_delete_token_object(secret_key, B_FALSE, + B_FALSE); + else + soft_delete_object(session_p, secret_key, + B_FALSE); + return (rv); + } + + break; + case CKM_SHA1_KEY_DERIVATION: hash_size = SHA1_HASH_SIZE; digest_mech.mechanism = CKM_SHA_1; @@ -940,7 +975,7 @@ common: } rv = soft_digest(session_p, OBJ_SEC_VALUE(basekey_p), - OBJ_SEC_VALUE_LEN(basekey_p), hash, &hash_len); + OBJ_SEC_VALUE_LEN(basekey_p), hash, &hash_len); (void) pthread_mutex_lock(&session_p->session_mutex); /* soft_digest_common() has freed the digest context */ @@ -1011,8 +1046,7 @@ common: return (CKR_TEMPLATE_INCONSISTENT); return (derive_tls_prf( - (CK_TLS_PRF_PARAMS_PTR)pMechanism->pParameter, - basekey_p)); + (CK_TLS_PRF_PARAMS_PTR)pMechanism->pParameter, basekey_p)); default: return (CKR_MECHANISM_INVALID); @@ -1150,8 +1184,7 @@ do_prf(soft_session_t *session_p, } /* Call PRF function (SHA1_HMAC for now). */ - rv = soft_sign(session_p, input, inlen, - output, &hmac_outlen); + rv = soft_sign(session_p, input, inlen, output, &hmac_outlen); if (rv != CKR_OK) { goto cleanup; @@ -1162,7 +1195,7 @@ do_prf(soft_session_t *session_p, */ if (i == 0) { (void) memcpy(blockdata, output, - local_min(blocklen, hmac_outlen)); + local_min(blocklen, hmac_outlen)); } else { /* * XOR the existing data with output from PRF. @@ -1231,10 +1264,9 @@ soft_create_hmac_key(soft_session_t *session_p, CK_BYTE *passwd, * mechanism parameter structure. */ rv = soft_gen_keyobject(keytemplate, - sizeof (keytemplate)/sizeof (CK_ATTRIBUTE), - phKey, session_p, CKO_SECRET_KEY, - (CK_KEY_TYPE)CKK_GENERIC_SECRET, 0, - SOFT_CREATE_OBJ, B_TRUE); + sizeof (keytemplate)/sizeof (CK_ATTRIBUTE), phKey, session_p, + CKO_SECRET_KEY, (CK_KEY_TYPE)CKK_GENERIC_SECRET, 0, + SOFT_CREATE_OBJ, B_TRUE); return (rv); } @@ -1246,7 +1278,7 @@ soft_generate_pkcs5_pbkdf2_key(soft_session_t *session_p, { CK_RV rv = CKR_OK; CK_PKCS5_PBKD2_PARAMS *params = - (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; + (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; CK_ULONG hLen = SHA1_HASH_SIZE; CK_ULONG dkLen, i; CK_ULONG blocks, remainder; @@ -1271,7 +1303,7 @@ soft_generate_pkcs5_pbkdf2_key(soft_session_t *session_p, * Create a key object to use for HMAC operations. */ rv = soft_create_hmac_key(session_p, params->pPassword, - *params->ulPasswordLen, &phKey); + *params->ulPasswordLen, &phKey); if (rv != CKR_OK) return (rv); @@ -1306,10 +1338,9 @@ soft_generate_pkcs5_pbkdf2_key(soft_session_t *session_p, * salt, so we will allow for this and not return error * if the salt is not specified. */ - if (params->pSaltSourceData != NULL && - params->ulSaltSourceDataLen > 0) + if (params->pSaltSourceData != NULL && params->ulSaltSourceDataLen > 0) (void) memcpy(salt, params->pSaltSourceData, - params->ulSaltSourceDataLen); + params->ulSaltSourceDataLen); /* * Get pointer to the data section of the key, @@ -1341,9 +1372,8 @@ soft_generate_pkcs5_pbkdf2_key(soft_session_t *session_p, * PRF output to the current key. */ rv = do_prf(session_p, params, hmac_key, - salt, params->ulSaltSourceDataLen + 4, - keydata, - ((i + 1) == blocks ? remainder : hLen)); + salt, params->ulSaltSourceDataLen + 4, keydata, + ((i + 1) == blocks ? remainder : hLen)); keydata += hLen; } @@ -1528,7 +1558,7 @@ soft_unwrap_secret_len_check(CK_KEY_TYPE keytype, CK_MECHANISM_TYPE mechtype, pTemplate[i].pValue != NULL) { isValueLen = B_TRUE; break; - } + } } /* Does its presence conflict with the mech type and key type? */ diff --git a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softObject.h b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softObject.h index b780a88805..5cd118cb94 100644 --- a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softObject.h +++ b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softObject.h @@ -474,10 +474,6 @@ typedef enum { */ #define KEY_PUB_EC(k) \ &((k)->key_type_u.ec_pub_key) -#define OBJ_PUB_EC_PARAM(o) \ - &((o)->object_class_u.public_key->key_type_u.ec_pub_key.param) -#define KEY_PUB_EC_PARAM(k) \ - &((k)->key_type_u.ec_pub_key.param) #define OBJ_PUB_EC_POINT(o) \ &((o)->object_class_u.public_key->key_type_u.ec_pub_key.point) #define KEY_PUB_EC_POINT(k) \ @@ -596,10 +592,6 @@ typedef enum { #define KEY_PRI_EC(k) \ &((k)->key_type_u.ec_pri_key) -#define OBJ_PRI_EC_PARAM(o) \ - &((o)->object_class_u.private_key->key_type_u.ec_pri_key.param) -#define KEY_PRI_EC_PARAM(k) \ - &((k)->key_type_u.ec_pri_key.param) #define OBJ_PRI_EC_VALUE(o) \ &((o)->object_class_u.private_key->key_type_u.ec_pri_key.value) #define KEY_PRI_EC_VALUE(k) \ diff --git a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softSignUtil.c b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softSignUtil.c index 3eabed15e0..586dd76fdc 100644 --- a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softSignUtil.c +++ b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softSignUtil.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -35,6 +35,7 @@ #include "softMAC.h" #include "softRSA.h" #include "softDSA.h" +#include "softEC.h" #include "softCrypt.h" /* @@ -90,6 +91,12 @@ soft_sign_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, return (soft_dsa_sign_verify_init_common(session_p, pMechanism, key_p, B_TRUE)); + case CKM_ECDSA: + case CKM_ECDSA_SHA1: + + return (soft_ecc_sign_verify_init_common(session_p, pMechanism, + key_p, B_TRUE)); + case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: @@ -167,13 +174,12 @@ soft_sign(soft_session_t *session_p, CK_BYTE_PTR pData, if (pSignature != NULL) { /* Pass local buffer to avoid overflow. */ rv = soft_des_sign_verify_common(session_p, pData, - ulDataLen, signature, pulSignatureLen, B_TRUE, - B_FALSE); + ulDataLen, signature, pulSignatureLen, B_TRUE, + B_FALSE); } else { /* Pass NULL, let callee to handle it. */ rv = soft_des_sign_verify_common(session_p, pData, - ulDataLen, NULL, pulSignatureLen, B_TRUE, - B_FALSE); + ulDataLen, NULL, pulSignatureLen, B_TRUE, B_FALSE); } if ((rv == CKR_OK) && (pSignature != NULL)) @@ -206,6 +212,16 @@ soft_sign(soft_session_t *session_p, CK_BYTE_PTR pData, return (soft_dsa_digest_sign_common(session_p, pData, ulDataLen, pSignature, pulSignatureLen, B_FALSE)); + case CKM_ECDSA: + + return (soft_ecc_sign(session_p, pData, ulDataLen, + pSignature, pulSignatureLen)); + + case CKM_ECDSA_SHA1: + + return (soft_ecc_digest_sign_common(session_p, pData, ulDataLen, + pSignature, pulSignatureLen, B_FALSE)); + default: return (CKR_MECHANISM_INVALID); } @@ -267,6 +283,7 @@ soft_sign_update(soft_session_t *session_p, CK_BYTE_PTR pPart, * signing. */ case CKM_DSA_SHA1: + case CKM_ECDSA_SHA1: return (soft_digest_update(session_p, pPart, ulPartLen)); @@ -338,11 +355,11 @@ soft_sign_final(soft_session_t *session_p, CK_BYTE_PTR pSignature, if (pSignature != NULL) { /* Pass local buffer to avoid overflow. */ rv = soft_des_sign_verify_common(session_p, NULL, 0, - signature, pulSignatureLen, B_TRUE, B_TRUE); + signature, pulSignatureLen, B_TRUE, B_TRUE); } else { /* Pass NULL, let callee to handle it. */ rv = soft_des_sign_verify_common(session_p, NULL, 0, - NULL, pulSignatureLen, B_TRUE, B_TRUE); + NULL, pulSignatureLen, B_TRUE, B_TRUE); } if ((rv == CKR_OK) && (pSignature != NULL)) @@ -364,6 +381,11 @@ soft_sign_final(soft_session_t *session_p, CK_BYTE_PTR pSignature, return (soft_dsa_digest_sign_common(session_p, NULL, 0, pSignature, pulSignatureLen, B_TRUE)); + case CKM_ECDSA_SHA1: + + return (soft_ecc_digest_sign_common(session_p, NULL, 0, + pSignature, pulSignatureLen, B_TRUE)); + default: /* PKCS11: The mechanism only supports single-part operation. */ return (CKR_MECHANISM_INVALID); diff --git a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softSlotToken.c b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softSlotToken.c index e3201c2d3c..54506aa8b9 100644 --- a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softSlotToken.c +++ b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softSlotToken.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -31,9 +31,10 @@ #include <arcfour.h> #include "softGlobal.h" #include "softSession.h" -#include <des_impl.h> #include <aes_impl.h> #include <blowfish_impl.h> +#include <des_impl.h> +#include <ecc_impl.h> #include "softDH.h" #include "softObject.h" #include "softKeystore.h" @@ -104,7 +105,11 @@ static CK_MECHANISM_TYPE soft_mechanisms[] = { CKM_TLS_MASTER_KEY_DERIVE_DH, CKM_SSL3_KEY_AND_MAC_DERIVE, CKM_TLS_KEY_AND_MAC_DERIVE, - CKM_TLS_PRF + CKM_TLS_PRF, + CKM_EC_KEY_PAIR_GEN, + CKM_ECDSA, + CKM_ECDSA_SHA1, + CKM_ECDH1_DERIVE }; /* @@ -225,7 +230,11 @@ static CK_MECHANISM_INFO soft_mechanism_info[] = { {48, 48, CKF_DERIVE}, /* CKM_TLS_MASTER_KEY_DERIVE_DH */ {0, 0, CKF_DERIVE}, /* CKM_SSL3_KEY_AND_MAC_DERIVE */ {0, 0, CKF_DERIVE}, /* CKM_TLS_KEY_AND_MAC_DERIVE */ - {0, 0, CKF_DERIVE} /* CKM_TLS_PRF */ + {0, 0, CKF_DERIVE}, /* CKM_TLS_PRF */ + {EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CKF_GENERATE_KEY_PAIR}, + {EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CKF_SIGN|CKF_VERIFY}, + {EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CKF_SIGN|CKF_VERIFY}, + {EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CKF_DERIVE} }; /* diff --git a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softVerifyUtil.c b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softVerifyUtil.c index acdb3b7ecc..dde02d2cf7 100644 --- a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softVerifyUtil.c +++ b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softVerifyUtil.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,6 +36,7 @@ #include "softMAC.h" #include "softRSA.h" #include "softDSA.h" +#include "softEC.h" #include "softCrypt.h" /* @@ -92,6 +92,12 @@ soft_verify_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, return (soft_dsa_sign_verify_init_common(session_p, pMechanism, key_p, B_FALSE)); + case CKM_ECDSA: + case CKM_ECDSA_SHA1: + + return (soft_ecc_sign_verify_init_common(session_p, pMechanism, + key_p, B_FALSE)); + case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: @@ -217,6 +223,16 @@ soft_verify(soft_session_t *session_p, CK_BYTE_PTR pData, return (soft_dsa_digest_verify_common(session_p, pData, ulDataLen, pSignature, ulSignatureLen, B_FALSE)); + case CKM_ECDSA: + + return (soft_ecc_verify(session_p, pData, ulDataLen, + pSignature, ulSignatureLen)); + + case CKM_ECDSA_SHA1: + + return (soft_ecc_digest_verify_common(session_p, pData, + ulDataLen, pSignature, ulSignatureLen, B_FALSE)); + default: return (CKR_MECHANISM_INVALID); } @@ -264,7 +280,7 @@ soft_verify_update(soft_session_t *session_p, CK_BYTE_PTR pPart, case CKM_DES_MAC: return (soft_des_mac_sign_verify_update(session_p, pPart, - ulPartLen)); + ulPartLen)); case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: @@ -278,6 +294,7 @@ soft_verify_update(soft_session_t *session_p, CK_BYTE_PTR pPart, * verification. */ case CKM_DSA_SHA1: + case CKM_ECDSA_SHA1: return (soft_digest_update(session_p, pPart, ulPartLen)); @@ -358,7 +375,7 @@ soft_verify_final(soft_session_t *session_p, CK_BYTE_PTR pSignature, /* Pass local buffer to avoid overflow. */ rv = soft_des_sign_verify_common(session_p, NULL, 0, - signature, &len, B_FALSE, B_TRUE); + signature, &len, B_FALSE, B_TRUE); if (rv == CKR_OK) { if (len != ulSignatureLen) { @@ -386,6 +403,11 @@ soft_verify_final(soft_session_t *session_p, CK_BYTE_PTR pSignature, return (soft_dsa_digest_verify_common(session_p, NULL, 0, pSignature, ulSignatureLen, B_TRUE)); + case CKM_ECDSA_SHA1: + + return (soft_ecc_digest_verify_common(session_p, NULL, 0, + pSignature, ulSignatureLen, B_TRUE)); + default: /* PKCS11: The mechanism only supports single-part operation. */ return (CKR_MECHANISM_INVALID); diff --git a/usr/src/pkgdefs/SUNWckr/prototype_i386 b/usr/src/pkgdefs/SUNWckr/prototype_i386 index 0f14a2f445..b016c0a3f8 100644 --- a/usr/src/pkgdefs/SUNWckr/prototype_i386 +++ b/usr/src/pkgdefs/SUNWckr/prototype_i386 @@ -54,6 +54,7 @@ f none kernel/crypto/blowfish 755 root sys f none kernel/crypto/des 755 root sys f none kernel/crypto/md4 755 root sys f none kernel/crypto/md5 755 root sys +f none kernel/crypto/ecc 755 root sys f none kernel/crypto/rsa 755 root sys f none kernel/crypto/sha1 755 root sys f none kernel/crypto/sha2 755 root sys @@ -256,6 +257,7 @@ f none kernel/crypto/amd64/blowfish 755 root sys f none kernel/crypto/amd64/des 755 root sys f none kernel/crypto/amd64/md4 755 root sys f none kernel/crypto/amd64/md5 755 root sys +f none kernel/crypto/amd64/ecc 755 root sys f none kernel/crypto/amd64/rsa 755 root sys f none kernel/crypto/amd64/sha1 755 root sys f none kernel/crypto/amd64/sha2 755 root sys diff --git a/usr/src/pkgdefs/SUNWckr/prototype_sparc b/usr/src/pkgdefs/SUNWckr/prototype_sparc index 18b28c5aac..73976c4180 100644 --- a/usr/src/pkgdefs/SUNWckr/prototype_sparc +++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc @@ -54,6 +54,7 @@ f none kernel/crypto/sparcv9/aes 755 root sys f none kernel/crypto/sparcv9/arcfour 755 root sys f none kernel/crypto/sparcv9/blowfish 755 root sys f none kernel/crypto/sparcv9/des 755 root sys +f none kernel/crypto/sparcv9/ecc 755 root sys f none kernel/crypto/sparcv9/md4 755 root sys f none kernel/crypto/sparcv9/md5 755 root sys f none kernel/crypto/sparcv9/rsa 755 root sys diff --git a/usr/src/pkgdefs/common_files/i.kcfconfbase b/usr/src/pkgdefs/common_files/i.kcfconfbase index 0531b93b82..3a7cb223b6 100644 --- a/usr/src/pkgdefs/common_files/i.kcfconfbase +++ b/usr/src/pkgdefs/common_files/i.kcfconfbase @@ -35,6 +35,10 @@ do if [ $? != 0 ]; then rc4=`egrep '^arcfour' $src` fi + egrep '^ecc' $dest > /dev/null 2>&1 + if [ $? != 0 ]; then + ecc=`egrep '^ecc' $src` + fi egrep '^rsa' $dest > /dev/null 2>&1 if [ $? != 0 ]; then rsa=`egrep '^rsa' $src` @@ -51,6 +55,7 @@ do if [ $? != 0 ]; then md4=`egrep '^md4' $src` fi + export ecc export rsa export rc4 export sha2 @@ -58,6 +63,7 @@ do export md4 nawk '/^# End SUNWcsr/ { \ if (ENVIRON["rc4"] != "") {print ENVIRON["rc4"]} \ + if (ENVIRON["ecc"] != "") {print ENVIRON["ecc"]} \ if (ENVIRON["rsa"] != "") {print ENVIRON["rsa"]} \ if (ENVIRON["sha2"] != "") {print ENVIRON["sha2"]} \ if (ENVIRON["swrand"] != "") {print ENVIRON["swrand"]} \ diff --git a/usr/src/tools/opensolaris/license-list b/usr/src/tools/opensolaris/license-list index 41327c5f77..6268db094d 100644 --- a/usr/src/tools/opensolaris/license-list +++ b/usr/src/tools/opensolaris/license-list @@ -123,3 +123,5 @@ usr/src/uts/intel/THIRDPARTYLICENSE usr/src/uts/intel/io/aac/THIRDPARTYLICENSE usr/src/uts/intel/io/acpica/THIRDPARTYLICENSE usr/src/uts/intel/io/amr/THIRDPARTYLICENSE +usr/src/common/crypto/ecc/THIRDPARTYLICENSE +usr/src/common/mpi/THIRDPARTYLICENSE diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 82188bc035..1b3f4b449d 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1244,6 +1244,13 @@ ARCFOURPROV_OBJS += arcfour.o arcfour_crypt.o BLOWFISHPROV_OBJS += blowfish.o blowfish_impl.o blowfish_cbc_crypt.o +ECCPROV_OBJS += ecc.o ec.o ec2_163.o ec2_mont.o ecdecode.o ecl_mult.o \ + ecp_384.o ecp_jac.o ec2_193.o ecl.o ecp_192.o ecp_521.o \ + ecp_jm.o ec2_233.o ecl_curve.o ecp_224.o ecp_aff.o \ + ecp_mont.o ec2_aff.o ec_naf.o ecl_gf.o ecp_256.o mp_gf2m.o \ + mpi.o mpcpucache.o mplogic.o mpmontg.o mpprime.o oid.o \ + secitem.o ec2_test.o ecp_test.o + COMMON_RSAPROV_OBJS += rsa.o bignumimpl.o rsa_impl.o RSAPROV_OBJS += $(COMMON_RSAPROV_OBJS) $(RSAPROV_PSR_OBJS) diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index 5933472125..29723cf66d 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -54,6 +54,10 @@ $(OBJS_DIR)/%.o: $(COMMONBASE)/crypto/blowfish/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(COMMONBASE)/crypto/ecc/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(COMMONBASE)/crypto/rsa/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -62,7 +66,11 @@ $(OBJS_DIR)/%.o: $(COMMONBASE)/bignum/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) -$(OBJS_DIR)/%.o: $(COMMONBASE)/acl/%.c +$(OBJS_DIR)/%.o: $(COMMONBASE)/mpi/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(COMMONBASE)/acl/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1067,12 +1075,18 @@ $(LINTS_DIR)/%.ln: $(COMMONBASE)/crypto/arcfour/%.c $(LINTS_DIR)/%.ln: $(COMMONBASE)/crypto/blowfish/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(COMMONBASE)/crypto/ecc/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(COMMONBASE)/crypto/rsa/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) $(LINTS_DIR)/%.ln: $(COMMONBASE)/bignum/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(COMMONBASE)/mpi/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(COMMONBASE)/acl/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/common/crypto/io/dprov.c b/usr/src/uts/common/crypto/io/dprov.c index 690ad7eb1f..715c86a2b0 100644 --- a/usr/src/uts/common/crypto/io/dprov.c +++ b/usr/src/uts/common/crypto/io/dprov.c @@ -95,6 +95,7 @@ #include <sys/sha2.h> #include <aes/aes_cbc_crypt.h> #include <des/des_impl.h> +#include <ecc/ecc_impl.h> #include <blowfish/blowfish_impl.h> /* @@ -250,6 +251,10 @@ typedef enum dprov_mech_type { AES_KEY_GEN_MECH_INFO_TYPE, /* SUN_CKM_AES_KEY_GEN */ BLOWFISH_KEY_GEN_MECH_INFO_TYPE, /* SUN_CKM_BLOWFISH_KEY_GEN */ RC4_KEY_GEN_MECH_INFO_TYPE, /* SUN_CKM_RC4_KEY_GEN */ + EC_KEY_PAIR_GEN_MECH_INFO_TYPE, /* SUN_CKM_EC_KEY_PAIR_GEN */ + ECDSA_MECH_INFO_TYPE, /* SUN_CKM_ECDSA */ + ECDSA_SHA1_MECH_INFO_TYPE, /* SUN_CKM_ECDSA_SHA1 */ + ECDH1_DERIVE_MECH_INFO_TYPE, /* SUN_CKM_ECDH1_DERIVE */ DH_PKCS_KEY_PAIR_GEN_MECH_INFO_TYPE, /* SUN_CKM_DH_PKCS_KEY_PAIR_GEN */ DH_PKCS_DERIVE_MECH_INFO_TYPE, /* SUN_CKM_DH_PKCS_DERIVE */ RSA_PKCS_KEY_PAIR_GEN_MECH_INFO_TYPE /* SUN_CKM_RSA_PKCS_KEY_PAIR_GEN */ @@ -297,6 +302,10 @@ typedef enum dprov_mech_type { #define DPROV_CKM_BLOWFISH_KEY_GEN "CKM_BLOWFISH_KEY_GEN" #define DPROV_CKM_RC4_KEY_GEN "CKM_RC4_KEY_GEN" #define DPROV_CKM_RSA_PKCS_KEY_PAIR_GEN "CKM_RSA_PKCS_KEY_PAIR_GEN" +#define DPROV_CKM_EC_KEY_PAIR_GEN "CKM_EC_KEY_PAIR_GEN" +#define DPROV_CKM_ECDSA "CKM_ECDSA" +#define DPROV_CKM_ECDSA_SHA1 "CKM_ECDSA_SHA1" +#define DPROV_CKM_ECDH1_DERIVE "CKM_ECDH1_DERIVE" #define DPROV_CKM_DH_PKCS_KEY_PAIR_GEN "CKM_DH_PKCS_KEY_PAIR_GEN" #define DPROV_CKM_DH_PKCS_DERIVE "CKM_DH_PKCS_DERIVE" @@ -579,8 +588,24 @@ static crypto_mech_info_t dprov_mech_info_tab[] = { /* RSA_PKCS_KEY_PAIR_GEN */ {DPROV_CKM_RSA_PKCS_KEY_PAIR_GEN, RSA_PKCS_KEY_PAIR_GEN_MECH_INFO_TYPE, CRYPTO_FG_GENERATE_KEY_PAIR, RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, - CRYPTO_KEYSIZE_UNIT_IN_BITS} - + CRYPTO_KEYSIZE_UNIT_IN_BITS}, + /* EC_KEY_PAIR_GEN */ + {DPROV_CKM_EC_KEY_PAIR_GEN, EC_KEY_PAIR_GEN_MECH_INFO_TYPE, + CRYPTO_FG_GENERATE_KEY_PAIR, EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, + CRYPTO_KEYSIZE_UNIT_IN_BITS}, + /* ECDSA */ + {DPROV_CKM_ECDSA, ECDSA_MECH_INFO_TYPE, + CRYPTO_FG_SIGN | CRYPTO_FG_VERIFY | + CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_VERIFY_ATOMIC | + EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS}, + /* ECDSA_SHA1 */ + {DPROV_CKM_ECDSA_SHA1, ECDSA_SHA1_MECH_INFO_TYPE, + CRYPTO_FG_SIGN | CRYPTO_FG_VERIFY | + CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_VERIFY_ATOMIC | + EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS}, + /* ECDH1_DERIVE */ + {DPROV_CKM_ECDH1_DERIVE, ECDH1_DERIVE_MECH_INFO_TYPE, + CRYPTO_FG_DERIVE, 0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS} }; /* @@ -1107,6 +1132,8 @@ typedef struct dprov_object { #define DPROV_CKA_VALUE_BITS 0x00000160 #define DPROV_CKA_VALUE_LEN 0x00000161 #define DPROV_CKA_EXTRACTABLE 0x00000162 +#define DPROV_CKA_EC_PARAMS 0x00000180 +#define DPROV_CKA_EC_POINT 0x00000181 #define DPROV_HW_FEATURE_TYPE 0x00000300 /* @@ -1531,6 +1558,7 @@ static int dprov_get_sw_prov(crypto_mechanism_t *, kcf_provider_desc_t **, /* object management helper functions */ static void dprov_free_object(dprov_object_t *); static void dprov_release_session_objects(dprov_session_t *); +static void dprov_adjust_attrs(crypto_object_attribute_t *, int); static boolean_t dprov_object_is_private(dprov_object_t *); static boolean_t dprov_object_is_token(dprov_object_t *); static int dprov_key_value_secret(dprov_state_t *, crypto_session_id_t, @@ -2288,7 +2316,9 @@ is_publickey_mech(crypto_mech_type_t mech_type) mech_type == SHA1_RSA_PKCS_MECH_INFO_TYPE || mech_type == SHA256_RSA_PKCS_MECH_INFO_TYPE || mech_type == SHA384_RSA_PKCS_MECH_INFO_TYPE || - mech_type == SHA512_RSA_PKCS_MECH_INFO_TYPE); + mech_type == SHA512_RSA_PKCS_MECH_INFO_TYPE || + mech_type == ECDSA_SHA1_MECH_INFO_TYPE || + mech_type == ECDSA_MECH_INFO_TYPE); } @@ -2602,7 +2632,9 @@ dprov_valid_sign_verif_mech(crypto_mech_type_t mech_type) mech_type == SHA1_RSA_PKCS_MECH_INFO_TYPE || mech_type == SHA256_RSA_PKCS_MECH_INFO_TYPE || mech_type == SHA384_RSA_PKCS_MECH_INFO_TYPE || - mech_type == SHA512_RSA_PKCS_MECH_INFO_TYPE); + mech_type == SHA512_RSA_PKCS_MECH_INFO_TYPE || + mech_type == ECDSA_SHA1_MECH_INFO_TYPE || + mech_type == ECDSA_MECH_INFO_TYPE); } static int @@ -4397,6 +4429,82 @@ out: return (rv); } +static int +copyin_ecc_mech(crypto_mechanism_t *in_mech, crypto_mechanism_t *out_mech, + int *out_error, int mode) +{ + STRUCT_DECL(crypto_mechanism, mech); + STRUCT_DECL(CK_ECDH1_DERIVE_PARAMS, params); + CK_ECDH1_DERIVE_PARAMS *ecc_params; + caddr_t pp; + size_t param_len, shared_data_len, public_data_len; + int error = 0; + int rv = 0; + + STRUCT_INIT(mech, mode); + STRUCT_INIT(params, mode); + bcopy(in_mech, STRUCT_BUF(mech), STRUCT_SIZE(mech)); + pp = STRUCT_FGETP(mech, cm_param); + param_len = STRUCT_FGET(mech, cm_param_len); + + if (param_len != STRUCT_SIZE(params)) { + rv = CRYPTO_ARGUMENTS_BAD; + goto out; + } + + out_mech->cm_type = STRUCT_FGET(mech, cm_type); + out_mech->cm_param = NULL; + out_mech->cm_param_len = 0; + if (pp != NULL) { + if (copyin((char *)pp, STRUCT_BUF(params), param_len) != 0) { + out_mech->cm_param = NULL; + error = EFAULT; + goto out; + } + shared_data_len = STRUCT_FGET(params, ulSharedDataLen); + public_data_len = STRUCT_FGET(params, ulPublicDataLen); + /* allocate param structure and buffers */ + ecc_params = kmem_alloc(sizeof (CK_ECDH1_DERIVE_PARAMS) + + roundup(shared_data_len, sizeof (caddr_t)) + + roundup(public_data_len, sizeof (caddr_t)), KM_NOSLEEP); + if (ecc_params == NULL) { + rv = CRYPTO_HOST_MEMORY; + goto out; + } + ecc_params->pSharedData = (uchar_t *)ecc_params + + sizeof (CK_ECDH1_DERIVE_PARAMS); + ecc_params->pPublicData = (uchar_t *)ecc_params->pSharedData + + roundup(shared_data_len, sizeof (caddr_t)); + if (copyin((char *)STRUCT_FGETP(params, pSharedData), + ecc_params->pSharedData, shared_data_len) != 0) { + kmem_free(ecc_params, sizeof (CK_ECDH1_DERIVE_PARAMS) + + roundup(shared_data_len, sizeof (caddr_t)) + + roundup(public_data_len, sizeof (caddr_t))); + out_mech->cm_param = NULL; + error = EFAULT; + goto out; + } + ecc_params->ulSharedDataLen = shared_data_len; + + if (copyin((char *)STRUCT_FGETP(params, pPublicData), + ecc_params->pPublicData, public_data_len) != 0) { + kmem_free(ecc_params, sizeof (CK_ECDH1_DERIVE_PARAMS) + + roundup(shared_data_len, sizeof (caddr_t)) + + roundup(public_data_len, sizeof (caddr_t))); + out_mech->cm_param = NULL; + error = EFAULT; + goto out; + } + ecc_params->ulPublicDataLen = public_data_len; + ecc_params->kdf = STRUCT_FGET(params, kdf); + out_mech->cm_param = (char *)ecc_params; + out_mech->cm_param_len = sizeof (CK_ECDH1_DERIVE_PARAMS); + } +out: + *out_error = error; + return (rv); +} + /* ARGSUSED */ static int copyout_aes_ctr_mech(crypto_mechanism_t *in_mech, crypto_mechanism_t *out_mech, @@ -4477,6 +4585,10 @@ dprov_copyin_mechanism(crypto_provider_handle_t provider, rv = copyin_aes_ctr_mech(umech, kmech, &error, mode); goto out; + case ECDH1_DERIVE_MECH_INFO_TYPE: + rv = copyin_ecc_mech(umech, kmech, &error, mode); + goto out; + case AES_CCM_MECH_INFO_TYPE: rv = copyin_aes_ccm_mech(umech, kmech, &error, mode); goto out; @@ -4529,6 +4641,8 @@ dprov_copyout_mechanism(crypto_provider_handle_t provider, case AES_CTR_MECH_INFO_TYPE: case SHA1_KEY_DERIVATION_MECH_INFO_TYPE: /* for testing only */ return (copyout_aes_ctr_mech(kmech, umech, out_error, mode)); + case ECDH1_DERIVE_MECH_INFO_TYPE: + return (CRYPTO_SUCCESS); default: return (CRYPTO_MECHANISM_INVALID); } @@ -4547,10 +4661,22 @@ dprov_free_mechanism(crypto_provider_handle_t provider, if (mech->cm_param == NULL || mech->cm_param_len == 0) return (CRYPTO_SUCCESS); - if (mech->cm_type == AES_CTR_MECH_INFO_TYPE || - mech->cm_type == SHA1_KEY_DERIVATION_MECH_INFO_TYPE) { + switch (mech->cm_type) { + case AES_CTR_MECH_INFO_TYPE: + case SHA1_KEY_DERIVATION_MECH_INFO_TYPE: len = sizeof (CK_AES_CTR_PARAMS); - } else { + break; + case ECDH1_DERIVE_MECH_INFO_TYPE: { + CK_ECDH1_DERIVE_PARAMS *ecc_params; + + /* LINTED: pointer alignment */ + ecc_params = (CK_ECDH1_DERIVE_PARAMS *)mech->cm_param; + kmem_free(ecc_params, sizeof (CK_ECDH1_DERIVE_PARAMS) + + roundup(ecc_params->ulSharedDataLen, sizeof (caddr_t)) + + roundup(ecc_params->ulPublicDataLen, sizeof (caddr_t))); + return (CRYPTO_SUCCESS); + } + default: len = mech->cm_param_len; } kmem_free(mech->cm_param, len); @@ -5318,7 +5444,6 @@ dprov_sign_task(dprov_req_t *taskq_req) } mutex_enter(&softc->ds_lock); - /* get key value for secret key algorithms */ if (is_publickey_mech(mech.cm_type)) { if ((error = dprov_key_attr_asymmetric(softc, ctx->cc_session, taskq_req->dr_type, @@ -7896,7 +8021,9 @@ free_derived_key: case DPROV_REQ_NOSTORE_KEY_GENERATE_PAIR: { crypto_mechanism_t *mechp; crypto_object_attribute_t *pub_template; + crypto_object_attribute_t *pri_template; uint_t pub_attribute_count; + uint_t pri_attribute_count; crypto_object_attribute_t *out_pub_template; crypto_object_attribute_t *out_pri_template; uint_t out_pub_attribute_count; @@ -7905,6 +8032,9 @@ free_derived_key: mechp = taskq_req->dr_key_req.kr_mechanism; pub_template = taskq_req->dr_key_req.kr_template; pub_attribute_count = taskq_req->dr_key_req.kr_attribute_count; + pri_template = taskq_req->dr_key_req.kr_private_key_template; + pri_attribute_count = + taskq_req->dr_key_req.kr_private_key_attribute_count; out_pub_template = taskq_req->dr_key_req.kr_out_template1; out_pub_attribute_count = taskq_req->dr_key_req.kr_out_attribute_count1; @@ -7953,6 +8083,56 @@ free_derived_key: out_pri_attribute_count, DPROV_CKA_VALUE); break; + case EC_KEY_PAIR_GEN_MECH_INFO_TYPE: { + crypto_mechanism_t mech, *mechp; + kcf_req_params_t params; + crypto_object_attribute_t *pub_template; + uint_t pub_attribute_count; + crypto_object_attribute_t *out_pub_template; + crypto_object_attribute_t *out_pri_template; + uint_t out_pub_attribute_count; + uint_t out_pri_attribute_count; + + mechp = taskq_req->dr_key_req.kr_mechanism; + pub_template = taskq_req->dr_key_req.kr_template; + pub_attribute_count = + taskq_req->dr_key_req.kr_attribute_count; + out_pub_template = + taskq_req->dr_key_req.kr_out_template1; + out_pub_attribute_count = + taskq_req->dr_key_req.kr_out_attribute_count1; + out_pri_template = + taskq_req->dr_key_req.kr_out_template2; + out_pri_attribute_count = + taskq_req->dr_key_req.kr_out_attribute_count2; + + /* get the software provider for this mechanism */ + mech = *mechp; + if ((error = dprov_get_sw_prov(mechp, &pd, + &mech.cm_type)) != CRYPTO_SUCCESS) + break; + /* + * Turn 32-bit values into 64-bit values for certain + * attributes like CKA_CLASS. + */ + dprov_adjust_attrs(pub_template, pub_attribute_count); + dprov_adjust_attrs(pri_template, pri_attribute_count); + + /* bypass the kernel API for now */ + KCF_WRAP_NOSTORE_KEY_OPS_PARAMS(¶ms, + KCF_OP_KEY_GENERATE_PAIR, + 0, /* session 0 for sw provider */ + &mech, pub_template, pub_attribute_count, + pri_template, pri_attribute_count, NULL, + out_pub_template, out_pub_attribute_count, + out_pri_template, out_pri_attribute_count); + + error = kcf_submit_request(pd, NULL, NULL, ¶ms, + B_FALSE); + + KCF_PROV_REFRELE(pd); + break; + } default: error = CRYPTO_MECHANISM_INVALID; } @@ -8041,6 +8221,38 @@ free_derived_key: fill_dh(value, value_len); break; } + case ECDH1_DERIVE_MECH_INFO_TYPE: { + crypto_mechanism_t mech; + kcf_req_params_t params; + + /* get the software provider for this mechanism */ + mech = *mechp; + if ((error = dprov_get_sw_prov(mechp, &pd, + &mech.cm_type)) != CRYPTO_SUCCESS) + break; + + /* + * Turn 32-bit values into 64-bit values for certain + * attributes like CKA_VALUE_LEN. + */ + dprov_adjust_attrs(in_template, in_attribute_count); + + /* bypass the kernel API for now */ + KCF_WRAP_NOSTORE_KEY_OPS_PARAMS(¶ms, + KCF_OP_KEY_DERIVE, + 0, /* session 0 for sw provider */ + &mech, in_template, in_attribute_count, + NULL, 0, base_key, + out_template, out_attribute_count, + NULL, 0); + + error = kcf_submit_request(pd, NULL, NULL, ¶ms, + B_FALSE); + + KCF_PROV_REFRELE(pd); + break; + } + default: error = CRYPTO_MECHANISM_INVALID; } @@ -9335,3 +9547,37 @@ dprov_release_session_objects(dprov_session_t *session) } } } + +/* + * Adjust an attribute list by turning 32-bit values into 64-bit values + * for certain attributes like CKA_CLASS. Assumes that at least 8 bytes + * of storage have been allocated for all attributes. + */ +static void +dprov_adjust_attrs(crypto_object_attribute_t *in, int in_count) +{ + int i; + size_t offset = 0; + ulong_t tmp = 0; + + for (i = 0; i < in_count; i++) { + /* + * For some attributes, it's okay to copy the value + * into a larger container, e.g. copy an unsigned + * 32-bit integer into a 64-bit container. + */ + if (in[i].oa_type == CKA_VALUE_LEN || + in[i].oa_type == CKA_KEY_TYPE || + in[i].oa_type == CKA_CLASS) { + if (in[i].oa_value_len < sizeof (ulong_t)) { +#ifdef _BIG_ENDIAN + offset = sizeof (ulong_t) - in[i].oa_value_len; +#endif + bcopy(in[i].oa_value, (uchar_t *)&tmp + offset, + in[i].oa_value_len); + bcopy(&tmp, in[i].oa_value, sizeof (ulong_t)); + in[i].oa_value_len = sizeof (ulong_t); + } + } + } +} diff --git a/usr/src/uts/common/crypto/io/ecc.c b/usr/src/uts/common/crypto/io/ecc.c new file mode 100644 index 0000000000..265e7f82d6 --- /dev/null +++ b/usr/src/uts/common/crypto/io/ecc.c @@ -0,0 +1,1705 @@ +/* + * 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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/param.h> +#include <sys/modctl.h> +#include <sys/ddi.h> +#include <sys/crypto/spi.h> +#include <sys/sysmacros.h> +#include <sys/strsun.h> +#include <sys/sha1.h> +#include <sys/random.h> +#include <sys/conf.h> +#include <sys/devops.h> +#include <sys/sunddi.h> +#include <sys/varargs.h> +#include <sys/kmem.h> +#include <sys/kstat.h> + +#include "des_impl.h" +#include "ecc_impl.h" + +#define CKD_NULL 0x00000001 + +extern struct mod_ops mod_cryptoops; + +/* + * Module linkage information for the kernel. + */ +static struct modlcrypto modlcrypto = { + &mod_cryptoops, + "EC Kernel SW Provider" +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modlcrypto, + NULL +}; + +/* + * CSPI information (entry points, provider info, etc.) + */ +typedef enum ecc_mech_type { + EC_KEY_PAIR_GEN_MECH_INFO_TYPE, /* SUN_CKM_EC_KEY_PAIR_GEN */ + ECDSA_MECH_INFO_TYPE, /* SUN_CKM_ECDSA */ + ECDSA_SHA1_MECH_INFO_TYPE, /* SUN_CKM_ECDSA_SHA1 */ + ECDH1_DERIVE_MECH_INFO_TYPE /* SUN_CKM_ECDH1_DERIVE */ +} ecc_mech_type_t; + +/* + * Context for ECDSA mechanism. + */ +typedef struct ecc_ctx { + ecc_mech_type_t mech_type; + crypto_key_t *key; + size_t keychunk_size; + ECParams ecparams; +} ecc_ctx_t; + +/* + * Context for ECDSA_SHA1 mechanism. + */ +typedef struct digest_ecc_ctx { + ecc_mech_type_t mech_type; + crypto_key_t *key; + size_t keychunk_size; + ECParams ecparams; + union { + SHA1_CTX sha1ctx; + } dctx_u; +} digest_ecc_ctx_t; + +#define sha1_ctx dctx_u.sha1ctx + +/* + * Mechanism info structure passed to KCF during registration. + */ +static crypto_mech_info_t ecc_mech_info_tab[] = { + /* EC_KEY_PAIR_GEN */ + {SUN_CKM_EC_KEY_PAIR_GEN, EC_KEY_PAIR_GEN_MECH_INFO_TYPE, + CRYPTO_FG_GENERATE_KEY_PAIR, EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, + CRYPTO_KEYSIZE_UNIT_IN_BITS}, + /* ECDH */ + {SUN_CKM_ECDH1_DERIVE, ECDH1_DERIVE_MECH_INFO_TYPE, CRYPTO_FG_DERIVE, + EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS}, + /* ECDSA */ + {SUN_CKM_ECDSA, ECDSA_MECH_INFO_TYPE, + CRYPTO_FG_SIGN | CRYPTO_FG_VERIFY | + CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_VERIFY_ATOMIC, + EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS}, + /* ECDSA_SHA1 */ + {SUN_CKM_ECDSA_SHA1, ECDSA_SHA1_MECH_INFO_TYPE, + CRYPTO_FG_SIGN | CRYPTO_FG_VERIFY | + CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_VERIFY_ATOMIC, + EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS} +}; + +static void ecc_provider_status(crypto_provider_handle_t, uint_t *); + +static crypto_control_ops_t ecc_control_ops = { + ecc_provider_status +}; + +static int ecc_sign_init(crypto_ctx_t *, crypto_mechanism_t *, + crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t); +static int ecc_sign(crypto_ctx_t *, crypto_data_t *, crypto_data_t *, + crypto_req_handle_t); +static int ecc_sign_update(crypto_ctx_t *, crypto_data_t *, + crypto_req_handle_t); +static int ecc_sign_final(crypto_ctx_t *, crypto_data_t *, + crypto_req_handle_t); +static int ecc_sign_atomic(crypto_provider_handle_t, crypto_session_id_t, + crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *, + crypto_spi_ctx_template_t, crypto_req_handle_t); + +static crypto_sign_ops_t ecc_sign_ops = { + ecc_sign_init, + ecc_sign, + ecc_sign_update, + ecc_sign_final, + ecc_sign_atomic, + NULL, + NULL, + NULL +}; + +static int ecc_verify_init(crypto_ctx_t *, crypto_mechanism_t *, + crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t); +static int ecc_verify(crypto_ctx_t *, crypto_data_t *, crypto_data_t *, + crypto_req_handle_t); +static int ecc_verify_update(crypto_ctx_t *, crypto_data_t *, + crypto_req_handle_t); +static int ecc_verify_final(crypto_ctx_t *, crypto_data_t *, + crypto_req_handle_t); +static int ecc_verify_atomic(crypto_provider_handle_t, crypto_session_id_t, + crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, + crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t); + +static crypto_verify_ops_t ecc_verify_ops = { + ecc_verify_init, + ecc_verify, + ecc_verify_update, + ecc_verify_final, + ecc_verify_atomic, + NULL, + NULL, + NULL +}; + +static int ecc_nostore_key_generate_pair(crypto_provider_handle_t, + crypto_session_id_t, crypto_mechanism_t *, crypto_object_attribute_t *, + uint_t, crypto_object_attribute_t *, uint_t, crypto_object_attribute_t *, + uint_t, crypto_object_attribute_t *, uint_t, crypto_req_handle_t); +static int ecc_nostore_key_derive(crypto_provider_handle_t, + crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *, + crypto_object_attribute_t *, uint_t, crypto_object_attribute_t *, + uint_t, crypto_req_handle_t); + +static crypto_nostore_key_ops_t ecc_nostore_key_ops = { + NULL, + ecc_nostore_key_generate_pair, + ecc_nostore_key_derive +}; + +static crypto_ops_t ecc_crypto_ops = { + &ecc_control_ops, + NULL, + NULL, + NULL, + &ecc_sign_ops, + &ecc_verify_ops, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &ecc_nostore_key_ops +}; + +static crypto_provider_info_t ecc_prov_info = { + CRYPTO_SPI_VERSION_3, + "EC Software Provider", + CRYPTO_SW_PROVIDER, + {&modlinkage}, + NULL, + &ecc_crypto_ops, + sizeof (ecc_mech_info_tab)/sizeof (crypto_mech_info_t), + ecc_mech_info_tab +}; + +static crypto_kcf_provider_handle_t ecc_prov_handle = NULL; + +static int ecc_sign_common(ecc_ctx_t *, crypto_data_t *, crypto_data_t *, + crypto_req_handle_t); +static int ecc_verify_common(ecc_ctx_t *, crypto_data_t *, crypto_data_t *, + crypto_req_handle_t); +static int find_attr(crypto_object_attribute_t *, uint_t, uint64_t); +static int get_template_attr_ulong(crypto_object_attribute_t *, + uint_t, uint64_t, ulong_t *); +static void ecc_free_context(crypto_ctx_t *); +static void free_ecparams(ECParams *, boolean_t); +static void free_ecprivkey(ECPrivateKey *); + +int +_init(void) +{ + int ret; + + /* + * Register with KCF. If the registration fails, return error. + */ + if ((ret = crypto_register_provider(&ecc_prov_info, + &ecc_prov_handle)) != CRYPTO_SUCCESS) { + cmn_err(CE_WARN, "ecc _init: crypto_register_provider()" + "failed (0x%x)", ret); + return (EACCES); + } + + if ((ret = mod_install(&modlinkage)) != 0) { + int rv; + + ASSERT(ecc_prov_handle != NULL); + /* We should not return if the unregister returns busy. */ + while ((rv = crypto_unregister_provider(ecc_prov_handle)) + == CRYPTO_BUSY) { + cmn_err(CE_WARN, "ecc _init: " + "crypto_unregister_provider() " + "failed (0x%x). Retrying.", rv); + /* wait 10 seconds and try again. */ + delay(10 * drv_usectohz(1000000)); + } + } + + return (ret); +} + +int +_fini(void) +{ + int ret; + + /* + * Unregister from KCF if previous registration succeeded. + */ + if (ecc_prov_handle != NULL) { + if ((ret = crypto_unregister_provider(ecc_prov_handle)) != + CRYPTO_SUCCESS) { + cmn_err(CE_WARN, "ecc _fini: " + "crypto_unregister_provider() " + "failed (0x%x)", ret); + return (EBUSY); + } + ecc_prov_handle = NULL; + } + + return (mod_remove(&modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +/* ARGSUSED */ +static void +ecc_provider_status(crypto_provider_handle_t provider, uint_t *status) +{ + *status = CRYPTO_PROVIDER_READY; +} + +/* + * Utility routine to look up a attribute of type, 'type', + * in the key. + */ +static int +get_key_attr(crypto_key_t *key, crypto_attr_type_t type, + uchar_t **value, ssize_t *value_len) +{ + int i; + + ASSERT(key->ck_format == CRYPTO_KEY_ATTR_LIST); + for (i = 0; i < key->ck_count; i++) { + if (key->ck_attrs[i].oa_type == type) { + *value = (uchar_t *)key->ck_attrs[i].oa_value; + *value_len = key->ck_attrs[i].oa_value_len; + return (CRYPTO_SUCCESS); + } + } + + return (CRYPTO_FAILED); +} + +/* + * Return the index of an attribute of specified type found in + * the specified array of attributes. If the attribute cannot + * found, return -1. + */ +static int +find_attr(crypto_object_attribute_t *attr, uint_t nattr, uint64_t attr_type) +{ + int i; + + for (i = 0; i < nattr; i++) + if (attr[i].oa_value != NULL && attr[i].oa_type == attr_type) + return (i); + return (-1); +} + +/* + * Common function used by the get_template_attr_*() family of + * functions. Returns the value of the specified attribute of specified + * length. Returns CRYPTO_SUCCESS on success, CRYPTO_ATTRIBUTE_VALUE_INVALID + * if the length of the attribute does not match the specified length, + * or CRYPTO_ARGUMENTS_BAD if the attribute cannot be found. + */ +static int +get_template_attr_scalar_common(crypto_object_attribute_t *template, + uint_t nattr, uint64_t attr_type, void *value, size_t value_len) +{ + size_t oa_value_len; + size_t offset = 0; + int attr_idx; + + if ((attr_idx = find_attr(template, nattr, attr_type)) == -1) + return (CRYPTO_ARGUMENTS_BAD); + + oa_value_len = template[attr_idx].oa_value_len; + if (oa_value_len != value_len) { + return (CRYPTO_ATTRIBUTE_VALUE_INVALID); + } + +do_copy: + bcopy(template[attr_idx].oa_value, (uchar_t *)value + offset, + oa_value_len); + + return (CRYPTO_SUCCESS); +} + +/* + * Get the value of a ulong_t attribute from the specified template. + */ +static int +get_template_attr_ulong(crypto_object_attribute_t *template, + uint_t nattr, uint64_t attr_type, ulong_t *attr_value) +{ + return (get_template_attr_scalar_common(template, nattr, + attr_type, attr_value, sizeof (ulong_t))); +} + +/* + * Called from init routines to do basic sanity checks. Init routines, + * e.g. sign_init should fail rather than subsequent operations. + */ +static int +check_mech_and_key(ecc_mech_type_t mech_type, crypto_key_t *key, ulong_t class) +{ + int rv = CRYPTO_SUCCESS; + uchar_t *foo; + ssize_t point_len; + ssize_t value_len; + + if (mech_type != ECDSA_SHA1_MECH_INFO_TYPE && + mech_type != ECDSA_MECH_INFO_TYPE) + return (CRYPTO_MECHANISM_INVALID); + + if (key->ck_format != CRYPTO_KEY_ATTR_LIST) { + return (CRYPTO_KEY_TYPE_INCONSISTENT); + } + + switch (class) { + case CKO_PUBLIC_KEY: + if ((rv = get_key_attr(key, CKA_EC_POINT, &foo, &point_len)) + != CRYPTO_SUCCESS) { + return (CRYPTO_TEMPLATE_INCOMPLETE); + } + if (point_len < CRYPTO_BITS2BYTES(EC_MIN_KEY_LEN) * 2 + 1 || + point_len > CRYPTO_BITS2BYTES(EC_MAX_KEY_LEN) * 2 + 1) + return (CRYPTO_KEY_SIZE_RANGE); + break; + + case CKO_PRIVATE_KEY: + if ((rv = get_key_attr(key, CKA_VALUE, &foo, &value_len)) + != CRYPTO_SUCCESS) { + return (CRYPTO_TEMPLATE_INCOMPLETE); + } + if (value_len < CRYPTO_BITS2BYTES(EC_MIN_KEY_LEN) || + value_len > CRYPTO_BITS2BYTES(EC_MAX_KEY_LEN)) + return (CRYPTO_KEY_SIZE_RANGE); + break; + + default: + return (CRYPTO_TEMPLATE_INCONSISTENT); + } + + return (rv); +} + +/* + * This function guarantees to return non-zero random numbers. + * This is needed as the /dev/urandom kernel interface, + * random_get_pseudo_bytes(), may return zeros. + */ +int +ecc_knzero_random_generator(uint8_t *ran_out, size_t ran_len) +{ + int rv; + size_t ebc = 0; /* count of extra bytes in extrarand */ + size_t i = 0; + uint8_t extrarand[32]; + size_t extrarand_len; + + if ((rv = random_get_pseudo_bytes(ran_out, ran_len)) != 0) + return (rv); + + /* + * Walk through the returned random numbers pointed by ran_out, + * and look for any random number which is zero. + * If we find zero, call random_get_pseudo_bytes() to generate + * another 32 random numbers pool. Replace any zeros in ran_out[] + * from the random number in pool. + */ + while (i < ran_len) { + if (ran_out[i] != 0) { + i++; + continue; + } + + /* + * Note that it is 'while' so we are guaranteed a + * non-zero value on exit. + */ + if (ebc == 0) { + /* refresh extrarand */ + extrarand_len = sizeof (extrarand); + if ((rv = random_get_pseudo_bytes(extrarand, + extrarand_len)) != 0) { + return (rv); + } + + ebc = extrarand_len; + } + /* Replace zero with byte from extrarand. */ + -- ebc; + + /* + * The new random byte zero/non-zero will be checked in + * the next pass through the loop. + */ + ran_out[i] = extrarand[ebc]; + } + + return (CRYPTO_SUCCESS); +} + +typedef enum cmd_type { + COPY_FROM_DATA, + COPY_TO_DATA, + COMPARE_TO_DATA, + SHA1_DIGEST_DATA +} cmd_type_t; + +/* + * Utility routine to apply the command, 'cmd', to the + * data in the uio structure. + */ +static int +process_uio_data(crypto_data_t *data, uchar_t *buf, int len, + cmd_type_t cmd, void *digest_ctx) +{ + uio_t *uiop = data->cd_uio; + off_t offset = data->cd_offset; + size_t length = len; + uint_t vec_idx; + size_t cur_len; + uchar_t *datap; + + ASSERT(data->cd_format == CRYPTO_DATA_UIO); + if (uiop->uio_segflg != UIO_SYSSPACE) { + return (CRYPTO_ARGUMENTS_BAD); + } + + /* + * Jump to the first iovec containing data to be + * processed. + */ + for (vec_idx = 0; vec_idx < uiop->uio_iovcnt && + offset >= uiop->uio_iov[vec_idx].iov_len; + offset -= uiop->uio_iov[vec_idx++].iov_len) + ; + + if (vec_idx == uiop->uio_iovcnt) { + /* + * The caller specified an offset that is larger than + * the total size of the buffers it provided. + */ + return (CRYPTO_DATA_LEN_RANGE); + } + + while (vec_idx < uiop->uio_iovcnt && length > 0) { + cur_len = MIN(uiop->uio_iov[vec_idx].iov_len - + offset, length); + + datap = (uchar_t *)(uiop->uio_iov[vec_idx].iov_base + + offset); + switch (cmd) { + case COPY_FROM_DATA: + bcopy(datap, buf, cur_len); + buf += cur_len; + break; + case COPY_TO_DATA: + bcopy(buf, datap, cur_len); + buf += cur_len; + break; + case COMPARE_TO_DATA: + if (bcmp(datap, buf, cur_len)) + return (CRYPTO_SIGNATURE_INVALID); + buf += cur_len; + break; + case SHA1_DIGEST_DATA: + SHA1Update(digest_ctx, datap, cur_len); + break; + } + + length -= cur_len; + vec_idx++; + offset = 0; + } + + if (vec_idx == uiop->uio_iovcnt && length > 0) { + /* + * The end of the specified iovec's was reached but + * the length requested could not be processed. + */ + switch (cmd) { + case COPY_TO_DATA: + data->cd_length = len; + return (CRYPTO_BUFFER_TOO_SMALL); + default: + return (CRYPTO_DATA_LEN_RANGE); + } + } + + return (CRYPTO_SUCCESS); +} + +/* + * Utility routine to apply the command, 'cmd', to the + * data in the mblk structure. + */ +static int +process_mblk_data(crypto_data_t *data, uchar_t *buf, int len, + cmd_type_t cmd, void *digest_ctx) +{ + off_t offset = data->cd_offset; + size_t length = len; + mblk_t *mp; + size_t cur_len; + uchar_t *datap; + + ASSERT(data->cd_format == CRYPTO_DATA_MBLK); + /* + * Jump to the first mblk_t containing data to be processed. + */ + for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp); + offset -= MBLKL(mp), mp = mp->b_cont) + ; + if (mp == NULL) { + /* + * The caller specified an offset that is larger + * than the total size of the buffers it provided. + */ + return (CRYPTO_DATA_LEN_RANGE); + } + + /* + * Now do the processing on the mblk chain. + */ + while (mp != NULL && length > 0) { + cur_len = MIN(MBLKL(mp) - offset, length); + + datap = (uchar_t *)(mp->b_rptr + offset); + switch (cmd) { + case COPY_FROM_DATA: + bcopy(datap, buf, cur_len); + buf += cur_len; + break; + case COPY_TO_DATA: + bcopy(buf, datap, cur_len); + buf += cur_len; + break; + case COMPARE_TO_DATA: + if (bcmp(datap, buf, cur_len)) + return (CRYPTO_SIGNATURE_INVALID); + buf += cur_len; + break; + case SHA1_DIGEST_DATA: + SHA1Update(digest_ctx, datap, cur_len); + break; + } + + length -= cur_len; + offset = 0; + mp = mp->b_cont; + } + + if (mp == NULL && length > 0) { + /* + * The end of the mblk was reached but the length + * requested could not be processed. + */ + switch (cmd) { + case COPY_TO_DATA: + data->cd_length = len; + return (CRYPTO_BUFFER_TOO_SMALL); + default: + return (CRYPTO_DATA_LEN_RANGE); + } + } + + return (CRYPTO_SUCCESS); +} + +/* + * Utility routine to copy a buffer to a crypto_data structure. + */ +static int +put_output_data(uchar_t *buf, crypto_data_t *output, int len) +{ + switch (output->cd_format) { + case CRYPTO_DATA_RAW: + if (output->cd_raw.iov_len < len) { + output->cd_length = len; + return (CRYPTO_BUFFER_TOO_SMALL); + } + bcopy(buf, (uchar_t *)(output->cd_raw.iov_base + + output->cd_offset), len); + break; + + case CRYPTO_DATA_UIO: + return (process_uio_data(output, buf, len, COPY_TO_DATA, NULL)); + + case CRYPTO_DATA_MBLK: + return (process_mblk_data(output, buf, len, + COPY_TO_DATA, NULL)); + + default: + return (CRYPTO_ARGUMENTS_BAD); + } + + return (CRYPTO_SUCCESS); +} + +/* + * Utility routine to get data from a crypto_data structure. + * + * '*dptr' contains a pointer to a buffer on return. 'buf' + * is allocated by the caller and is ignored for CRYPTO_DATA_RAW case. + */ +static int +get_input_data(crypto_data_t *input, uchar_t **dptr, uchar_t *buf) +{ + int rv; + + switch (input->cd_format) { + case CRYPTO_DATA_RAW: + if (input->cd_raw.iov_len < input->cd_length) + return (CRYPTO_ARGUMENTS_BAD); + *dptr = (uchar_t *)(input->cd_raw.iov_base + + input->cd_offset); + break; + + case CRYPTO_DATA_UIO: + if ((rv = process_uio_data(input, buf, input->cd_length, + COPY_FROM_DATA, NULL)) != CRYPTO_SUCCESS) + return (rv); + *dptr = buf; + break; + + case CRYPTO_DATA_MBLK: + if ((rv = process_mblk_data(input, buf, input->cd_length, + COPY_FROM_DATA, NULL)) != CRYPTO_SUCCESS) + return (rv); + *dptr = buf; + break; + + default: + return (CRYPTO_ARGUMENTS_BAD); + } + + return (CRYPTO_SUCCESS); +} + +static int +copy_key_to_ctx(crypto_key_t *in_key, ecc_ctx_t *ctx, int kmflag) +{ + int i, count; + size_t len; + caddr_t attr_val; + crypto_object_attribute_t *k_attrs = NULL; + + ASSERT(in_key->ck_format == CRYPTO_KEY_ATTR_LIST); + + count = in_key->ck_count; + /* figure out how much memory to allocate for everything */ + len = sizeof (crypto_key_t) + + count * sizeof (crypto_object_attribute_t); + for (i = 0; i < count; i++) { + len += roundup(in_key->ck_attrs[i].oa_value_len, + sizeof (caddr_t)); + } + + /* one big allocation for everything */ + ctx->key = kmem_alloc(len, kmflag); + if (ctx->key == NULL) + return (CRYPTO_HOST_MEMORY); + /* LINTED: pointer alignment */ + k_attrs = (crypto_object_attribute_t *)((caddr_t)(ctx->key) + + sizeof (crypto_key_t)); + + attr_val = (caddr_t)k_attrs + + count * sizeof (crypto_object_attribute_t); + for (i = 0; i < count; i++) { + k_attrs[i].oa_type = in_key->ck_attrs[i].oa_type; + bcopy(in_key->ck_attrs[i].oa_value, attr_val, + in_key->ck_attrs[i].oa_value_len); + k_attrs[i].oa_value = attr_val; + k_attrs[i].oa_value_len = in_key->ck_attrs[i].oa_value_len; + attr_val += roundup(k_attrs[i].oa_value_len, sizeof (caddr_t)); + } + + ctx->keychunk_size = len; /* save the size to be freed */ + ctx->key->ck_format = CRYPTO_KEY_ATTR_LIST; + ctx->key->ck_count = count; + ctx->key->ck_attrs = k_attrs; + + return (CRYPTO_SUCCESS); +} + +static void +ecc_free_context(crypto_ctx_t *ctx) +{ + ecc_ctx_t *ctxp = ctx->cc_provider_private; + + if (ctxp != NULL) { + bzero(ctxp->key, ctxp->keychunk_size); + kmem_free(ctxp->key, ctxp->keychunk_size); + + free_ecparams(&ctxp->ecparams, B_FALSE); + + if (ctxp->mech_type == ECDSA_MECH_INFO_TYPE) + kmem_free(ctxp, sizeof (ecc_ctx_t)); + else + kmem_free(ctxp, sizeof (digest_ecc_ctx_t)); + + ctx->cc_provider_private = NULL; + } +} + +/* ARGSUSED */ +static int +ecc_sign_verify_common_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism, + crypto_key_t *key, crypto_spi_ctx_template_t ctx_template, + crypto_req_handle_t req) +{ + int rv; + int kmflag; + ecc_ctx_t *ctxp; + digest_ecc_ctx_t *dctxp; + ecc_mech_type_t mech_type = mechanism->cm_type; + uchar_t *params; + ssize_t params_len; + ECParams *ecparams; + SECKEYECParams params_item; + + if (get_key_attr(key, CKA_EC_PARAMS, (void *) ¶ms, + ¶ms_len)) { + return (CRYPTO_ARGUMENTS_BAD); + } + + /* ASN1 check */ + if (params[0] != 0x06 || + params[1] != params_len - 2) { + return (CRYPTO_ATTRIBUTE_VALUE_INVALID); + } + params_item.data = params; + params_item.len = (uint_t)params_len; + kmflag = crypto_kmflag(req); + if (EC_DecodeParams(¶ms_item, &ecparams, kmflag) != SECSuccess) { + /* bad curve OID */ + return (CRYPTO_ARGUMENTS_BAD); + } + + /* + * Allocate an ECC context. + */ + switch (mech_type) { + case ECDSA_SHA1_MECH_INFO_TYPE: + dctxp = kmem_zalloc(sizeof (digest_ecc_ctx_t), kmflag); + ctxp = (ecc_ctx_t *)dctxp; + break; + default: + ctxp = kmem_zalloc(sizeof (ecc_ctx_t), kmflag); + break; + } + + if (ctxp == NULL) { + free_ecparams(ecparams, B_TRUE); + return (CRYPTO_HOST_MEMORY); + } + + if ((rv = copy_key_to_ctx(key, ctxp, kmflag)) != CRYPTO_SUCCESS) { + switch (mech_type) { + case ECDSA_SHA1_MECH_INFO_TYPE: + kmem_free(dctxp, sizeof (digest_ecc_ctx_t)); + break; + default: + kmem_free(ctxp, sizeof (ecc_ctx_t)); + break; + } + free_ecparams(ecparams, B_TRUE); + return (rv); + } + ctxp->mech_type = mech_type; + ctxp->ecparams = *ecparams; + kmem_free(ecparams, sizeof (ECParams)); + + switch (mech_type) { + case ECDSA_SHA1_MECH_INFO_TYPE: + SHA1Init(&(dctxp->sha1_ctx)); + break; + } + + ctx->cc_provider_private = ctxp; + + return (CRYPTO_SUCCESS); +} + +/* ARGSUSED */ +static int +ecc_sign_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism, + crypto_key_t *key, crypto_spi_ctx_template_t ctx_template, + crypto_req_handle_t req) +{ + int rv; + + ecc_mech_type_t mech_type = mechanism->cm_type; + + if ((rv = check_mech_and_key(mech_type, key, + CKO_PRIVATE_KEY)) != CRYPTO_SUCCESS) + return (rv); + + rv = ecc_sign_verify_common_init(ctx, mechanism, key, + ctx_template, req); + + return (rv); +} + +/* ARGSUSED */ +static int +ecc_verify_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism, + crypto_key_t *key, crypto_spi_ctx_template_t ctx_template, + crypto_req_handle_t req) +{ + int rv; + + ecc_mech_type_t mech_type = mechanism->cm_type; + + if ((rv = check_mech_and_key(mech_type, key, + CKO_PUBLIC_KEY)) != CRYPTO_SUCCESS) + return (rv); + + rv = ecc_sign_verify_common_init(ctx, mechanism, key, + ctx_template, req); + + return (rv); +} + +#define SHA1_DIGEST_SIZE 20 + +#define INIT_RAW_CRYPTO_DATA(data, base, len, cd_len) \ + (data).cd_format = CRYPTO_DATA_RAW; \ + (data).cd_offset = 0; \ + (data).cd_raw.iov_base = (char *)base; \ + (data).cd_raw.iov_len = len; \ + (data).cd_length = cd_len; + +#define DO_UPDATE 0x01 +#define DO_FINAL 0x02 +#define DO_SHA1 0x08 +#define DO_SIGN 0x10 +#define DO_VERIFY 0x20 + +static int +digest_data(crypto_data_t *data, void *dctx, uchar_t *digest, + uchar_t flag) +{ + int rv, dlen; + uchar_t *dptr; + + ASSERT(flag & DO_SHA1); + if (data == NULL) { + ASSERT((flag & DO_UPDATE) == 0); + goto dofinal; + } + + dlen = data->cd_length; + + if (flag & DO_UPDATE) { + + switch (data->cd_format) { + case CRYPTO_DATA_RAW: + dptr = (uchar_t *)(data->cd_raw.iov_base + + data->cd_offset); + + if (flag & DO_SHA1) + SHA1Update(dctx, dptr, dlen); + + break; + + case CRYPTO_DATA_UIO: + if (flag & DO_SHA1) + rv = process_uio_data(data, NULL, dlen, + SHA1_DIGEST_DATA, dctx); + + if (rv != CRYPTO_SUCCESS) + return (rv); + + break; + + case CRYPTO_DATA_MBLK: + if (flag & DO_SHA1) + rv = process_mblk_data(data, NULL, dlen, + SHA1_DIGEST_DATA, dctx); + + if (rv != CRYPTO_SUCCESS) + return (rv); + + break; + } + } + +dofinal: + if (flag & DO_FINAL) { + if (flag & DO_SHA1) + SHA1Final(digest, dctx); + } + + return (CRYPTO_SUCCESS); +} + +static int +ecc_digest_svrfy_common(digest_ecc_ctx_t *ctxp, crypto_data_t *data, + crypto_data_t *signature, uchar_t flag, crypto_req_handle_t req) +{ + int rv = CRYPTO_FAILED; + uchar_t digest[SHA1_DIGEST_LENGTH]; + crypto_data_t der_cd; + ecc_mech_type_t mech_type; + + ASSERT(flag & DO_SIGN || flag & DO_VERIFY); + ASSERT(data != NULL || (flag & DO_FINAL)); + + mech_type = ctxp->mech_type; + if (mech_type != ECDSA_SHA1_MECH_INFO_TYPE) + return (CRYPTO_MECHANISM_INVALID); + + /* Don't digest if only returning length of signature. */ + if (signature->cd_length > 0) { + if (mech_type == ECDSA_SHA1_MECH_INFO_TYPE) { + rv = digest_data(data, &(ctxp->sha1_ctx), + digest, flag | DO_SHA1); + if (rv != CRYPTO_SUCCESS) + return (rv); + } + } + + INIT_RAW_CRYPTO_DATA(der_cd, digest, SHA1_DIGEST_SIZE, + SHA1_DIGEST_SIZE); + + if (flag & DO_SIGN) { + rv = ecc_sign_common((ecc_ctx_t *)ctxp, &der_cd, signature, + req); + } else + rv = ecc_verify_common((ecc_ctx_t *)ctxp, &der_cd, signature, + req); + + return (rv); +} + +/* + * This is a single-part signing routine. It does not + * compute a hash before signing. + */ +static int +ecc_sign_common(ecc_ctx_t *ctx, crypto_data_t *data, crypto_data_t *signature, + crypto_req_handle_t req) +{ + int rv = CRYPTO_FAILED; + SECStatus ss; + uchar_t *param; + uchar_t *private; + ssize_t param_len; + ssize_t private_len; + uchar_t tmp_data[EC_MAX_DIGEST_LEN]; + uchar_t signed_data[EC_MAX_SIG_LEN]; + ECPrivateKey ECkey; + SECItem signature_item; + SECItem digest_item; + crypto_key_t *key = ctx->key; + int kmflag; + + if ((rv = get_key_attr(key, CKA_EC_PARAMS, ¶m, + ¶m_len)) != CRYPTO_SUCCESS) { + return (rv); + } + + if (data->cd_length > sizeof (tmp_data)) + return (CRYPTO_DATA_LEN_RANGE); + + if ((rv = get_input_data(data, &digest_item.data, tmp_data)) + != CRYPTO_SUCCESS) { + return (rv); + } + digest_item.len = data->cd_length; + + /* structure assignment */ + ECkey.ecParams = ctx->ecparams; + + if ((rv = get_key_attr(key, CKA_VALUE, &private, + &private_len)) != CRYPTO_SUCCESS) { + return (rv); + } + ECkey.privateValue.data = private; + ECkey.privateValue.len = (uint_t)private_len; + + signature_item.data = signed_data; + signature_item.len = sizeof (signed_data); + + kmflag = crypto_kmflag(req); + if ((ss = ECDSA_SignDigest(&ECkey, &signature_item, &digest_item, + kmflag)) != SECSuccess) { + if (ss == SECBufferTooSmall) + return (CRYPTO_BUFFER_TOO_SMALL); + + return (CRYPTO_FAILED); + } + + if (rv == CRYPTO_SUCCESS) { + /* copy out the signature */ + if ((rv = put_output_data(signed_data, + signature, signature_item.len)) != CRYPTO_SUCCESS) + return (rv); + + signature->cd_length = signature_item.len; + } + + return (rv); +} + +/* ARGSUSED */ +static int +ecc_sign(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *signature, + crypto_req_handle_t req) +{ + int rv; + ecc_ctx_t *ctxp; + + ASSERT(ctx->cc_provider_private != NULL); + ctxp = ctx->cc_provider_private; + + switch (ctxp->mech_type) { + case ECDSA_SHA1_MECH_INFO_TYPE: + rv = ecc_digest_svrfy_common((digest_ecc_ctx_t *)ctxp, data, + signature, DO_SIGN | DO_UPDATE | DO_FINAL, req); + break; + default: + rv = ecc_sign_common(ctxp, data, signature, req); + break; + } + + if (rv != CRYPTO_BUFFER_TOO_SMALL) + ecc_free_context(ctx); + + return (rv); +} + +/* ARGSUSED */ +static int +ecc_sign_update(crypto_ctx_t *ctx, crypto_data_t *data, crypto_req_handle_t req) +{ + int rv; + digest_ecc_ctx_t *ctxp; + ecc_mech_type_t mech_type; + + ASSERT(ctx->cc_provider_private != NULL); + ctxp = ctx->cc_provider_private; + mech_type = ctxp->mech_type; + + if (mech_type == ECDSA_MECH_INFO_TYPE) { + ecc_free_context(ctx); + return (CRYPTO_MECHANISM_INVALID); + } + + if (mech_type == ECDSA_SHA1_MECH_INFO_TYPE) + rv = digest_data(data, &(ctxp->sha1_ctx), + NULL, DO_SHA1 | DO_UPDATE); + + if (rv != CRYPTO_SUCCESS) + ecc_free_context(ctx); + + return (rv); +} + +/* ARGSUSED */ +static int +ecc_sign_final(crypto_ctx_t *ctx, crypto_data_t *signature, + crypto_req_handle_t req) +{ + int rv; + digest_ecc_ctx_t *ctxp; + + ASSERT(ctx->cc_provider_private != NULL); + ctxp = ctx->cc_provider_private; + + rv = ecc_digest_svrfy_common(ctxp, NULL, signature, DO_SIGN | DO_FINAL, + req); + if (rv != CRYPTO_BUFFER_TOO_SMALL) + ecc_free_context(ctx); + + return (rv); +} + +/* ARGSUSED */ +static int +ecc_sign_atomic(crypto_provider_handle_t provider, + crypto_session_id_t session_id, crypto_mechanism_t *mechanism, + crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature, + crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req) +{ + int rv; + ecc_mech_type_t mech_type = mechanism->cm_type; + uchar_t *params; + ssize_t params_len; + ECParams *ecparams; + SECKEYECParams params_item; + int kmflag; + + if ((rv = check_mech_and_key(mech_type, key, + CKO_PRIVATE_KEY)) != CRYPTO_SUCCESS) + return (rv); + + if (get_key_attr(key, CKA_EC_PARAMS, (void *) ¶ms, + ¶ms_len)) { + return (CRYPTO_ARGUMENTS_BAD); + } + + /* ASN1 check */ + if (params[0] != 0x06 || + params[1] != params_len - 2) { + return (CRYPTO_ATTRIBUTE_VALUE_INVALID); + } + params_item.data = params; + params_item.len = (uint_t)params_len; + kmflag = crypto_kmflag(req); + if (EC_DecodeParams(¶ms_item, &ecparams, kmflag) != SECSuccess) { + /* bad curve OID */ + return (CRYPTO_ARGUMENTS_BAD); + } + + if (mechanism->cm_type == ECDSA_MECH_INFO_TYPE) { + ecc_ctx_t ctx; + + ctx.mech_type = mech_type; + /* structure assignment */ + ctx.ecparams = *ecparams; + ctx.key = key; + rv = ecc_sign_common(&ctx, data, signature, req); + } else { + digest_ecc_ctx_t dctx; + + dctx.mech_type = mech_type; + /* structure assignment */ + dctx.ecparams = *ecparams; + dctx.key = key; + SHA1Init(&(dctx.sha1_ctx)); + + rv = ecc_digest_svrfy_common(&dctx, data, signature, + DO_SIGN | DO_UPDATE | DO_FINAL, req); + } + free_ecparams(ecparams, B_TRUE); + + return (rv); +} + +static int +ecc_verify_common(ecc_ctx_t *ctx, crypto_data_t *data, crypto_data_t *signature, + crypto_req_handle_t req) +{ + int rv = CRYPTO_FAILED; + uchar_t *param; + uchar_t *public; + ssize_t param_len; + ssize_t public_len; + uchar_t tmp_data[EC_MAX_DIGEST_LEN]; + uchar_t signed_data[EC_MAX_SIG_LEN]; + ECPublicKey ECkey; + SECItem signature_item; + SECItem digest_item; + crypto_key_t *key = ctx->key; + int kmflag; + + if ((rv = get_key_attr(key, CKA_EC_PARAMS, ¶m, + ¶m_len)) != CRYPTO_SUCCESS) { + return (rv); + } + + if (signature->cd_length > sizeof (signed_data)) { + return (CRYPTO_SIGNATURE_LEN_RANGE); + } + + if ((rv = get_input_data(signature, &signature_item.data, + signed_data)) != CRYPTO_SUCCESS) { + return (rv); + } + signature_item.len = signature->cd_length; + + if (data->cd_length > sizeof (tmp_data)) + return (CRYPTO_DATA_LEN_RANGE); + + if ((rv = get_input_data(data, &digest_item.data, tmp_data)) + != CRYPTO_SUCCESS) { + return (rv); + } + digest_item.len = data->cd_length; + + /* structure assignment */ + ECkey.ecParams = ctx->ecparams; + + if ((rv = get_key_attr(key, CKA_EC_POINT, &public, + &public_len)) != CRYPTO_SUCCESS) { + return (rv); + } + ECkey.publicValue.data = public; + ECkey.publicValue.len = (uint_t)public_len; + + kmflag = crypto_kmflag(req); + if (ECDSA_VerifyDigest(&ECkey, &signature_item, &digest_item, kmflag) + != SECSuccess) { + rv = CRYPTO_SIGNATURE_INVALID; + } else { + rv = CRYPTO_SUCCESS; + } + + return (rv); +} + +/* ARGSUSED */ +static int +ecc_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *signature, + crypto_req_handle_t req) +{ + int rv; + ecc_ctx_t *ctxp; + + ASSERT(ctx->cc_provider_private != NULL); + ctxp = ctx->cc_provider_private; + + switch (ctxp->mech_type) { + case ECDSA_SHA1_MECH_INFO_TYPE: + rv = ecc_digest_svrfy_common((digest_ecc_ctx_t *)ctxp, data, + signature, DO_VERIFY | DO_UPDATE | DO_FINAL, req); + break; + default: + rv = ecc_verify_common(ctxp, data, signature, req); + break; + } + + ecc_free_context(ctx); + return (rv); +} + +/* ARGSUSED */ +static int +ecc_verify_update(crypto_ctx_t *ctx, crypto_data_t *data, + crypto_req_handle_t req) +{ + int rv; + digest_ecc_ctx_t *ctxp; + + ASSERT(ctx->cc_provider_private != NULL); + ctxp = ctx->cc_provider_private; + + switch (ctxp->mech_type) { + case ECDSA_SHA1_MECH_INFO_TYPE: + rv = digest_data(data, &(ctxp->sha1_ctx), + NULL, DO_SHA1 | DO_UPDATE); + break; + default: + rv = CRYPTO_MECHANISM_INVALID; + } + + if (rv != CRYPTO_SUCCESS) + ecc_free_context(ctx); + + return (rv); +} + +/* ARGSUSED */ +static int +ecc_verify_final(crypto_ctx_t *ctx, crypto_data_t *signature, + crypto_req_handle_t req) +{ + int rv; + digest_ecc_ctx_t *ctxp; + + ASSERT(ctx->cc_provider_private != NULL); + ctxp = ctx->cc_provider_private; + + rv = ecc_digest_svrfy_common(ctxp, NULL, signature, + DO_VERIFY | DO_FINAL, req); + + ecc_free_context(ctx); + + return (rv); +} + + +/* ARGSUSED */ +static int +ecc_verify_atomic(crypto_provider_handle_t provider, + crypto_session_id_t session_id, crypto_mechanism_t *mechanism, + crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature, + crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req) +{ + int rv; + ecc_mech_type_t mech_type = mechanism->cm_type; + uchar_t *params; + ssize_t params_len; + ECParams *ecparams; + SECKEYECParams params_item; + int kmflag; + + if ((rv = check_mech_and_key(mech_type, key, + CKO_PUBLIC_KEY)) != CRYPTO_SUCCESS) + return (rv); + + if (get_key_attr(key, CKA_EC_PARAMS, (void *) ¶ms, + ¶ms_len)) { + return (CRYPTO_ARGUMENTS_BAD); + } + + /* ASN1 check */ + if (params[0] != 0x06 || + params[1] != params_len - 2) { + return (CRYPTO_ATTRIBUTE_VALUE_INVALID); + } + params_item.data = params; + params_item.len = (uint_t)params_len; + kmflag = crypto_kmflag(req); + if (EC_DecodeParams(¶ms_item, &ecparams, kmflag) != SECSuccess) { + /* bad curve OID */ + return (CRYPTO_ARGUMENTS_BAD); + } + + if (mechanism->cm_type == ECDSA_MECH_INFO_TYPE) { + ecc_ctx_t ctx; + + ctx.mech_type = mech_type; + /* structure assignment */ + ctx.ecparams = *ecparams; + ctx.key = key; + rv = ecc_verify_common(&ctx, data, signature, req); + } else { + digest_ecc_ctx_t dctx; + + dctx.mech_type = mech_type; + /* structure assignment */ + dctx.ecparams = *ecparams; + dctx.key = key; + SHA1Init(&(dctx.sha1_ctx)); + + rv = ecc_digest_svrfy_common(&dctx, data, signature, + DO_VERIFY | DO_UPDATE | DO_FINAL, req); + } + free_ecparams(ecparams, B_TRUE); + return (rv); +} + +/* ARGSUSED */ +static int +ecc_nostore_key_generate_pair(crypto_provider_handle_t provider, + crypto_session_id_t session_id, crypto_mechanism_t *mechanism, + crypto_object_attribute_t *pub_template, uint_t pub_attribute_count, + crypto_object_attribute_t *pri_template, uint_t pri_attribute_count, + crypto_object_attribute_t *pub_out_template, uint_t pub_out_attribute_count, + crypto_object_attribute_t *pri_out_template, uint_t pri_out_attribute_count, + crypto_req_handle_t req) +{ + int rv = CRYPTO_SUCCESS; + ECPrivateKey *privKey; /* contains both public and private values */ + ECParams *ecparams; + SECKEYECParams params_item; + ulong_t pub_key_type = ~0UL, pub_class = ~0UL; + ulong_t pri_key_type = ~0UL, pri_class = ~0UL; + int params_idx, value_idx, point_idx; + uchar_t *params = NULL; + unsigned params_len; + uchar_t *value = NULL; + uchar_t *point = NULL; + int valuelen; + int pointlen; + int xylen; + int kmflag; + + if (mechanism->cm_type != EC_KEY_PAIR_GEN_MECH_INFO_TYPE) { + return (CRYPTO_MECHANISM_INVALID); + } + + /* optional */ + (void) get_template_attr_ulong(pub_template, + pub_attribute_count, CKA_CLASS, &pub_class); + + /* optional */ + (void) get_template_attr_ulong(pri_template, + pri_attribute_count, CKA_CLASS, &pri_class); + + /* optional */ + (void) get_template_attr_ulong(pub_template, + pub_attribute_count, CKA_KEY_TYPE, &pub_key_type); + + /* optional */ + (void) get_template_attr_ulong(pri_template, + pri_attribute_count, CKA_KEY_TYPE, &pri_key_type); + + if (pub_class != ~0UL && pub_class != CKO_PUBLIC_KEY) { + return (CRYPTO_TEMPLATE_INCONSISTENT); + } + pub_class = CKO_PUBLIC_KEY; + + if (pri_class != ~0UL && pri_class != CKO_PRIVATE_KEY) { + return (CRYPTO_TEMPLATE_INCONSISTENT); + } + pri_class = CKO_PRIVATE_KEY; + + if (pub_key_type != ~0UL && pub_key_type != CKK_EC) { + return (CRYPTO_TEMPLATE_INCONSISTENT); + } + pub_key_type = CKK_EC; + + if (pri_key_type != ~0UL && pri_key_type != CKK_EC) { + return (CRYPTO_TEMPLATE_INCONSISTENT); + } + pri_key_type = CKK_EC; + + /* public output template must contain CKA_EC_POINT attribute */ + if ((point_idx = find_attr(pub_out_template, pub_out_attribute_count, + CKA_EC_POINT)) == -1) { + return (CRYPTO_TEMPLATE_INCOMPLETE); + } + + /* private output template must contain CKA_VALUE attribute */ + if ((value_idx = find_attr(pri_out_template, pri_out_attribute_count, + CKA_VALUE)) == -1) { + return (CRYPTO_TEMPLATE_INCOMPLETE); + } + + if ((params_idx = find_attr(pub_template, pub_attribute_count, + CKA_EC_PARAMS)) == -1) { + return (CRYPTO_TEMPLATE_INCOMPLETE); + } + + params = (uchar_t *)pub_template[params_idx].oa_value; + params_len = pub_template[params_idx].oa_value_len; + + value = (uchar_t *)pri_out_template[value_idx].oa_value; + valuelen = (int)pri_out_template[value_idx].oa_value_len; + point = (uchar_t *)pub_out_template[point_idx].oa_value; + pointlen = (int)pub_out_template[point_idx].oa_value_len; + + /* ASN1 check */ + if (params[0] != 0x06 || + params[1] != params_len - 2) { + return (CRYPTO_ATTRIBUTE_VALUE_INVALID); + } + params_item.data = params; + params_item.len = params_len; + kmflag = crypto_kmflag(req); + if (EC_DecodeParams(¶ms_item, &ecparams, kmflag) != SECSuccess) { + /* bad curve OID */ + return (CRYPTO_ARGUMENTS_BAD); + } + + if (EC_NewKey(ecparams, &privKey, kmflag) != SECSuccess) { + free_ecparams(ecparams, B_TRUE); + return (CRYPTO_FAILED); + } + + xylen = privKey->publicValue.len; + /* ASSERT that xylen - 1 is divisible by 2 */ + if (xylen > pointlen) { + rv = CRYPTO_BUFFER_TOO_SMALL; + goto out; + } + + if (privKey->privateValue.len > valuelen) { + rv = CRYPTO_BUFFER_TOO_SMALL; + goto out; + } + bcopy(privKey->privateValue.data, value, privKey->privateValue.len); + pri_out_template[value_idx].oa_value_len = privKey->privateValue.len; + + bcopy(privKey->publicValue.data, point, xylen); + pub_out_template[point_idx].oa_value_len = xylen; + +out: + free_ecprivkey(privKey); + free_ecparams(ecparams, B_TRUE); + return (rv); +} + +/* ARGSUSED */ +static int +ecc_nostore_key_derive(crypto_provider_handle_t provider, + crypto_session_id_t session_id, crypto_mechanism_t *mechanism, + crypto_key_t *base_key, crypto_object_attribute_t *in_attrs, + uint_t in_attr_count, crypto_object_attribute_t *out_attrs, + uint_t out_attr_count, crypto_req_handle_t req) +{ + int rv = CRYPTO_SUCCESS; + int params_idx, value_idx = -1, out_value_idx = -1; + ulong_t key_type; + ulong_t key_len; + crypto_object_attribute_t *attrs; + ECParams *ecparams; + SECKEYECParams params_item; + CK_ECDH1_DERIVE_PARAMS *mech_param; + SECItem public_value_item, private_value_item, secret_item; + int kmflag; + + if (mechanism->cm_type != ECDH1_DERIVE_MECH_INFO_TYPE) { + return (CRYPTO_MECHANISM_INVALID); + } + + ASSERT(IS_P2ALIGNED(mechanism->cm_param, sizeof (uint64_t))); + /* LINTED: pointer alignment */ + mech_param = (CK_ECDH1_DERIVE_PARAMS *)mechanism->cm_param; + if (mech_param->kdf != CKD_NULL) { + return (CRYPTO_MECHANISM_PARAM_INVALID); + } + + if ((base_key->ck_format != CRYPTO_KEY_ATTR_LIST) || + (base_key->ck_count == 0)) { + return (CRYPTO_ARGUMENTS_BAD); + } + + if ((rv = get_template_attr_ulong(in_attrs, in_attr_count, + CKA_KEY_TYPE, &key_type)) != CRYPTO_SUCCESS) { + return (rv); + } + + switch (key_type) { + case CKK_DES: + key_len = DES_KEYSIZE; + break; + case CKK_DES2: + key_len = DES2_KEYSIZE; + break; + case CKK_DES3: + key_len = DES3_KEYSIZE; + break; + case CKK_RC4: + case CKK_AES: + case CKK_GENERIC_SECRET: + if ((rv = get_template_attr_ulong(in_attrs, in_attr_count, + CKA_VALUE_LEN, &key_len)) != CRYPTO_SUCCESS) { + return (rv); + } + break; + default: + key_len = 0; + } + + attrs = base_key->ck_attrs; + if ((value_idx = find_attr(attrs, base_key->ck_count, + CKA_VALUE)) == -1) { + return (CRYPTO_TEMPLATE_INCOMPLETE); + } + + if ((params_idx = find_attr(attrs, base_key->ck_count, + CKA_EC_PARAMS)) == -1) { + return (CRYPTO_TEMPLATE_INCOMPLETE); + } + + private_value_item.data = (uchar_t *)attrs[value_idx].oa_value; + private_value_item.len = attrs[value_idx].oa_value_len; + + params_item.len = attrs[params_idx].oa_value_len; + params_item.data = (uchar_t *)attrs[params_idx].oa_value; + + /* ASN1 check */ + if (params_item.data[0] != 0x06 || + params_item.data[1] != params_item.len - 2) { + return (CRYPTO_ATTRIBUTE_VALUE_INVALID); + } + kmflag = crypto_kmflag(req); + if (EC_DecodeParams(¶ms_item, &ecparams, kmflag) != SECSuccess) { + /* bad curve OID */ + return (CRYPTO_ARGUMENTS_BAD); + } + + public_value_item.data = (uchar_t *)mech_param->pPublicData; + public_value_item.len = mech_param->ulPublicDataLen; + + if ((out_value_idx = find_attr(out_attrs, out_attr_count, + CKA_VALUE)) == -1) { + rv = CRYPTO_TEMPLATE_INCOMPLETE; + goto out; + } + secret_item.data = NULL; + secret_item.len = 0; + + if (ECDH_Derive(&public_value_item, ecparams, &private_value_item, + B_FALSE, &secret_item, kmflag) != SECSuccess) { + free_ecparams(ecparams, B_TRUE); + return (CRYPTO_FAILED); + } else { + rv = CRYPTO_SUCCESS; + } + + if (key_len == 0) + key_len = secret_item.len; + + if (key_len > secret_item.len) { + rv = CRYPTO_ATTRIBUTE_VALUE_INVALID; + goto out; + } + if (key_len > out_attrs[out_value_idx].oa_value_len) { + rv = CRYPTO_BUFFER_TOO_SMALL; + goto out; + } + bcopy(secret_item.data + secret_item.len - key_len, + (uchar_t *)out_attrs[out_value_idx].oa_value, key_len); + out_attrs[out_value_idx].oa_value_len = key_len; +out: + free_ecparams(ecparams, B_TRUE); + SECITEM_FreeItem(&secret_item, B_FALSE); + return (rv); +} + +static void +free_ecparams(ECParams *params, boolean_t freeit) +{ + SECITEM_FreeItem(¶ms->fieldID.u.prime, B_FALSE); + SECITEM_FreeItem(¶ms->curve.a, B_FALSE); + SECITEM_FreeItem(¶ms->curve.b, B_FALSE); + SECITEM_FreeItem(¶ms->curve.seed, B_FALSE); + SECITEM_FreeItem(¶ms->base, B_FALSE); + SECITEM_FreeItem(¶ms->order, B_FALSE); + SECITEM_FreeItem(¶ms->DEREncoding, B_FALSE); + SECITEM_FreeItem(¶ms->curveOID, B_FALSE); + if (freeit) + kmem_free(params, sizeof (ECParams)); +} + +static void +free_ecprivkey(ECPrivateKey *key) +{ + free_ecparams(&key->ecParams, B_FALSE); + SECITEM_FreeItem(&key->publicValue, B_FALSE); + bzero(key->privateValue.data, key->privateValue.len); + SECITEM_FreeItem(&key->privateValue, B_FALSE); + SECITEM_FreeItem(&key->version, B_FALSE); + kmem_free(key, sizeof (ECPrivateKey)); +} diff --git a/usr/src/uts/common/sys/crypto/common.h b/usr/src/uts/common/sys/crypto/common.h index 656564e8c9..917bbf9b36 100644 --- a/usr/src/uts/common/sys/crypto/common.h +++ b/usr/src/uts/common/sys/crypto/common.h @@ -82,7 +82,6 @@ typedef struct CK_AES_CCM_PARAMS { uchar_t *authData; } CK_AES_CCM_PARAMS; - #ifdef _KERNEL /* * CK_ECDH1_DERIVE_PARAMS provides the parameters to the @@ -180,6 +179,10 @@ typedef uint32_t crypto_keysize_unit_t; #define SUN_CKM_SHA256_RSA_PKCS "CKM_SHA256_RSA_PKCS" #define SUN_CKM_SHA384_RSA_PKCS "CKM_SHA384_RSA_PKCS" #define SUN_CKM_SHA512_RSA_PKCS "CKM_SHA512_RSA_PKCS" +#define SUN_CKM_EC_KEY_PAIR_GEN "CKM_EC_KEY_PAIR_GEN" +#define SUN_CKM_ECDH1_DERIVE "CKM_ECDH1_DERIVE" +#define SUN_CKM_ECDSA_SHA1 "CKM_ECDSA_SHA1" +#define SUN_CKM_ECDSA "CKM_ECDSA" /* Shared operation context format for CKM_RC4 */ typedef struct { @@ -257,6 +260,23 @@ typedef uint64_t crypto_attr_type_t; #define SUN_CKA_SUBPRIME 0x00000131 #define SUN_CKA_BASE 0x00000132 +#define CKK_EC 0x00000003 +#define CKK_GENERIC_SECRET 0x00000010 +#define CKK_RC4 0x00000012 +#define CKK_AES 0x0000001F +#define CKK_DES 0x00000013 +#define CKK_DES2 0x00000014 +#define CKK_DES3 0x00000015 + +#define CKO_PUBLIC_KEY 0x00000002 +#define CKO_PRIVATE_KEY 0x00000003 +#define CKA_CLASS 0x00000000 +#define CKA_VALUE 0x00000011 +#define CKA_KEY_TYPE 0x00000100 +#define CKA_VALUE_LEN 0x00000161 +#define CKA_EC_PARAMS 0x00000180 +#define CKA_EC_POINT 0x00000181 + typedef uint32_t crypto_object_id_t; typedef struct crypto_object_attribute { diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared index 1e6b8e434c..d489172d07 100644 --- a/usr/src/uts/intel/Makefile.intel.shared +++ b/usr/src/uts/intel/Makefile.intel.shared @@ -583,6 +583,7 @@ $(CLOSED_BUILD)CRYPTO_KMODS += aes $(CLOSED_BUILD)CRYPTO_KMODS += arcfour $(CLOSED_BUILD)CRYPTO_KMODS += blowfish $(CLOSED_BUILD)CRYPTO_KMODS += des +$(CLOSED_BUILD)CRYPTO_KMODS += ecc CRYPTO_KMODS += md4 CRYPTO_KMODS += md5 CRYPTO_KMODS += rsa diff --git a/usr/src/uts/intel/ecc/Makefile b/usr/src/uts/intel/ecc/Makefile new file mode 100644 index 0000000000..bffe91117e --- /dev/null +++ b/usr/src/uts/intel/ecc/Makefile @@ -0,0 +1,101 @@ +# +# 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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the ECc kef provider. +# +# intel implementation architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. +COM1_DIR = $(COMMONBASE)/mpi +COM2_DIR = $(COMMONBASE)/crypto/ecc +COM3_DIR = $(COMMONBASE)/crypto/des + +# +# Define the module and object file sets. +# +MODULE = ecc +OBJECTS = $(ECCPROV_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(LINTS_DIR)/ecc.ln +ROOTMODULE = $(ROOT_CRYPTO_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# set signing mode +ELFSIGN_MOD = $(ELFSIGN_CRYPTO) + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Linkage dependencies +# +LDFLAGS += -dy + +CPPFLAGS += -I$(COM1_DIR) -I$(COM2_DIR) -I$(COM3_DIR) + +CFLAGS += -DMP_API_COMPATIBLE -DNSS_ECC_MORE_THAN_SUITE_B + +LINTFLAGS += -I$(COM1_DIR) -I$(COM2_DIR) -I$(COM3_DIR) +LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ + +include Makefile.$(CLASS) diff --git a/usr/src/uts/intel/ecc/Makefile.32 b/usr/src/uts/intel/ecc/Makefile.32 new file mode 100644 index 0000000000..09d5ce158b --- /dev/null +++ b/usr/src/uts/intel/ecc/Makefile.32 @@ -0,0 +1,31 @@ +# +# 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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# Configuration and targets for ECC KEF provider +# specific to Intel 32-bit architecture, i386. +# + +CPPFLAGS += -DMP_USE_UINT_DIGIT diff --git a/usr/src/uts/intel/ecc/Makefile.64 b/usr/src/uts/intel/ecc/Makefile.64 new file mode 100644 index 0000000000..c6559537f5 --- /dev/null +++ b/usr/src/uts/intel/ecc/Makefile.64 @@ -0,0 +1,29 @@ +# +# 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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# Configuration and targets for ECC KEF provider +# specific to AMD 64-bit architecture, amd64. +# diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 7d1865aa59..5a00f356db 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -410,6 +410,7 @@ $(CLOSED_BUILD)CRYPTO_KMODS += blowfish $(CLOSED_BUILD)CRYPTO_KMODS += des CRYPTO_KMODS += md4 CRYPTO_KMODS += md5 +CRYPTO_KMODS += ecc CRYPTO_KMODS += rsa CRYPTO_KMODS += sha1 CRYPTO_KMODS += sha2 diff --git a/usr/src/uts/sparc/ecc/Makefile b/usr/src/uts/sparc/ecc/Makefile new file mode 100644 index 0000000000..f7530d338f --- /dev/null +++ b/usr/src/uts/sparc/ecc/Makefile @@ -0,0 +1,100 @@ +# +# 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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the ECC KEF provider. +# +# sparc implementation architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. +COM1_DIR = $(COMMONBASE)/mpi +COM2_DIR = $(COMMONBASE)/crypto/ecc +COM3_DIR = $(COMMONBASE)/crypto/des + +# +# Define the module and object file sets. +# +MODULE = ecc +OBJECTS = $(ECCPROV_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(LINTS_DIR)/ecc.ln +ROOTMODULE = $(ROOT_CRYPTO_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# set signing mode +ELFSIGN_MOD = $(ELFSIGN_CRYPTO) + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Linkage dependencies +# +LDFLAGS += -dy + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) -I$(COM1_DIR) -I$(COM2_DIR) -I$(COM3_DIR) +CFLAGS += -DMP_API_COMPATIBLE -DNSS_ECC_MORE_THAN_SUITE_B +LINTFLAGS += -I$(COM1_DIR) -I$(COM2_DIR) -I$(COM3_DIR) +LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ |