diff options
author | Nikos Mavrogiannopoulos <nmav@crystal.(none)> | 2008-06-28 01:25:02 +0300 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@crystal.(none)> | 2008-06-28 01:25:02 +0300 |
commit | 95c55c0eb57484533f4dd72c10481c66a66a53f2 (patch) | |
tree | 3bc580f54abd1775b28415ae8e20aab4fe2baade /lib/opencdk/seskey.c | |
parent | 0def0a1d7c28de6fd49995755de7b915cf701225 (diff) | |
download | gnutls-95c55c0eb57484533f4dd72c10481c66a66a53f2.tar.gz |
Initial merge attempt with gnutls_with_ext_mpi
Diffstat (limited to 'lib/opencdk/seskey.c')
-rw-r--r-- | lib/opencdk/seskey.c | 706 |
1 files changed, 119 insertions, 587 deletions
diff --git a/lib/opencdk/seskey.c b/lib/opencdk/seskey.c index f8a9da8e59..f7d3af111d 100644 --- a/lib/opencdk/seskey.c +++ b/lib/opencdk/seskey.c @@ -40,618 +40,151 @@ * PAD consists of FF bytes. */ static cdk_error_t -do_encode_md (byte **r_frame, size_t *r_flen, const byte *md, int algo, - size_t len, unsigned nbits, const byte *asn, size_t asnlen) +do_encode_md(byte ** r_frame, size_t * r_flen, const byte * md, int algo, + size_t len, unsigned nbits, const byte * asn, size_t asnlen) { byte *frame = NULL; size_t nframe = (nbits + 7) / 8; - size_t i, n = 0; + ssize_t i; + size_t n = 0; if (!asn || !md || !r_frame || !r_flen) return CDK_Inv_Value; - + if (len + asnlen + 4 > nframe) return CDK_General_Error; - - frame = cdk_calloc (1, nframe); + + frame = cdk_calloc(1, nframe); if (!frame) return CDK_Out_Of_Core; frame[n++] = 0; frame[n++] = 1; i = nframe - len - asnlen - 3; - if (i < 0) - { - cdk_free (frame); - return CDK_Inv_Value; - } - memset (frame + n, 0xFF, i); + if (i < 0) { + cdk_free(frame); + return CDK_Inv_Value; + } + memset(frame + n, 0xFF, i); n += i; frame[n++] = 0; - memcpy (frame + n, asn, asnlen); + memcpy(frame + n, asn, asnlen); n += asnlen; - memcpy (frame + n, md, len); + memcpy(frame + n, md, len); n += len; - if (n != nframe) - { - cdk_free (frame); - return CDK_Inv_Value; - } + if (n != nframe) { + cdk_free(frame); + return CDK_Inv_Value; + } *r_frame = frame; *r_flen = n; return 0; } - - -/* RFC2437 format: - * - * 0 2 RND(n bytes) 0 [A DEK(k bytes) CSUM(2 bytes)] - * - * RND - randomized bytes for padding. - * A - cipher algorithm. - * DEK - random session key. - * CKSUM - algebraic checksum of the DEK. - */ - -/** - * cdk_dek_encode_pkcs1 - * @dek: DEK object - * @nbits: size of the multi precision integer frame - * @r_enc: output of the encoded multiprecision integer - * - * Encode the given random session key in the DEK object - * into a multiprecision integer. - **/ -cdk_error_t -cdk_dek_encode_pkcs1 (cdk_dek_t dek, size_t nbits, gcry_mpi_t *r_enc) -{ - gcry_mpi_t a = NULL; - gcry_error_t err; - byte *p, *frame; - size_t n; - size_t nframe; - size_t i; - u16 chksum; - - if (!r_enc || !dek) - return CDK_Inv_Value; - - *r_enc = NULL; - chksum = 0; - for (i = 0; i < dek->keylen; i++) - chksum += dek->key[i]; - nframe = (nbits + 7) / 8; - frame = cdk_salloc (nframe + 1, 1); - if (!frame) - return CDK_Out_Of_Core; - n = 0; - frame[n++] = 0x00; - frame[n++] = 0x02; - i = nframe - 6 - dek->keylen; - p = gcry_random_bytes (i, GCRY_STRONG_RANDOM); - /* Replace zero bytes by new values */ - for (;;) - { - size_t j, k; - byte *pp; - - /* count the zero bytes */ - for (j = k = 0; j < i; j++) - { - if (!p[j]) - k++; - } - if (!k) - break; /* No zeroes remain. */ - k += k / 128; /* better get some more */ - pp = gcry_random_bytes (k, GCRY_STRONG_RANDOM); - for (j = 0; j < i && k; j++) - { - if (!p[j]) - p[j] = pp[--k]; - } - cdk_free (pp); - } - memcpy (frame + n, p, i); - cdk_free (p); - n += i; - frame[n++] = 0; - frame[n++] = dek->algo; - memcpy (frame + n, dek->key, dek->keylen); - n += dek->keylen; - frame[n++] = chksum >> 8; - frame[n++] = chksum; - err = gcry_mpi_scan (&a, GCRYMPI_FMT_USG, frame, nframe, &nframe); - cdk_free (frame); - if (err) - return map_gcry_error (err); - *r_enc = a; - return 0; -} - - -/** - * cdk_dek_decode_pkcs1: - * @ret_dek: the decoded DEK object - * @esk: the pkcs#1 encoded session key. - * - * Decode the given multi precision integer in pkcs#1 and - * store it into the DEK object. - **/ -cdk_error_t -cdk_dek_decode_pkcs1 (cdk_dek_t *ret_dek, gcry_mpi_t esk) -{ - cdk_dek_t dek; - byte frame[MAX_MPI_BYTES+2+1]; - size_t nframe, n; - u16 csum, csum2; - gcry_error_t err; - - if (!ret_dek || !esk) - return CDK_Inv_Value; - - *ret_dek = NULL; /* reset */ - nframe = DIM (frame)-1; - err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &nframe, esk); - if (err) - return map_gcry_error (err); - dek = cdk_salloc (sizeof *dek, 1); - if (!dek) - return CDK_Out_Of_Core; - - /* Now get the DEK (data encryption key) from the frame - * - * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes) - * - * (gcry_mpi_print already removed the leading zero). - * - * RND are non-zero randow bytes. - * A is the cipher algorithm - * DEK is the encryption key (session key) with length k - * CSUM - */ - n = 0; - if (frame[n] != 2) - { - cdk_free (dek); - return CDK_Inv_Mode; - } - for (n++; n < nframe && frame[n]; n++) - ; - n++; - dek->keylen = nframe - (n + 1) - 2; - dek->algo = frame[n++]; - if (dek->keylen != gcry_cipher_get_algo_keylen (dek->algo)) - { - _cdk_log_debug ("pkcs1 decode: invalid cipher keylen %d\n", dek->keylen); - cdk_free (dek); - return CDK_Inv_Algo; +static const byte md5_asn[18] = /* Object ID is 1.2.840.113549.2.5 */ +{ 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 +}; + +static const byte sha1_asn[15] = /* Object ID is 1.3.14.3.2.26 */ +{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 +}; + +static const byte sha224_asn[19] = /* Object ID is 2.16.840.1.101.3.4.2.4 */ +{ 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, + 0x1C +}; + +static const byte sha256_asn[19] = /* Object ID is 2.16.840.1.101.3.4.2.1 */ +{ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, + 0x00, 0x04, 0x20 +}; + +static const byte sha512_asn[] = /* Object ID is 2.16.840.1.101.3.4.2.3 */ +{ + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, + 0x00, 0x04, 0x40 +}; + +static const byte sha384_asn[] = /* Object ID is 2.16.840.1.101.3.4.2.2 */ +{ + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, + 0x00, 0x04, 0x30 +}; + +static const byte rmd160_asn[15] = /* Object ID is 1.3.36.3.2.1 */ +{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, + 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 +}; + +static int _gnutls_get_digest_oid(gnutls_digest_algorithm_t algo, const byte** data) +{ + switch (algo) { + case GNUTLS_DIG_MD5: + *data = md5_asn; + return sizeof(md5_asn); + case GNUTLS_DIG_SHA1: + *data = sha1_asn; + return sizeof(sha1_asn); + case GNUTLS_DIG_RMD160: + *data = rmd160_asn; + return sizeof(rmd160_asn); + case GNUTLS_DIG_SHA256: + *data = sha256_asn; + return sizeof(sha256_asn); + case GNUTLS_DIG_SHA384: + *data = sha384_asn; + return sizeof(sha384_asn); + case GNUTLS_DIG_SHA512: + *data = sha512_asn; + return sizeof(sha512_asn); + case GNUTLS_DIG_SHA224: + *data = sha224_asn; + return sizeof(sha224_asn); + default: + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; } - csum = frame[nframe-2] << 8; - csum |= frame[nframe-1]; - memcpy (dek->key, frame + n, dek->keylen); - csum2 = 0; - for (n = 0; n < dek->keylen; n++) - csum2 += dek->key[n]; - if (csum != csum2) - { - _cdk_log_debug ("pkcs decode: checksum does not match\n"); - cdk_free (dek); - return CDK_Chksum_Error; - } - *ret_dek = dek; - return 0; } /* Encode the given digest into a pkcs#1 compatible format. */ cdk_error_t -_cdk_digest_encode_pkcs1 (byte **r_md, size_t *r_mdlen, int pk_algo, - const byte *md, int digest_algo, unsigned nbits) +_cdk_digest_encode_pkcs1(byte ** r_md, size_t * r_mdlen, int pk_algo, + const byte * md, int digest_algo, unsigned nbits) { - gcry_error_t err; size_t dlen; if (!md || !r_md || !r_mdlen) return CDK_Inv_Value; - - dlen = gcry_md_get_algo_dlen (digest_algo); - if (!dlen) - return CDK_Inv_Algo; - if (is_DSA (pk_algo)) /* DSS does not use a special encoding. */ - { - *r_md = cdk_malloc (dlen + 1); - if (!*r_md) - return CDK_Out_Of_Core; - *r_mdlen = dlen; - memcpy (*r_md, md, dlen); - return 0; - } - else - { - byte *asn; - size_t asnlen; - cdk_error_t rc; - - err = gcry_md_get_asnoid (digest_algo, NULL, &asnlen); - if (err) - return map_gcry_error (err); - asn = cdk_malloc (asnlen + 1); - if (!asn) - return CDK_Out_Of_Core; - err = gcry_md_get_asnoid (digest_algo, asn, &asnlen); - if (err) - { - cdk_free (asn); - return map_gcry_error (err); - } - rc = do_encode_md (r_md, r_mdlen, md, digest_algo, dlen, - nbits, asn, asnlen); - cdk_free (asn); - return rc; - } - return 0; -} - - -/* FIXME: The prompt should be provided in a more generic way. - Like: (keyid, algorithm, [user-id]) */ -static char* -passphrase_prompt (cdk_pkt_seckey_t sk) -{ - u32 keyid = cdk_pk_get_keyid (sk->pk, NULL); - int bits = cdk_pk_get_nbits (sk->pk), pk_algo = sk->pubkey_algo; - const char *algo = "???", *fmt; - char *p; - - if (is_RSA (pk_algo)) - algo = "RSA"; - else if (is_ELG (pk_algo)) - algo = "ELG"; - else if (is_DSA (pk_algo)) - algo = "DSA"; - - fmt = "%d-bit %s key, ID %08lX\nEnter Passphrase: "; - p = cdk_calloc (1, 64 + strlen (fmt) + strlen (algo) + 1); - if (!p) - return NULL; - sprintf (p, fmt, bits, algo, keyid); - return p; -} - - -/* Try to unprotect the secret key, if needed, automatically. - The passphrase callback is used to get the passphrase directly - from the user. */ -cdk_error_t -_cdk_sk_unprotect_auto (cdk_ctx_t hd, cdk_pkt_seckey_t sk) -{ - char *pw, *p; - cdk_error_t rc; - - if (!sk->is_protected) - return 0; - - p = passphrase_prompt (sk); - pw = _cdk_passphrase_get (hd, p); - cdk_free (p); - if (!pw) - return CDK_No_Passphrase; - - rc = cdk_sk_unprotect (sk, pw); - - wipemem (pw, strlen (pw)); - cdk_free (pw); - return rc; -} - - -/** - * cdk_dek_extract: - * @ret_dek: the raw DEK object - * @hd: the session handle - * @enc: the public key encrypted packet - * @sk: the secret key. - * - * Try to extract the DEK from the public key encrypted packet. - **/ -cdk_error_t -cdk_dek_extract (cdk_dek_t *ret_dek, cdk_ctx_t hd, - cdk_pkt_pubkey_enc_t enc, cdk_pkt_seckey_t sk) -{ - gcry_mpi_t skey = NULL; - cdk_dek_t dek; - cdk_error_t rc; - - if (!enc || !sk || !ret_dek) - return CDK_Inv_Value; - - /* FIXME: it is not very elegant that we need the session handle here. */ - if (sk->is_protected) - { - rc = _cdk_sk_unprotect_auto (hd, sk); - if (rc) - return rc; - } - - rc = cdk_pk_decrypt (sk, enc, &skey); - if (rc) - return rc; - - rc = cdk_dek_decode_pkcs1 (&dek, skey); - gcry_mpi_release (skey); - if (rc) - { - cdk_dek_free (dek); - dek = NULL; - } - *ret_dek = dek; - return rc; -} - - -/** - * cdk_dek_new: - * @r_dek: the new DEK object - * - * Create a new DEK object. - **/ -cdk_error_t -cdk_dek_new (cdk_dek_t *r_dek) -{ - cdk_dek_t dek; - - if (!r_dek) - return CDK_Inv_Value; - *r_dek = NULL; - dek = cdk_salloc (sizeof *dek, 1); - if (!dek) - return CDK_Out_Of_Core; - *r_dek = dek; - return 0; -} - -/** - * cdk_dek_set_cipher: - * @dek: the DEK object - * @algo: the cipher algorithm to use - * - * Set the cipher for the given DEK object. - **/ -cdk_error_t -cdk_dek_set_cipher (cdk_dek_t dek, int algo) -{ - if (!dek) - return CDK_Inv_Value; - - if (!algo) - algo = GCRY_CIPHER_AES128; - if (gcry_cipher_test_algo (algo)) + dlen = _gnutls_hash_get_algo_len(digest_algo); + if (dlen <= 0) return CDK_Inv_Algo; - dek->algo = algo; - dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); - return 0; -} - -cdk_error_t -cdk_dek_get_cipher (cdk_dek_t dek, int *r_algo) -{ - if (!dek || !r_algo) - return CDK_Inv_Value; - - - *r_algo = dek->algo; - return 0; -} - - -/** - * cdk_dek_set_key: - * @dek: the DEK object - * @key: the random session key - * @keylen: the length of the session key. - * - * Set the random session key for the given DEK object. - * If @key and @keylen is NULL (0) a random key will be generated. - * In any case, cdk_dek_set_cipher must be called first. - **/ -cdk_error_t -cdk_dek_set_key (cdk_dek_t dek, const byte *key, size_t keylen) -{ - gcry_cipher_hd_t hd; - size_t i; - - if (!dek) - return CDK_Inv_Value; - - /* The given key must be compatible with the symmetric - cipher algorithm set before. */ - if (keylen > 0 && keylen != dek->keylen) - return CDK_Inv_Mode; - - if (!key && !keylen) - { - gcry_error_t err; - - /* Used to generate a random session key. The extra code is used - to detect weak keys, if they are possible at all. */ - err = gcry_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, - GCRY_CIPHER_ENABLE_SYNC); - if (err) - return map_gcry_error (err); - gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM); - for (i = 0; i < 8; i++) - { - if (!gcry_cipher_setkey (hd, dek->key, dek->keylen)) - { - gcry_cipher_close (hd); - return 0; - } - gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM); - } - gcry_cipher_close (hd); - return CDK_Weak_Key; - } - - memcpy (dek->key, key, dek->keylen); - return 0; -} - - -/** - * cdk_dek_set_mdc_flag: - * @dek: the DEK object - * @val: value to enable or disable the use - * - * Enable or disable the MDC flag for the given DEK object. - **/ -void -cdk_dek_set_mdc_flag (cdk_dek_t dek, int val) -{ - if (dek) - dek->use_mdc = val; -} - - -int -cdk_dek_get_mdc_flag (cdk_dek_t dek) -{ - if (!dek) + if (is_DSA(pk_algo)) { /* DSS does not use a special encoding. */ + *r_md = cdk_malloc(dlen + 1); + if (!*r_md) + return CDK_Out_Of_Core; + *r_mdlen = dlen; + memcpy(*r_md, md, dlen); return 0; - return dek->use_mdc; -} - - -/** - * cdk_dek_free: - * @dek: the DEK object - * - * Release the DEK object. - **/ -void -cdk_dek_free (cdk_dek_t dek) -{ - if (!dek) - return; - - /* Make sure sentensive data is overwritten. */ - wipemem (dek->key, sizeof (dek->key)); - cdk_free (dek); -} + } else { + const byte *asn; + int asnlen; + cdk_error_t rc; + asnlen = _gnutls_get_digest_oid( digest_algo, &asn); + if (asnlen < 0) + return asnlen; -/* Hash the passphrase to produce the a DEK. - If create is set, a random salt will be generated. */ -static cdk_error_t -hash_passphrase (cdk_dek_t dek, const char *pw, cdk_s2k_t s2k, int create) -{ - gcry_md_hd_t md; - byte zero[1] = {0x00}; - int pass, i; - int used = 0, pwlen; - gcry_error_t err; - - if (!dek || !pw || !s2k) - return CDK_Inv_Value; - - if (!s2k->hash_algo) - s2k->hash_algo = GCRY_MD_SHA1; - pwlen = strlen (pw); - - dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); - err = gcry_md_open (&md, s2k->hash_algo, 0); - if (err) - return map_gcry_error (err); - - for (pass = 0; used < dek->keylen; pass++) - { - if (pass) - { - gcry_md_reset (md); - for (i = 0; i < pass; i++) /* preset the hash context */ - gcry_md_write (md, zero, 1); - } - if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED) - { - int len2 = pwlen + 8; - u32 count = len2; - if (create && !pass) - { - gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM); - if (s2k->mode == 3) - s2k->count = 96; /* 65536 iterations */ - } - if (s2k->mode == 3) - { - count = (16ul + (s2k->count & 15)) << ((s2k->count >> 4) + 6); - if (count < len2) - count = len2; - } - /* a little bit complicated because we need a ulong for count */ - while (count > len2) - { /* maybe iterated+salted */ - gcry_md_write (md, s2k->salt, 8); - gcry_md_write (md, pw, pwlen); - count -= len2; - } - if (count < 8) - gcry_md_write (md, s2k->salt, count); - else - { - gcry_md_write (md, s2k->salt, 8); - count -= 8; - gcry_md_write (md, pw, count); - } - } - else - gcry_md_write (md, pw, pwlen); - gcry_md_final (md); - i = gcry_md_get_algo_dlen (s2k->hash_algo); - if (i > dek->keylen - used) - i = dek->keylen - used; - memcpy (dek->key + used, gcry_md_read (md, s2k->hash_algo), i); - used += i; - } - gcry_md_close (md); - return 0; -} - - -/** - * cdk_dek_from_passphrase: - * @ret_dek: the new DEK. - * @cipher_algo: symmetric key algorithm to use - * @s2k: the S2K to use - * @rndsalt: 1=create random salt - * @pw: the passphrase. - * - * Transform a passphrase into a DEK object. - */ -cdk_error_t -cdk_dek_from_passphrase (cdk_dek_t *ret_dek, int cipher_algo, cdk_s2k_t s2k, - int rndsalt, const char *pw) -{ - cdk_dek_t dek; - cdk_error_t rc; - - if (!ret_dek) - return CDK_Inv_Value; - - *ret_dek = NULL; - rc = cdk_dek_new (&dek); - if (rc) + rc = do_encode_md(r_md, r_mdlen, md, digest_algo, dlen, + nbits, asn, asnlen); return rc; - rc = cdk_dek_set_cipher (dek, cipher_algo); - if (!rc) - rc = hash_passphrase (dek, pw, s2k, rndsalt); - if (rc) - { - cdk_dek_free (dek); - return rc; - } - - *ret_dek = dek; + } return 0; } @@ -667,7 +200,8 @@ cdk_dek_from_passphrase (cdk_dek_t *ret_dek, int cipher_algo, cdk_s2k_t s2k, * The @salt parameter must be always 8 octets. **/ cdk_error_t -cdk_s2k_new (cdk_s2k_t *ret_s2k, int mode, int digest_algo, const byte *salt) +cdk_s2k_new(cdk_s2k_t * ret_s2k, int mode, int digest_algo, + const byte * salt) { cdk_s2k_t s2k; @@ -676,17 +210,17 @@ cdk_s2k_new (cdk_s2k_t *ret_s2k, int mode, int digest_algo, const byte *salt) if (mode != 0x00 && mode != 0x01 && mode != 0x03) return CDK_Inv_Mode; - - if (gcry_md_test_algo (digest_algo)) + + if (_gnutls_hash_get_algo_len(digest_algo) <= 0) return CDK_Inv_Algo; - - s2k = cdk_calloc (1, sizeof *s2k); + + s2k = cdk_calloc(1, sizeof *s2k); if (!s2k) return CDK_Out_Of_Core; s2k->mode = mode; s2k->hash_algo = digest_algo; if (salt) - memcpy (s2k->salt, salt, 8); + memcpy(s2k->salt, salt, 8); *ret_s2k = s2k; return 0; } @@ -698,25 +232,23 @@ cdk_s2k_new (cdk_s2k_t *ret_s2k, int mode, int digest_algo, const byte *salt) * * Release the given S2K object. **/ -void -cdk_s2k_free (cdk_s2k_t s2k) +void cdk_s2k_free(cdk_s2k_t s2k) { - cdk_free (s2k); + cdk_free(s2k); } /* Make a copy of the source s2k into R_DST. */ -cdk_error_t -_cdk_s2k_copy (cdk_s2k_t *r_dst, cdk_s2k_t src) +cdk_error_t _cdk_s2k_copy(cdk_s2k_t * r_dst, cdk_s2k_t src) { cdk_s2k_t dst; cdk_error_t err; - - err = cdk_s2k_new (&dst, src->mode, src->hash_algo, src->salt); + + err = cdk_s2k_new(&dst, src->mode, src->hash_algo, src->salt); if (err) return err; dst->count = src->count; *r_dst = dst; - + return 0; } |