/* * Copyright (C) 2001-2012 Free Software Foundation, Inc. * * Author: Nikos Mavrogiannopoulos * * This file is part of GnuTLS. * * The GnuTLS is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * */ /* This file contains the functions needed for RSA/DSA public key * encryption and signatures. */ #include #include #include #include #include #include #include #include "debug.h" #include #include #include /* Do PKCS-1 RSA encryption. * params is modulus, public exp. */ int _gnutls_pkcs1_rsa_encrypt (gnutls_datum_t * ciphertext, const gnutls_datum_t * plaintext, gnutls_pk_params_st * params, unsigned btype) { unsigned int i, pad; int ret; uint8_t *edata, *ps; size_t k, psize; size_t mod_bits; gnutls_datum_t to_encrypt, encrypted; mod_bits = _gnutls_mpi_get_nbits (params->params[0]); k = mod_bits / 8; if (mod_bits % 8 != 0) k++; if (plaintext->size > k - 11) { gnutls_assert (); return GNUTLS_E_PK_ENCRYPTION_FAILED; } edata = gnutls_malloc (k); if (edata == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } /* EB = 00||BT||PS||00||D * (use block type 'btype') */ edata[0] = 0; edata[1] = btype; psize = k - 3 - plaintext->size; ps = &edata[2]; switch (btype) { case 2: /* using public key */ if (params->params_nr < RSA_PUBLIC_PARAMS) { gnutls_assert (); gnutls_free (edata); return GNUTLS_E_INTERNAL_ERROR; } ret = _gnutls_rnd (GNUTLS_RND_RANDOM, ps, psize); if (ret < 0) { gnutls_assert (); gnutls_free (edata); return ret; } for (i = 0; i < psize; i++) while (ps[i] == 0) { ret = _gnutls_rnd (GNUTLS_RND_RANDOM, &ps[i], 1); if (ret < 0) { gnutls_assert (); gnutls_free (edata); return ret; } } break; case 1: /* using private key */ if (params->params_nr < RSA_PRIVATE_PARAMS) { gnutls_assert (); gnutls_free (edata); return GNUTLS_E_INTERNAL_ERROR; } for (i = 0; i < psize; i++) ps[i] = 0xff; break; default: gnutls_assert (); gnutls_free (edata); return GNUTLS_E_INTERNAL_ERROR; } ps[psize] = 0; memcpy (&ps[psize + 1], plaintext->data, plaintext->size); to_encrypt.data = edata; to_encrypt.size = k; if (btype == 2) /* encrypt */ ret = _gnutls_pk_encrypt (GNUTLS_PK_RSA, &encrypted, &to_encrypt, params); else /* sign */ ret = _gnutls_pk_sign (GNUTLS_PK_RSA, &encrypted, &to_encrypt, params); gnutls_free (edata); if (ret < 0) { gnutls_assert (); return ret; } psize = encrypted.size; if (psize < k) { /* padding psize */ pad = k - psize; psize = k; } else if (psize == k) { /* pad = 0; * no need to do anything else */ ciphertext->data = encrypted.data; ciphertext->size = encrypted.size; return 0; } else { /* psize > k !!! */ /* This is an impossible situation */ gnutls_assert (); _gnutls_free_datum (&encrypted); return GNUTLS_E_INTERNAL_ERROR; } ciphertext->data = gnutls_malloc (psize); if (ciphertext->data == NULL) { gnutls_assert (); ret = GNUTLS_E_MEMORY_ERROR; goto cleanup; } memcpy (&ciphertext->data[pad], encrypted.data, encrypted.size); for (i = 0; i < pad; i++) ciphertext->data[i] = 0; ciphertext->size = k; ret = 0; cleanup: _gnutls_free_datum (&encrypted); return ret; } /* Do PKCS-1 RSA decryption. * params is modulus, public exp., private key * Can decrypt block type 1 and type 2 packets. */ int _gnutls_pkcs1_rsa_decrypt (gnutls_datum_t * plaintext, const gnutls_datum_t * ciphertext, gnutls_pk_params_st* params, unsigned btype) { unsigned int k, i; int ret; size_t esize, mod_bits; mod_bits = _gnutls_mpi_get_nbits (params->params[0]); k = mod_bits / 8; if (mod_bits % 8 != 0) k++; esize = ciphertext->size; if (esize != k) { gnutls_assert (); return GNUTLS_E_PK_DECRYPTION_FAILED; } /* we can use btype to see if the private key is * available. */ if (btype == 2) { ret = _gnutls_pk_decrypt (GNUTLS_PK_RSA, plaintext, ciphertext, params); } else { ret = _gnutls_pk_encrypt (GNUTLS_PK_RSA, plaintext, ciphertext, params); } if (ret < 0) { gnutls_assert (); return ret; } /* EB = 00||BT||PS||00||D * (use block type 'btype') * * From now on, return GNUTLS_E_DECRYPTION_FAILED on errors, to * avoid attacks similar to the one described by Bleichenbacher in: * "Chosen Ciphertext Attacks against Protocols Based on RSA * Encryption Standard PKCS #1". */ if (plaintext->data[0] != 0 || plaintext->data[1] != btype) { gnutls_assert (); _gnutls_free_datum (plaintext); return GNUTLS_E_DECRYPTION_FAILED; } ret = GNUTLS_E_DECRYPTION_FAILED; switch (btype) { case 2: for (i = 2; i < plaintext->size; i++) { if (plaintext->data[i] == 0) { ret = 0; break; } } break; case 1: for (i = 2; i < plaintext->size; i++) { if (plaintext->data[i] == 0 && i > 2) { ret = 0; break; } if (plaintext->data[i] != 0xff) { _gnutls_handshake_log ("PKCS #1 padding error"); _gnutls_free_datum (plaintext); /* PKCS #1 padding error. Don't use GNUTLS_E_PKCS1_WRONG_PAD here. */ break; } } break; default: gnutls_assert (); _gnutls_free_datum (plaintext); break; } if (ret < 0) { gnutls_assert (); _gnutls_free_datum (plaintext); return GNUTLS_E_DECRYPTION_FAILED; } i++; memmove (plaintext->data, &plaintext->data[i], esize - i); plaintext->size = esize - i; return 0; } int _gnutls_rsa_verify (const gnutls_datum_t * vdata, const gnutls_datum_t * ciphertext, gnutls_pk_params_st * params, int btype) { gnutls_datum_t plain; int ret; /* decrypt signature */ if ((ret = _gnutls_pkcs1_rsa_decrypt (&plain, ciphertext, params, btype)) < 0) { gnutls_assert (); return ret; } if (plain.size != vdata->size) { gnutls_assert (); _gnutls_free_datum (&plain); return GNUTLS_E_PK_SIG_VERIFY_FAILED; } if (memcmp (plain.data, vdata->data, plain.size) != 0) { gnutls_assert (); _gnutls_free_datum (&plain); return GNUTLS_E_PK_SIG_VERIFY_FAILED; } _gnutls_free_datum (&plain); return 0; /* ok */ } /* encodes the Dss-Sig-Value structure */ int _gnutls_encode_ber_rs (gnutls_datum_t * sig_value, bigint_t r, bigint_t s) { ASN1_TYPE sig; int result; if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DSASignatureValue", &sig)) != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } result = _gnutls_x509_write_int (sig, "r", r, 1); if (result < 0) { gnutls_assert (); asn1_delete_structure (&sig); return result; } result = _gnutls_x509_write_int (sig, "s", s, 1); if (result < 0) { gnutls_assert (); asn1_delete_structure (&sig); return result; } result = _gnutls_x509_der_encode (sig, "", sig_value, 0); asn1_delete_structure (&sig); if (result < 0) { gnutls_assert (); return result; } return 0; } /* decodes the Dss-Sig-Value structure */ int _gnutls_decode_ber_rs (const gnutls_datum_t * sig_value, bigint_t * r, bigint_t * s) { ASN1_TYPE sig; int result; if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DSASignatureValue", &sig)) != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } result = asn1_der_decoding (&sig, sig_value->data, sig_value->size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&sig); return _gnutls_asn2err (result); } result = _gnutls_x509_read_int (sig, "r", r); if (result < 0) { gnutls_assert (); asn1_delete_structure (&sig); return result; } result = _gnutls_x509_read_int (sig, "s", s); if (result < 0) { gnutls_assert (); _gnutls_mpi_release (s); asn1_delete_structure (&sig); return result; } asn1_delete_structure (&sig); return 0; } /* some generic pk functions */ int _gnutls_pk_params_copy (gnutls_pk_params_st * dst, const gnutls_pk_params_st * src) { unsigned int i, j; dst->params_nr = 0; if (src == NULL || src->params_nr == 0) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } for (i = 0; i < src->params_nr; i++) { dst->params[i] = _gnutls_mpi_set (NULL, src->params[i]); if (dst->params[i] == NULL) { for (j = 0; j < i; j++) _gnutls_mpi_release (&dst->params[j]); return GNUTLS_E_MEMORY_ERROR; } dst->params_nr++; } return 0; } void gnutls_pk_params_init (gnutls_pk_params_st * p) { memset (p, 0, sizeof (gnutls_pk_params_st)); } void gnutls_pk_params_release (gnutls_pk_params_st * p) { unsigned int i; for (i = 0; i < p->params_nr; i++) { _gnutls_mpi_release (&p->params[i]); } p->params_nr = 0; } int _gnutls_pk_get_hash_algorithm (gnutls_pk_algorithm_t pk, gnutls_pk_params_st* params, gnutls_digest_algorithm_t * dig, unsigned int *mand) { if (mand) { if (pk == GNUTLS_PK_DSA) *mand = 1; else *mand = 0; } return _gnutls_x509_verify_algorithm (dig, NULL, pk, params); }