Mercurial > dropbear
diff libtomcrypt/src/pk/dh/dh_sys.c @ 285:1b9e69c058d2
propagate from branch 'au.asn.ucc.matt.ltc.dropbear' (head 20dccfc09627970a312d77fb41dc2970b62689c3)
to branch 'au.asn.ucc.matt.dropbear' (head fdf4a7a3b97ae5046139915de7e40399cceb2c01)
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Wed, 08 Mar 2006 13:23:58 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtomcrypt/src/pk/dh/dh_sys.c Wed Mar 08 13:23:58 2006 +0000 @@ -0,0 +1,499 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, [email protected], http://libtomcrypt.org + */ + +/** + @file dh_sys.c + DH Crypto, Tom St Denis +*/ + +/** + Encrypt a short symmetric key with a public DH key + @param in The symmetric key to encrypt + @param inlen The length of the key (octets) + @param out [out] The ciphertext + @param outlen [in/out] The max size and resulting size of the ciphertext + @param prng An active PRNG state + @param wprng The index of the PRNG desired + @param hash The index of the hash desired (must produce a digest of size >= the size of the plaintext) + @param key The public key you wish to encrypt with. + @return CRYPT_OK if successful +*/ +int dh_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, int hash, + dh_key *key) +{ + unsigned char *pub_expt, *dh_shared, *skey; + dh_key pubkey; + unsigned long x, y, z, hashsize, pubkeysize; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* check that wprng/hash are not invalid */ + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + if (inlen > hash_descriptor[hash].hashsize) { + return CRYPT_INVALID_HASH; + } + + /* allocate memory */ + pub_expt = XMALLOC(DH_BUF_SIZE); + dh_shared = XMALLOC(DH_BUF_SIZE); + skey = XMALLOC(MAXBLOCKSIZE); + if (pub_expt == NULL || dh_shared == NULL || skey == NULL) { + if (pub_expt != NULL) { + XFREE(pub_expt); + } + if (dh_shared != NULL) { + XFREE(dh_shared); + } + if (skey != NULL) { + XFREE(skey); + } + return CRYPT_MEM; + } + + /* make a random key and export the public copy */ + if ((err = dh_make_key(prng, wprng, dh_get_size(key), &pubkey)) != CRYPT_OK) { + goto LBL_ERR; + } + + pubkeysize = DH_BUF_SIZE; + if ((err = dh_export(pub_expt, &pubkeysize, PK_PUBLIC, &pubkey)) != CRYPT_OK) { + dh_free(&pubkey); + goto LBL_ERR; + } + + /* now check if the out buffer is big enough */ + if (*outlen < (1 + 4 + 4 + PACKET_SIZE + pubkeysize + inlen)) { + dh_free(&pubkey); + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* make random key */ + hashsize = hash_descriptor[hash].hashsize; + + x = DH_BUF_SIZE; + if ((err = dh_shared_secret(&pubkey, key, dh_shared, &x)) != CRYPT_OK) { + dh_free(&pubkey); + goto LBL_ERR; + } + dh_free(&pubkey); + + z = MAXBLOCKSIZE; + if ((err = hash_memory(hash, dh_shared, x, skey, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* store header */ + packet_store_header(out, PACKET_SECT_DH, PACKET_SUB_ENC_KEY); + + /* output header */ + y = PACKET_SIZE; + + /* size of hash name and the name itself */ + out[y++] = hash_descriptor[hash].ID; + + /* length of DH pubkey and the key itself */ + STORE32L(pubkeysize, out+y); + y += 4; + for (x = 0; x < pubkeysize; x++, y++) { + out[y] = pub_expt[x]; + } + + /* Store the encrypted key */ + STORE32L(inlen, out+y); + y += 4; + + for (x = 0; x < inlen; x++, y++) { + out[y] = skey[x] ^ in[x]; + } + *outlen = y; + + err = CRYPT_OK; +LBL_ERR: +#ifdef LTC_CLEAN_STACK + /* clean up */ + zeromem(pub_expt, DH_BUF_SIZE); + zeromem(dh_shared, DH_BUF_SIZE); + zeromem(skey, MAXBLOCKSIZE); +#endif + XFREE(skey); + XFREE(dh_shared); + XFREE(pub_expt); + + return err; +} + +/** + Decrypt a DH encrypted symmetric key + @param in The DH encrypted packet + @param inlen The length of the DH encrypted packet + @param out The plaintext + @param outlen [in/out] The max size and resulting size of the plaintext + @param key The private DH key corresponding to the public key that encrypted the plaintext + @return CRYPT_OK if successful +*/ +int dh_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + dh_key *key) +{ + unsigned char *shared_secret, *skey; + unsigned long x, y, z, hashsize, keysize; + int hash, err; + dh_key pubkey; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* right key type? */ + if (key->type != PK_PRIVATE) { + return CRYPT_PK_NOT_PRIVATE; + } + + /* allocate ram */ + shared_secret = XMALLOC(DH_BUF_SIZE); + skey = XMALLOC(MAXBLOCKSIZE); + if (shared_secret == NULL || skey == NULL) { + if (shared_secret != NULL) { + XFREE(shared_secret); + } + if (skey != NULL) { + XFREE(skey); + } + return CRYPT_MEM; + } + + /* check if initial header should fit */ + if (inlen < PACKET_SIZE+1+4+4) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } else { + inlen -= PACKET_SIZE+1+4+4; + } + + /* is header correct? */ + if ((err = packet_valid_header((unsigned char *)in, PACKET_SECT_DH, PACKET_SUB_ENC_KEY)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* now lets get the hash name */ + y = PACKET_SIZE; + hash = find_hash_id(in[y++]); + if (hash == -1) { + err = CRYPT_INVALID_HASH; + goto LBL_ERR; + } + + /* common values */ + hashsize = hash_descriptor[hash].hashsize; + + /* get public key */ + LOAD32L(x, in+y); + + /* now check if the imported key will fit */ + if (inlen < x) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } else { + inlen -= x; + } + + y += 4; + if ((err = dh_import(in+y, x, &pubkey)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + + /* make shared key */ + x = DH_BUF_SIZE; + if ((err = dh_shared_secret(key, &pubkey, shared_secret, &x)) != CRYPT_OK) { + dh_free(&pubkey); + goto LBL_ERR; + } + dh_free(&pubkey); + + z = MAXBLOCKSIZE; + if ((err = hash_memory(hash, shared_secret, x, skey, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* load in the encrypted key */ + LOAD32L(keysize, in+y); + + /* will the out fit as part of the input */ + if (inlen < keysize) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } else { + inlen -= keysize; + } + + if (keysize > *outlen) { + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + y += 4; + + *outlen = keysize; + + for (x = 0; x < keysize; x++, y++) { + out[x] = skey[x] ^ in[y]; + } + + err = CRYPT_OK; +LBL_ERR: +#ifdef LTC_CLEAN_STACK + zeromem(shared_secret, DH_BUF_SIZE); + zeromem(skey, MAXBLOCKSIZE); +#endif + + XFREE(skey); + XFREE(shared_secret); + + return err; +} + +/* perform an ElGamal Signature of a hash + * + * The math works as follows. x is the private key, M is the message to sign + + 1. pick a random k + 2. compute a = g^k mod p + 3. compute b = (M - xa)/k mod p + 4. Send (a,b) + + Now to verify with y=g^x mod p, a and b + + 1. compute y^a * a^b = g^(xa) * g^(k*(M-xa)/k) + = g^(xa + (M - xa)) + = g^M [all mod p] + + 2. Compare against g^M mod p [based on input hash]. + 3. If result of #2 == result of #1 then signature valid +*/ + +/** + Sign a message digest using a DH private key + @param in The data to sign + @param inlen The length of the input (octets) + @param out [out] The destination of the signature + @param outlen [in/out] The max size and resulting size of the output + @param prng An active PRNG state + @param wprng The index of the PRNG desired + @param key A private DH key + @return CRYPT_OK if successful +*/ +int dh_sign_hash(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, dh_key *key) +{ + mp_int a, b, k, m, g, p, p1, tmp; + unsigned char *buf; + unsigned long x, y; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* check parameters */ + if (key->type != PK_PRIVATE) { + return CRYPT_PK_NOT_PRIVATE; + } + + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + /* is the IDX valid ? */ + if (is_valid_idx(key->idx) != 1) { + return CRYPT_PK_INVALID_TYPE; + } + + /* allocate ram for buf */ + buf = XMALLOC(520); + + /* make up a random value k, + * since the order of the group is prime + * we need not check if gcd(k, r) is 1 + */ + if (prng_descriptor[wprng].read(buf, sets[key->idx].size, prng) != + (unsigned long)(sets[key->idx].size)) { + err = CRYPT_ERROR_READPRNG; + goto LBL_ERR; + } + + /* init bignums */ + if ((err = mp_init_multi(&a, &b, &k, &m, &p, &g, &p1, &tmp, NULL)) != MP_OKAY) { + err = mpi_to_ltc_error(err); + goto LBL_ERR; + } + + /* load k and m */ + if ((err = mp_read_unsigned_bin(&m, (unsigned char *)in, inlen)) != MP_OKAY) { goto error; } + if ((err = mp_read_unsigned_bin(&k, buf, sets[key->idx].size)) != MP_OKAY) { goto error; } + + /* load g, p and p1 */ + if ((err = mp_read_radix(&g, sets[key->idx].base, 64)) != MP_OKAY) { goto error; } + if ((err = mp_read_radix(&p, sets[key->idx].prime, 64)) != MP_OKAY) { goto error; } + if ((err = mp_sub_d(&p, 1, &p1)) != MP_OKAY) { goto error; } + if ((err = mp_div_2(&p1, &p1)) != MP_OKAY) { goto error; } /* p1 = (p-1)/2 */ + + /* now get a = g^k mod p */ + if ((err = mp_exptmod(&g, &k, &p, &a)) != MP_OKAY) { goto error; } + + /* now find M = xa + kb mod p1 or just b = (M - xa)/k mod p1 */ + if ((err = mp_invmod(&k, &p1, &k)) != MP_OKAY) { goto error; } /* k = 1/k mod p1 */ + if ((err = mp_mulmod(&a, &key->x, &p1, &tmp)) != MP_OKAY) { goto error; } /* tmp = xa */ + if ((err = mp_submod(&m, &tmp, &p1, &tmp)) != MP_OKAY) { goto error; } /* tmp = M - xa */ + if ((err = mp_mulmod(&k, &tmp, &p1, &b)) != MP_OKAY) { goto error; } /* b = (M - xa)/k */ + + /* check for overflow */ + if ((unsigned long)(PACKET_SIZE + 4 + 4 + mp_unsigned_bin_size(&a) + mp_unsigned_bin_size(&b)) > *outlen) { + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* store header */ + y = PACKET_SIZE; + + /* now store them both (a,b) */ + x = (unsigned long)mp_unsigned_bin_size(&a); + STORE32L(x, out+y); y += 4; + if ((err = mp_to_unsigned_bin(&a, out+y)) != MP_OKAY) { goto error; } + y += x; + + x = (unsigned long)mp_unsigned_bin_size(&b); + STORE32L(x, out+y); y += 4; + if ((err = mp_to_unsigned_bin(&b, out+y)) != MP_OKAY) { goto error; } + y += x; + + /* check if size too big */ + if (*outlen < y) { + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* store header */ + packet_store_header(out, PACKET_SECT_DH, PACKET_SUB_SIGNED); + *outlen = y; + + err = CRYPT_OK; + goto LBL_ERR; +error: + err = mpi_to_ltc_error(err); +LBL_ERR: + mp_clear_multi(&tmp, &p1, &g, &p, &m, &k, &b, &a, NULL); + + XFREE(buf); + + return err; +} + + +/** + Verify the signature given + @param sig The signature + @param siglen The length of the signature (octets) + @param hash The hash that was signed + @param hashlen The length of the hash (octets) + @param stat [out] Result of signature comparison, 1==valid, 0==invalid + @param key The public DH key that signed the hash + @return CRYPT_OK if succsessful (even if signature is invalid) +*/ +int dh_verify_hash(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, dh_key *key) +{ + mp_int a, b, p, g, m, tmp; + unsigned long x, y; + int err; + + LTC_ARGCHK(sig != NULL); + LTC_ARGCHK(hash != NULL); + LTC_ARGCHK(stat != NULL); + LTC_ARGCHK(key != NULL); + + /* default to invalid */ + *stat = 0; + + /* check initial input length */ + if (siglen < PACKET_SIZE+4+4) { + return CRYPT_INVALID_PACKET; + } + + /* header ok? */ + if ((err = packet_valid_header((unsigned char *)sig, PACKET_SECT_DH, PACKET_SUB_SIGNED)) != CRYPT_OK) { + return err; + } + + /* get hash out of packet */ + y = PACKET_SIZE; + + /* init all bignums */ + if ((err = mp_init_multi(&a, &p, &b, &g, &m, &tmp, NULL)) != MP_OKAY) { + return mpi_to_ltc_error(err); + } + + /* load a and b */ + INPUT_BIGNUM(&a, sig, x, y, siglen); + INPUT_BIGNUM(&b, sig, x, y, siglen); + + /* load p and g */ + if ((err = mp_read_radix(&p, sets[key->idx].prime, 64)) != MP_OKAY) { goto error1; } + if ((err = mp_read_radix(&g, sets[key->idx].base, 64)) != MP_OKAY) { goto error1; } + + /* load m */ + if ((err = mp_read_unsigned_bin(&m, (unsigned char *)hash, hashlen)) != MP_OKAY) { goto error1; } + + /* find g^m mod p */ + if ((err = mp_exptmod(&g, &m, &p, &m)) != MP_OKAY) { goto error1; } /* m = g^m mod p */ + + /* find y^a * a^b */ + if ((err = mp_exptmod(&key->y, &a, &p, &tmp)) != MP_OKAY) { goto error1; } /* tmp = y^a mod p */ + if ((err = mp_exptmod(&a, &b, &p, &a)) != MP_OKAY) { goto error1; } /* a = a^b mod p */ + if ((err = mp_mulmod(&a, &tmp, &p, &a)) != MP_OKAY) { goto error1; } /* a = y^a * a^b mod p */ + + /* y^a * a^b == g^m ??? */ + if (mp_cmp(&a, &m) == 0) { + *stat = 1; + } + + /* clean up */ + err = CRYPT_OK; + goto done; +error1: + err = mpi_to_ltc_error(err); +error: +done: + mp_clear_multi(&tmp, &m, &g, &p, &b, &a, NULL); + return err; +} + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/dh/dh_sys.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2005/05/05 14:35:59 $ */