diff --git a/src/pkglue.c b/src/pkglue.c index af8cf92..87f1e15 100644 --- a/src/pkglue.c +++ b/src/pkglue.c @@ -1,286 +1,290 @@ /* pkglue.c - Public key fucntions * Copyright (C) 2014 g10 Code GmbH * * This file is part of NTBTLS * * NTBTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * NTBTLS 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "ntbtls-int.h" static const char * md_alg_string (md_algo_t md_alg) { switch (md_alg) { case GCRY_MD_SHA1: return "sha1"; case GCRY_MD_SHA224: return "sha224"; case GCRY_MD_SHA256: return "sha256"; case GCRY_MD_SHA384: return "sha384"; case GCRY_MD_SHA512: return "sha512"; case GCRY_MD_RMD160: return "rmd160"; default: return NULL; } } /* Return the public key algorithm id from the S-expression PKEY. FIXME: libgcrypt should provide such a function. Note that this implementation uses the names as used by libksba. */ static pk_algo_t pk_algo_from_sexp (gcry_sexp_t pkey) { gcry_sexp_t l1, l2; const char *name; size_t n; pk_algo_t algo; l1 = gcry_sexp_find_token (pkey, "public-key", 0); if (!l1) return 0; /* Not found. */ l2 = gcry_sexp_cadr (l1); gcry_sexp_release (l1); name = gcry_sexp_nth_data (l2, 0, &n); if (!name) algo = 0; /* Not found. */ else if (n==3 && !memcmp (name, "rsa", 3)) algo = GCRY_PK_RSA; else if (n==3 && !memcmp (name, "dsa", 3)) algo = GCRY_PK_DSA; else if (n==3 && !memcmp (name, "ecc", 3)) algo = GCRY_PK_ECC; else if (n==13 && !memcmp (name, "ambiguous-rsa", 13)) algo = GCRY_PK_RSA; else algo = 0; gcry_sexp_release (l2); return algo; } gpg_error_t _ntbtls_pk_verify (x509_cert_t chain, pk_algo_t pk_alg, md_algo_t md_alg, const unsigned char *hash, size_t hashlen, const unsigned char *sig, size_t siglen) { gpg_error_t err; gcry_sexp_t s_pk = NULL; gcry_sexp_t s_hash = NULL; gcry_sexp_t s_sig = NULL; const char *md_alg_str; if (!chain ||!md_alg || !hashlen || !sig || !siglen) return gpg_error (GPG_ERR_INV_ARG); md_alg_str = md_alg_string (md_alg); if (!md_alg_str) return gpg_error (GPG_ERR_DIGEST_ALGO); /* Get the public key from the first certificate. */ err = _ntbtls_x509_get_pk (chain, 0, &s_pk); if (err) goto leave; /* Check the Public key algorithm. */ { pk_algo_t alg; alg = pk_algo_from_sexp (s_pk); if (!alg) err = gpg_error (GPG_ERR_PUBKEY_ALGO); else if (alg != pk_alg) err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); /* Does not match. */ if (err) goto leave; } /* Put the hash and the signature into s-expressions. */ switch (pk_alg) { case GCRY_PK_RSA: err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", md_alg_str, (int)hashlen, hash); if (!err) err = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%b)))", (int)siglen, sig); break; case GCRY_PK_ECC: { unsigned int qbits0, qbits; const unsigned char *r, *s; int rlen, slen; qbits0 = gcry_pk_get_nbits (s_pk); qbits = qbits0 == 521? 512 : qbits0; if ((qbits%8)) { debug_msg (1, "qbits are not a multiple of 8 bits"); err = gpg_error (GPG_ERR_INTERNAL); goto leave; } if (qbits < 224) { debug_msg (1, "key uses an unsafe (%u bit) hash\n", qbits0); err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); goto leave; } - if (hashlen < qbits/8) + /* + * For TLS 1.2, it is possible for a server to use SHA256 with + * secpr384 key. See RFC8422 section 5.10. + */ + if (0 && hashlen < qbits/8) { debug_msg (1, "a %u bit hash is not valid for a %u bit ECC key", (unsigned int)hashlen*8, qbits); err = gpg_error (GPG_ERR_DIGEST_ALGO); goto leave; } if (hashlen > qbits/8) hashlen = qbits/8; /* Truncate. */ err = gcry_sexp_build (&s_hash, NULL, "(data (flags raw)(value %b))", (int)hashlen, hash); if (err) goto leave; /* 3045 -- SEQUENCE with length 0x45 * 0220 -- INTEGER with length 0x20 * 3045bcceccda9464c1d340a225e55e3d045e17ce004c0508a2cd61dd * 23a63ba6 * 0221 -- INTEGER with length 0x21 (due to 0x00 prefix) * 00e39b404793be76e87089ff3b5c306246a9f8cb52d94c77c624c3bf * 118e2418e8 */ if (siglen < 6 || sig[0] != 0x30 || sig[1] != siglen - 2 || sig[2] != 0x02) { err = gpg_error (GPG_ERR_INV_BER); goto leave; } siglen -= 2; sig += 2; rlen = sig[1]; if ((rlen != 32 && rlen != 33 && rlen != 48 && rlen != 49 && rlen != 64 && rlen != 65) || (rlen + 2 > siglen)) { /* The signature length is not 256, 384 or 512 bit. The * odd values are to handle an extra zero prefix. Or * the length is larger than the entire frame. */ err = gpg_error (GPG_ERR_INV_LENGTH); goto leave; } r = sig + 2; sig = r + rlen; siglen -= rlen + 2; if (siglen < 3 || sig[0] != 0x02) { err = gpg_error (GPG_ERR_INV_BER); goto leave; } siglen -= 2; slen = sig[1]; if ((slen > siglen) || ((rlen & ~1) != (slen & ~1))) { /* The length of S does not match the length of R. Or * the length is larger than the entire frame. */ err = gpg_error (GPG_ERR_INV_LENGTH); goto leave; } s = sig + 2; err = gcry_sexp_build (&s_sig, NULL, "(sig-val(ecdsa(r%b)(s%b)))", rlen, r, slen, s); } break; default: err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); break; } if (err) goto leave; debug_sxp (4, "sig ", s_sig); debug_sxp (4, "hash", s_hash); debug_sxp (4, "pk ", s_pk); err = gcry_pk_verify (s_sig, s_hash, s_pk); debug_msg (4, "res=%d", err); leave: gcry_sexp_release (s_pk); gcry_sexp_release (s_hash); gcry_sexp_release (s_sig); return err; } gpg_error_t _ntbtls_pk_encrypt (x509_cert_t chain, const unsigned char *input, size_t ilen, unsigned char *output, size_t *olen, size_t osize) { gpg_error_t err; gcry_sexp_t s_pk = NULL; gcry_sexp_t s_data = NULL; gcry_sexp_t s_ciph = NULL; size_t len; const char *data; /* Get the public key from the first certificate. */ err = _ntbtls_x509_get_pk (chain, 0, &s_pk); if (err) return err; err = gcry_sexp_build (&s_data, NULL, "(data (flags pkcs1) (value %b))", (int)ilen, input); if (err) { gcry_sexp_release (s_pk); return err; } err = gcry_pk_encrypt (&s_ciph, s_data, s_pk); gcry_sexp_release (s_data); s_data = NULL; gcry_sexp_release (s_pk); s_pk = NULL; if (err) return err; s_data = gcry_sexp_find_token (s_ciph, "a", 0); data = gcry_sexp_nth_data (s_data, 1, &len); if (data == NULL) err = gpg_error (GPG_ERR_BAD_MPI); else if (osize < len) err = gpg_error (GPG_ERR_TOO_SHORT); else { *olen = len; memcpy (output, data, len); } gcry_sexp_release (s_data); gcry_sexp_release (s_ciph); return err; }