diff --git a/cipher/cipher-cmac.c b/cipher/cipher-cmac.c index da3ef75..5c941b6 100644 --- a/cipher/cipher-cmac.c +++ b/cipher/cipher-cmac.c @@ -251,3 +251,246 @@ _gcry_cipher_cmac_set_subkeys (gcry_cipher_hd_t c) return GPG_ERR_NO_ERROR; } + +/* CMAC selftests. + * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 2019 Red Hat, Inc. + */ + + + +/* Check one MAC with MAC ALGO using the regular MAC + * API. (DATA,DATALEN) is the data to be MACed, (KEY,KEYLEN) the key + * and (EXPECT,EXPECTLEN) the expected result. If TRUNC is set, the + * EXPECTLEN may be less than the digest length. Returns NULL on + * success or a string describing the failure. */ +static const char * +check_one (int algo, + const void *data, size_t datalen, + const void *key, size_t keylen, + const void *expect, size_t expectlen) +{ + gcry_mac_hd_t hd; + unsigned char mac[512]; /* hardcoded to avoid allocation */ + size_t macoutlen = expectlen; + +/* printf ("MAC algo %d\n", algo); */ + if (_gcry_mac_get_algo_maclen (algo) != expectlen || + expectlen > sizeof (mac)) + return "invalid tests data"; + if (_gcry_mac_open (&hd, algo, 0, NULL)) + return "gcry_mac_open failed"; + if (_gcry_mac_setkey (hd, key, keylen)) + { + _gcry_mac_close (hd); + return "gcry_md_setkey failed"; + } + if (_gcry_mac_write (hd, data, datalen)) + { + _gcry_mac_close (hd); + return "gcry_mac_write failed"; + } + if (_gcry_mac_read (hd, mac, &macoutlen)) + { + _gcry_mac_close (hd); + return "gcry_mac_read failed"; + } + _gcry_mac_close (hd); + if (macoutlen != expectlen || memcmp (mac, expect, expectlen)) + { +/* int i; */ + +/* fputs (" {", stdout); */ +/* for (i=0; i < expectlen-1; i++) */ +/* { */ +/* if (i && !(i % 8)) */ +/* fputs ("\n ", stdout); */ +/* printf (" 0x%02x,", mac[i]); */ +/* } */ +/* printf (" 0x%02x } },\n", mac[i]); */ + + return "does not match"; + } + return NULL; +} + + +static gpg_err_code_t +selftests_cmac_tdes (int extended, selftest_report_func_t report) +{ + const char *what; + const char *errtxt; + + what = "Basic TDES"; + errtxt = check_one (GCRY_MAC_CMAC_3DES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57", 20, + "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" + "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", 24, + "\x74\x3d\xdb\xe0\xce\x2d\xc2\xed", 8); + if (errtxt) + goto failed; + + if (extended) + { + what = "Extended TDES #1"; + errtxt = check_one (GCRY_MAC_CMAC_3DES, + "", 0, + "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" + "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", 24, + "\xb7\xa6\x88\xe1\x22\xff\xaf\x95", 8); + if (errtxt) + goto failed; + + what = "Extended TDES #2"; + errtxt = check_one (GCRY_MAC_CMAC_3DES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96", 8, + "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" + "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", 24, + "\x8e\x8f\x29\x31\x36\x28\x37\x97", 8); + if (errtxt) + goto failed; + + what = "Extended TDES #3"; + errtxt = check_one (GCRY_MAC_CMAC_3DES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51", 32, + "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" + "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", 24, + "\x33\xe6\xb1\x09\x24\x00\xea\xe5", 8); + if (errtxt) + goto failed; + } + + return 0; /* Succeeded. */ + + failed: + if (report) + report ("cmac", GCRY_MAC_CMAC_3DES, what, errtxt); + return GPG_ERR_SELFTEST_FAILED; +} + + + +static gpg_err_code_t +selftests_cmac_aes (int extended, selftest_report_func_t report) +{ + const char *what; + const char *errtxt; + + what = "Basic AES128"; + errtxt = check_one (GCRY_MAC_CMAC_AES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11", 40, + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "\xdf\xa6\x67\x47\xde\x9a\xe6\x30\x30\xca\x32\x61\x14\x97\xc8\x27", 16); + if (errtxt) + goto failed; + + what = "Basic AES192"; + errtxt = check_one (GCRY_MAC_CMAC_AES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11", 40, + "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5" + "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", 24, + "\x8a\x1d\xe5\xbe\x2e\xb3\x1a\xad\x08\x9a\x82\xe6\xee\x90\x8b\x0e", 16); + if (errtxt) + goto failed; + + what = "Basic AES256"; + errtxt = check_one (GCRY_MAC_CMAC_AES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11", 40, + "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81" + "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4", 32, + "\xaa\xf3\xd8\xf1\xde\x56\x40\xc2\x32\xf5\xb1\x69\xb9\xc9\x11\xe6", 16); + if (errtxt) + goto failed; + if (extended) + { + what = "Extended AES #1"; + errtxt = check_one (GCRY_MAC_CMAC_AES, + "", 0, + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "\xbb\x1d\x69\x29\xe9\x59\x37\x28\x7f\xa3\x7d\x12\x9b\x75\x67\x46", 16); + if (errtxt) + goto failed; + + what = "Extended AES #2"; + errtxt = check_one (GCRY_MAC_CMAC_AES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a", 16, + "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5" + "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", 24, + "\x9e\x99\xa7\xbf\x31\xe7\x10\x90\x06\x62\xf6\x5e\x61\x7c\x51\x84", 16); + if (errtxt) + goto failed; + + what = "Extended AES #3"; + errtxt = check_one (GCRY_MAC_CMAC_AES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10", 64, + "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81" + "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4", 32, + "\xe1\x99\x21\x90\x54\x9f\x6e\xd5\x69\x6a\x2c\x05\x6c\x31\x54\x10", 16 ); + if (errtxt) + goto failed; + } + + return 0; /* Succeeded. */ + + failed: + if (report) + report ("cmac", GCRY_MAC_CMAC_AES, what, errtxt); + return GPG_ERR_SELFTEST_FAILED; +} + + +/* Run a full self-test for ALGO and return 0 on success. */ +static gpg_err_code_t +run_cmac_selftests (int algo, int extended, selftest_report_func_t report) +{ + gpg_err_code_t ec; + + switch (algo) + { + case GCRY_MAC_CMAC_3DES: + ec = selftests_cmac_tdes (extended, report); + break; + case GCRY_MAC_CMAC_AES: + ec = selftests_cmac_aes (extended, report); + break; + + default: + ec = GPG_ERR_MAC_ALGO; + break; + } + return ec; +} + + + + +/* Run the selftests for CMAC with CMAC algorithm ALGO with optional + reporting function REPORT. */ +gpg_error_t +_gcry_cmac_selftest (int algo, int extended, selftest_report_func_t report) +{ + gcry_err_code_t ec = 0; + + if (!_gcry_mac_algo_info( algo, GCRYCTL_TEST_ALGO, NULL, NULL )) + { + ec = run_cmac_selftests (algo, extended, report); + } + else + { + ec = GPG_ERR_MAC_ALGO; + if (report) + report ("mac", algo, "module", "algorithm not available"); + } + return gpg_error (ec); +} diff --git a/cipher/cipher-cmac.c.cmac-selftest b/cipher/cipher-cmac.c.cmac-selftest new file mode 100644 index 0000000..da3ef75 --- /dev/null +++ b/cipher/cipher-cmac.c.cmac-selftest @@ -0,0 +1,253 @@ +/* cmac.c - CMAC, Cipher-based MAC. + * Copyright (C) 2013 Jussi Kivilinna + * + * This file is part of Libgcrypt. + * + * Libgcrypt 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 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt 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 . + */ + +#include +#include +#include +#include + +#include "g10lib.h" +#include "cipher.h" +#include "cipher-internal.h" +#include "bufhelp.h" + + +#define set_burn(burn, nburn) do { \ + unsigned int __nburn = (nburn); \ + (burn) = (burn) > __nburn ? (burn) : __nburn; } while (0) + + +static void +cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen) +{ + gcry_cipher_encrypt_t enc_fn = c->spec->encrypt; + const unsigned int blocksize = c->spec->blocksize; + byte outbuf[MAX_BLOCKSIZE]; + unsigned int burn = 0; + unsigned int nblocks; + + /* Tell compiler that we require a cipher with a 64bit or 128 bit block + * length, to allow better optimization of this function. */ + if (blocksize > 16 || blocksize < 8 || blocksize & (8 - 1)) + return; + + if (!inlen || !inbuf) + return; + + /* Last block is needed for cmac_final. */ + if (c->unused + inlen <= blocksize) + { + for (; inlen && c->unused < blocksize; inlen--) + c->lastiv[c->unused++] = *inbuf++; + return; + } + + if (c->unused) + { + for (; inlen && c->unused < blocksize; inlen--) + c->lastiv[c->unused++] = *inbuf++; + + buf_xor (c->u_iv.iv, c->u_iv.iv, c->lastiv, blocksize); + set_burn (burn, enc_fn (&c->context.c, c->u_iv.iv, c->u_iv.iv)); + + c->unused = 0; + } + + if (c->bulk.cbc_enc && inlen > blocksize) + { + nblocks = inlen / blocksize; + nblocks -= (nblocks * blocksize == inlen); + + c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks, 1); + inbuf += nblocks * blocksize; + inlen -= nblocks * blocksize; + + wipememory (outbuf, sizeof (outbuf)); + } + else + while (inlen > blocksize) + { + buf_xor (c->u_iv.iv, c->u_iv.iv, inbuf, blocksize); + set_burn (burn, enc_fn (&c->context.c, c->u_iv.iv, c->u_iv.iv)); + inlen -= blocksize; + inbuf += blocksize; + } + + /* Make sure that last block is passed to cmac_final. */ + if (inlen == 0) + BUG (); + + for (; inlen && c->unused < blocksize; inlen--) + c->lastiv[c->unused++] = *inbuf++; + + if (burn) + _gcry_burn_stack (burn + 4 * sizeof (void *)); +} + + +static void +cmac_generate_subkeys (gcry_cipher_hd_t c) +{ + const unsigned int blocksize = c->spec->blocksize; + byte rb, carry, t, bi; + unsigned int burn; + int i, j; + union + { + size_t _aligned; + byte buf[MAX_BLOCKSIZE]; + } u; + + /* Tell compiler that we require a cipher with a 64bit or 128 bit block + * length, to allow better optimization of this function. */ + if (blocksize > 16 || blocksize < 8 || blocksize & (8 - 1)) + return; + + if (MAX_BLOCKSIZE < blocksize) + BUG (); + + /* encrypt zero block */ + memset (u.buf, 0, blocksize); + burn = c->spec->encrypt (&c->context.c, u.buf, u.buf); + + /* Currently supported blocksizes are 16 and 8. */ + rb = blocksize == 16 ? 0x87 : 0x1B /*blocksize == 8 */ ; + + for (j = 0; j < 2; j++) + { + /* Generate subkeys K1 and K2 */ + carry = 0; + for (i = blocksize - 1; i >= 0; i--) + { + bi = u.buf[i]; + t = carry | (bi << 1); + carry = bi >> 7; + u.buf[i] = t & 0xff; + c->u_mode.cmac.subkeys[j][i] = u.buf[i]; + } + u.buf[blocksize - 1] ^= carry ? rb : 0; + c->u_mode.cmac.subkeys[j][blocksize - 1] = u.buf[blocksize - 1]; + } + + wipememory (&u, sizeof (u)); + if (burn) + _gcry_burn_stack (burn + 4 * sizeof (void *)); +} + + +static void +cmac_final (gcry_cipher_hd_t c) +{ + const unsigned int blocksize = c->spec->blocksize; + unsigned int count = c->unused; + unsigned int burn; + byte *subkey; + + /* Tell compiler that we require a cipher with a 64bit or 128 bit block + * length, to allow better optimization of this function. */ + if (blocksize > 16 || blocksize < 8 || blocksize & (8 - 1)) + return; + + if (count == blocksize) + subkey = c->u_mode.cmac.subkeys[0]; /* K1 */ + else + { + subkey = c->u_mode.cmac.subkeys[1]; /* K2 */ + c->lastiv[count++] = 0x80; + while (count < blocksize) + c->lastiv[count++] = 0; + } + + buf_xor (c->lastiv, c->lastiv, subkey, blocksize); + + buf_xor (c->u_iv.iv, c->u_iv.iv, c->lastiv, blocksize); + burn = c->spec->encrypt (&c->context.c, c->u_iv.iv, c->u_iv.iv); + if (burn) + _gcry_burn_stack (burn + 4 * sizeof (void *)); + + c->unused = 0; +} + + +static gcry_err_code_t +cmac_tag (gcry_cipher_hd_t c, unsigned char *tag, size_t taglen, int check) +{ + if (!tag || taglen == 0 || taglen > c->spec->blocksize) + return GPG_ERR_INV_ARG; + + if (!c->u_mode.cmac.tag) + { + cmac_final (c); + c->u_mode.cmac.tag = 1; + } + + if (!check) + { + memcpy (tag, c->u_iv.iv, taglen); + return GPG_ERR_NO_ERROR; + } + else + { + return buf_eq_const (tag, c->u_iv.iv, taglen) ? + GPG_ERR_NO_ERROR : GPG_ERR_CHECKSUM; + } +} + + +gcry_err_code_t +_gcry_cipher_cmac_authenticate (gcry_cipher_hd_t c, + const unsigned char *abuf, size_t abuflen) +{ + if (abuflen > 0 && !abuf) + return GPG_ERR_INV_ARG; + if (c->u_mode.cmac.tag) + return GPG_ERR_INV_STATE; + /* To support new blocksize, update cmac_generate_subkeys() then add new + blocksize here. */ + if (c->spec->blocksize != 16 && c->spec->blocksize != 8) + return GPG_ERR_INV_CIPHER_MODE; + + cmac_write (c, abuf, abuflen); + + return GPG_ERR_NO_ERROR; +} + + +gcry_err_code_t +_gcry_cipher_cmac_get_tag (gcry_cipher_hd_t c, + unsigned char *outtag, size_t taglen) +{ + return cmac_tag (c, outtag, taglen, 0); +} + + +gcry_err_code_t +_gcry_cipher_cmac_check_tag (gcry_cipher_hd_t c, + const unsigned char *intag, size_t taglen) +{ + return cmac_tag (c, (unsigned char *) intag, taglen, 1); +} + +gcry_err_code_t +_gcry_cipher_cmac_set_subkeys (gcry_cipher_hd_t c) +{ + cmac_generate_subkeys (c); + + return GPG_ERR_NO_ERROR; +} diff --git a/src/cipher-proto.h b/src/cipher-proto.h index d1ddc5d..d831d3f 100644 --- a/src/cipher-proto.h +++ b/src/cipher-proto.h @@ -256,6 +256,8 @@ gcry_error_t _gcry_pk_selftest (int algo, int extended, selftest_report_func_t report); gcry_error_t _gcry_hmac_selftest (int algo, int extended, selftest_report_func_t report); +gcry_error_t _gcry_cmac_selftest (int algo, int extended, + selftest_report_func_t report); gcry_error_t _gcry_random_selftest (selftest_report_func_t report); diff --git a/src/cipher-proto.h.cmac-selftest b/src/cipher-proto.h.cmac-selftest new file mode 100644 index 0000000..d1ddc5d --- /dev/null +++ b/src/cipher-proto.h.cmac-selftest @@ -0,0 +1,265 @@ +/* cipher-proto.h - Internal declarations + * Copyright (C) 2008, 2011 Free Software Foundation, Inc. + * + * This file is part of Libgcrypt. + * + * Libgcrypt 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 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt 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 has been factored out from cipher.h so that it can be + used standalone in visibility.c . */ + +#ifndef G10_CIPHER_PROTO_H +#define G10_CIPHER_PROTO_H + + +enum pk_encoding; + + +/* Definition of a function used to report selftest failures. + DOMAIN is a string describing the function block: + "cipher", "digest", "pubkey or "random", + ALGO is the algorithm under test, + WHAT is a string describing what has been tested, + DESC is a string describing the error. */ +typedef void (*selftest_report_func_t)(const char *domain, + int algo, + const char *what, + const char *errdesc); + +/* Definition of the selftest functions. */ +typedef gpg_err_code_t (*selftest_func_t) + (int algo, int extended, selftest_report_func_t report); + + +/* + * + * Public key related definitions. + * + */ + +/* Type for the pk_generate function. */ +typedef gcry_err_code_t (*gcry_pk_generate_t) (gcry_sexp_t genparms, + gcry_sexp_t *r_skey); + +/* Type for the pk_check_secret_key function. */ +typedef gcry_err_code_t (*gcry_pk_check_secret_key_t) (gcry_sexp_t keyparms); + +/* Type for the pk_encrypt function. */ +typedef gcry_err_code_t (*gcry_pk_encrypt_t) (gcry_sexp_t *r_ciph, + gcry_sexp_t s_data, + gcry_sexp_t keyparms); + +/* Type for the pk_decrypt function. */ +typedef gcry_err_code_t (*gcry_pk_decrypt_t) (gcry_sexp_t *r_plain, + gcry_sexp_t s_data, + gcry_sexp_t keyparms); + +/* Type for the pk_sign function. */ +typedef gcry_err_code_t (*gcry_pk_sign_t) (gcry_sexp_t *r_sig, + gcry_sexp_t s_data, + gcry_sexp_t keyparms); + +/* Type for the pk_verify function. */ +typedef gcry_err_code_t (*gcry_pk_verify_t) (gcry_sexp_t s_sig, + gcry_sexp_t s_data, + gcry_sexp_t keyparms); + +/* Type for the pk_get_nbits function. */ +typedef unsigned (*gcry_pk_get_nbits_t) (gcry_sexp_t keyparms); + + +/* The type used to compute the keygrip. */ +typedef gpg_err_code_t (*pk_comp_keygrip_t) (gcry_md_hd_t md, + gcry_sexp_t keyparm); + +/* The type used to query an ECC curve name. */ +typedef const char *(*pk_get_curve_t)(gcry_sexp_t keyparms, int iterator, + unsigned int *r_nbits); + +/* The type used to query ECC curve parameters by name. */ +typedef gcry_sexp_t (*pk_get_curve_param_t)(const char *name); + + +/* Module specification structure for public key algorithms. */ +typedef struct gcry_pk_spec +{ + int algo; + struct { + unsigned int disabled:1; + unsigned int fips:1; + } flags; + int use; + const char *name; + const char **aliases; + const char *elements_pkey; + const char *elements_skey; + const char *elements_enc; + const char *elements_sig; + const char *elements_grip; + gcry_pk_generate_t generate; + gcry_pk_check_secret_key_t check_secret_key; + gcry_pk_encrypt_t encrypt; + gcry_pk_decrypt_t decrypt; + gcry_pk_sign_t sign; + gcry_pk_verify_t verify; + gcry_pk_get_nbits_t get_nbits; + selftest_func_t selftest; + pk_comp_keygrip_t comp_keygrip; + pk_get_curve_t get_curve; + pk_get_curve_param_t get_curve_param; +} gcry_pk_spec_t; + + + +/* + * + * Symmetric cipher related definitions. + * + */ + +/* Type for the cipher_setkey function. */ +typedef gcry_err_code_t (*gcry_cipher_setkey_t) (void *c, + const unsigned char *key, + unsigned keylen); + +/* Type for the cipher_encrypt function. */ +typedef unsigned int (*gcry_cipher_encrypt_t) (void *c, + unsigned char *outbuf, + const unsigned char *inbuf); + +/* Type for the cipher_decrypt function. */ +typedef unsigned int (*gcry_cipher_decrypt_t) (void *c, + unsigned char *outbuf, + const unsigned char *inbuf); + +/* Type for the cipher_stencrypt function. */ +typedef void (*gcry_cipher_stencrypt_t) (void *c, + unsigned char *outbuf, + const unsigned char *inbuf, + size_t n); + +/* Type for the cipher_stdecrypt function. */ +typedef void (*gcry_cipher_stdecrypt_t) (void *c, + unsigned char *outbuf, + const unsigned char *inbuf, + size_t n); + +/* The type used to convey additional information to a cipher. */ +typedef gpg_err_code_t (*cipher_set_extra_info_t) + (void *c, int what, const void *buffer, size_t buflen); + +/* The type used to set an IV directly in the algorithm module. */ +typedef void (*cipher_setiv_func_t)(void *c, const byte *iv, size_t ivlen); + +/* A structure to map OIDs to encryption modes. */ +typedef struct gcry_cipher_oid_spec +{ + const char *oid; + int mode; +} gcry_cipher_oid_spec_t; + + +/* Module specification structure for ciphers. */ +typedef struct gcry_cipher_spec +{ + int algo; + struct { + unsigned int disabled:1; + unsigned int fips:1; + } flags; + const char *name; + const char **aliases; + gcry_cipher_oid_spec_t *oids; + size_t blocksize; + size_t keylen; + size_t contextsize; + gcry_cipher_setkey_t setkey; + gcry_cipher_encrypt_t encrypt; + gcry_cipher_decrypt_t decrypt; + gcry_cipher_stencrypt_t stencrypt; + gcry_cipher_stdecrypt_t stdecrypt; + selftest_func_t selftest; + cipher_set_extra_info_t set_extra_info; + cipher_setiv_func_t setiv; +} gcry_cipher_spec_t; + + + +/* + * + * Message digest related definitions. + * + */ + +/* Type for the md_init function. */ +typedef void (*gcry_md_init_t) (void *c, unsigned int flags); + +/* Type for the md_write function. */ +typedef void (*gcry_md_write_t) (void *c, const void *buf, size_t nbytes); + +/* Type for the md_final function. */ +typedef void (*gcry_md_final_t) (void *c); + +/* Type for the md_read function. */ +typedef unsigned char *(*gcry_md_read_t) (void *c); + +/* Type for the md_extract function. */ +typedef void (*gcry_md_extract_t) (void *c, void *outbuf, size_t nbytes); + +typedef struct gcry_md_oid_spec +{ + const char *oidstring; +} gcry_md_oid_spec_t; + +/* Module specification structure for message digests. */ +typedef struct gcry_md_spec +{ + int algo; + struct { + unsigned int disabled:1; + unsigned int fips:1; + } flags; + const char *name; + unsigned char *asnoid; + int asnlen; + gcry_md_oid_spec_t *oids; + int mdlen; + gcry_md_init_t init; + gcry_md_write_t write; + gcry_md_final_t final; + gcry_md_read_t read; + gcry_md_extract_t extract; + size_t contextsize; /* allocate this amount of context */ + selftest_func_t selftest; +} gcry_md_spec_t; + + + +/* The selftest functions. */ +gcry_error_t _gcry_cipher_selftest (int algo, int extended, + selftest_report_func_t report); +gcry_error_t _gcry_md_selftest (int algo, int extended, + selftest_report_func_t report); +gcry_error_t _gcry_pk_selftest (int algo, int extended, + selftest_report_func_t report); +gcry_error_t _gcry_hmac_selftest (int algo, int extended, + selftest_report_func_t report); + +gcry_error_t _gcry_random_selftest (selftest_report_func_t report); + + + + +#endif /*G10_CIPHER_PROTO_H*/ diff --git a/src/fips.c b/src/fips.c index 14586e6..0efad72 100644 --- a/src/fips.c +++ b/src/fips.c @@ -521,29 +521,32 @@ run_digest_selftests (int extended) /* Run self-tests for all HMAC algorithms. Return 0 on success. */ static int -run_hmac_selftests (int extended) +run_mac_selftests (int extended) { - static int algos[] = + static int algos[][2] = { - GCRY_MD_SHA1, - GCRY_MD_SHA224, - GCRY_MD_SHA256, - GCRY_MD_SHA384, - GCRY_MD_SHA512, - GCRY_MD_SHA3_224, - GCRY_MD_SHA3_256, - GCRY_MD_SHA3_384, - GCRY_MD_SHA3_512, - 0 + { GCRY_MD_SHA1, 0 }, + { GCRY_MD_SHA224, 0 }, + { GCRY_MD_SHA256, 0 }, + { GCRY_MD_SHA384, 0 }, + { GCRY_MD_SHA512, 0 }, + { GCRY_MD_SHA3_224, 0 }, + { GCRY_MD_SHA3_256, 0 }, + { GCRY_MD_SHA3_384, 0 }, + { GCRY_MD_SHA3_512, 0 }, + { GCRY_MAC_CMAC_3DES, 1 }, + { GCRY_MAC_CMAC_AES, 1 }, + { 0, 0 } }; int idx; gpg_error_t err; int anyerr = 0; - for (idx=0; algos[idx]; idx++) + for (idx=0; algos[idx][0]; idx++) { - err = _gcry_hmac_selftest (algos[idx], extended, reporter); - reporter ("hmac", algos[idx], NULL, + err = algos[idx][1] ? _gcry_cmac_selftest (algos[idx][0], extended, reporter) : + _gcry_hmac_selftest (algos[idx][0], extended, reporter); + reporter (algos[idx][1] ? "cmac" : "hmac", algos[idx][0], NULL, err? gpg_strerror (err):NULL); if (err) anyerr = 1; @@ -747,7 +750,7 @@ _gcry_fips_run_selftests (int extended) if (run_digest_selftests (extended)) goto leave; - if (run_hmac_selftests (extended)) + if (run_mac_selftests (extended)) goto leave; /* Run random tests before the pubkey tests because the latter diff --git a/src/fips.c.cmac-selftest b/src/fips.c.cmac-selftest new file mode 100644 index 0000000..14586e6 --- /dev/null +++ b/src/fips.c.cmac-selftest @@ -0,0 +1,919 @@ +/* fips.c - FIPS mode management + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This file is part of Libgcrypt. + * + * Libgcrypt 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 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt 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 . + */ + +#include +#include +#include +#include +#include +#include +#ifdef ENABLE_HMAC_BINARY_CHECK +# include +#endif +#ifdef HAVE_SYSLOG +# include +#endif /*HAVE_SYSLOG*/ + +#include "g10lib.h" +#include "cipher-proto.h" +#include "hmac256.h" + + +/* The name of the file used to force libgcrypt into fips mode. */ +#define FIPS_FORCE_FILE "/etc/gcrypt/fips_enabled" + + +/* The states of the finite state machine used in fips mode. */ +enum module_states + { + /* POWEROFF cannot be represented. */ + STATE_POWERON = 0, + STATE_INIT, + STATE_SELFTEST, + STATE_OPERATIONAL, + STATE_ERROR, + STATE_FATALERROR, + STATE_SHUTDOWN + }; + + +/* Flag telling whether we are in fips mode. It uses inverse logic so + that fips mode is the default unless changed by the initialization + code. To check whether fips mode is enabled, use the function + fips_mode()! */ +static int no_fips_mode_required; + +/* Flag to indicate that we are in the enforced FIPS mode. */ +static int enforced_fips_mode; + +/* If this flag is set, the application may no longer assume that the + process is running in FIPS mode. This flag is protected by the + FSM_LOCK. */ +static int inactive_fips_mode; + +/* This is the lock we use to protect the FSM. */ +GPGRT_LOCK_DEFINE (fsm_lock); + +/* The current state of the FSM. The whole state machinery is only + used while in fips mode. Change this only while holding fsm_lock. */ +static enum module_states current_state; + + + + + +static void fips_new_state (enum module_states new_state); + + + +/* Convert lowercase hex digits; assumes valid hex digits. */ +#define loxtoi_1(p) (*(p) <= '9'? (*(p)- '0'): (*(p)-'a'+10)) +#define loxtoi_2(p) ((loxtoi_1(p) * 16) + loxtoi_1((p)+1)) + +/* Returns true if P points to a lowercase hex digit. */ +#define loxdigit_p(p) !!strchr ("01234567890abcdef", *(p)) + + + +/* Initialize the FSM lock - this function may only + be called once and is intended to be run from the library + constructor */ +void +_gcry_initialize_fsm_lock (void) +{ + gpg_error_t err; + /* Intitialize the lock to protect the FSM. */ + err = gpgrt_lock_init (&fsm_lock); + if (err) + { + /* If that fails we can't do anything but abort the + process. We need to use log_info so that the FSM won't + get involved. */ + log_info ("FATAL: failed to create the FSM lock in libgcrypt: %s\n", + gpg_strerror (err)); +#ifdef HAVE_SYSLOG + syslog (LOG_USER|LOG_ERR, "Libgcrypt error: " + "creating FSM lock failed: %s - abort", + gpg_strerror (err)); +#endif /*HAVE_SYSLOG*/ + abort (); + } +} + +/* Check whether the OS is in FIPS mode and record that in a module + local variable. If FORCE is passed as true, fips mode will be + enabled anyway. Note: This function is not thread-safe and should + be called before any threads are created. This function may only + be called once. */ +void +_gcry_initialize_fips_mode (int force) +{ + static int done; + + /* Make sure we are not accidentally called twice. */ + if (done) + { + if ( fips_mode () ) + { + fips_new_state (STATE_FATALERROR); + fips_noreturn (); + } + /* If not in fips mode an assert is sufficient. */ + gcry_assert (!done); + } + done = 1; + + /* If the calling application explicitly requested fipsmode, do so. */ + if (force) + { + gcry_assert (!no_fips_mode_required); + goto leave; + } + + /* For testing the system it is useful to override the system + provided detection of the FIPS mode and force FIPS mode using a + file. The filename is hardwired so that there won't be any + confusion on whether /etc/gcrypt/ or /usr/local/etc/gcrypt/ is + actually used. The file itself may be empty. */ + if ( !access (FIPS_FORCE_FILE, F_OK) ) + { + gcry_assert (!no_fips_mode_required); + goto leave; + } + + /* Checking based on /proc file properties. */ + { + static const char procfname[] = "/proc/sys/crypto/fips_enabled"; + FILE *fp; + int saved_errno; + + fp = fopen (procfname, "r"); + if (fp) + { + char line[256]; + + if (fgets (line, sizeof line, fp) && atoi (line)) + { + /* System is in fips mode. */ + fclose (fp); + gcry_assert (!no_fips_mode_required); + goto leave; + } + fclose (fp); + } + else if ((saved_errno = errno) != ENOENT + && saved_errno != EACCES + && !access ("/proc/version", F_OK) ) + { + /* Problem reading the fips file despite that we have the proc + file system. We better stop right away. */ + log_info ("FATAL: error reading `%s' in libgcrypt: %s\n", + procfname, strerror (saved_errno)); +#ifdef HAVE_SYSLOG + syslog (LOG_USER|LOG_ERR, "Libgcrypt error: " + "reading `%s' failed: %s - abort", + procfname, strerror (saved_errno)); +#endif /*HAVE_SYSLOG*/ + abort (); + } + } + + /* Fips not not requested, set flag. */ + no_fips_mode_required = 1; + + leave: + if (!no_fips_mode_required) + { + /* Yes, we are in FIPS mode. */ + FILE *fp; + + /* If the FIPS force files exists, is readable and has a number + != 0 on its first line, we enable the enforced fips mode. */ + fp = fopen (FIPS_FORCE_FILE, "r"); + if (fp) + { + char line[256]; + + if (fgets (line, sizeof line, fp) && atoi (line)) + enforced_fips_mode = 1; + fclose (fp); + } + + /* Now get us into the INIT state. */ + fips_new_state (STATE_INIT); + + } + return; +} + +static void +lock_fsm (void) +{ + gpg_error_t err; + + err = gpgrt_lock_lock (&fsm_lock); + if (err) + { + log_info ("FATAL: failed to acquire the FSM lock in libgrypt: %s\n", + gpg_strerror (err)); +#ifdef HAVE_SYSLOG + syslog (LOG_USER|LOG_ERR, "Libgcrypt error: " + "acquiring FSM lock failed: %s - abort", + gpg_strerror (err)); +#endif /*HAVE_SYSLOG*/ + abort (); + } +} + +static void +unlock_fsm (void) +{ + gpg_error_t err; + + err = gpgrt_lock_unlock (&fsm_lock); + if (err) + { + log_info ("FATAL: failed to release the FSM lock in libgrypt: %s\n", + gpg_strerror (err)); +#ifdef HAVE_SYSLOG + syslog (LOG_USER|LOG_ERR, "Libgcrypt error: " + "releasing FSM lock failed: %s - abort", + gpg_strerror (err)); +#endif /*HAVE_SYSLOG*/ + abort (); + } +} + + +/* This function returns true if fips mode is enabled. This is + independent of the fips required finite state machine and only used + to enable fips specific code. Please use the fips_mode macro + instead of calling this function directly. */ +int +_gcry_fips_mode (void) +{ + /* No locking is required because we have the requirement that this + variable is only initialized once with no other threads + existing. */ + return !no_fips_mode_required; +} + + +/* Return a flag telling whether we are in the enforced fips mode. */ +int +_gcry_enforced_fips_mode (void) +{ + if (!_gcry_fips_mode ()) + return 0; + return enforced_fips_mode; +} + +/* Set a flag telling whether we are in the enforced fips mode. */ +void +_gcry_set_enforced_fips_mode (void) +{ + enforced_fips_mode = 1; +} + +/* If we do not want to enforce the fips mode, we can set a flag so + that the application may check whether it is still in fips mode. + TEXT will be printed as part of a syslog message. This function + may only be be called if in fips mode. */ +void +_gcry_inactivate_fips_mode (const char *text) +{ + gcry_assert (_gcry_fips_mode ()); + + if (_gcry_enforced_fips_mode () ) + { + /* Get us into the error state. */ + fips_signal_error (text); + return; + } + + lock_fsm (); + if (!inactive_fips_mode) + { + inactive_fips_mode = 1; + unlock_fsm (); +#ifdef HAVE_SYSLOG + syslog (LOG_USER|LOG_WARNING, "Libgcrypt warning: " + "%s - FIPS mode inactivated", text); +#endif /*HAVE_SYSLOG*/ + } + else + unlock_fsm (); +} + + +/* Return the FIPS mode inactive flag. If it is true the FIPS mode is + not anymore active. */ +int +_gcry_is_fips_mode_inactive (void) +{ + int flag; + + if (!_gcry_fips_mode ()) + return 0; + lock_fsm (); + flag = inactive_fips_mode; + unlock_fsm (); + return flag; +} + + + +static const char * +state2str (enum module_states state) +{ + const char *s; + + switch (state) + { + case STATE_POWERON: s = "Power-On"; break; + case STATE_INIT: s = "Init"; break; + case STATE_SELFTEST: s = "Self-Test"; break; + case STATE_OPERATIONAL: s = "Operational"; break; + case STATE_ERROR: s = "Error"; break; + case STATE_FATALERROR: s = "Fatal-Error"; break; + case STATE_SHUTDOWN: s = "Shutdown"; break; + default: s = "?"; break; + } + return s; +} + + +/* Return true if the library is in the operational state. */ +int +_gcry_fips_is_operational (void) +{ + int result; + + lock_fsm (); + if (current_state == STATE_POWERON && !fips_mode ()) + /* If we are at this point in POWERON state it means the FIPS + module installation was not completed. (/etc/system-fips + is not present.) */ + result = 1; + else + { + if (current_state == STATE_INIT || current_state == STATE_SELFTEST) + { + /* If we are still in the INIT (or SELFTEST) state, + we need to run (or finish) the selftests so + that the FSM can eventually get into operational + state. Given that we would need a 2-phase + initialization of libgcrypt, but that has traditionally + not been enforced, we use this on demand self-test + checking. Note that Proper applications would do the + application specific libgcrypt initialization between a + gcry_check_version() and gcry_control + (GCRYCTL_INITIALIZATION_FINISHED) where the latter will + run the selftests. The drawback of these on-demand + self-tests are a small chance that self-tests are + performed by several threads; that is no problem because + our FSM make sure that we won't oversee any error. */ + unlock_fsm (); + _gcry_fips_run_selftests (0); + lock_fsm (); + } + + result = (current_state == STATE_OPERATIONAL) || !fips_mode (); + /* We always run the selftests but ignore the result + in non-FIPS mode. */ + } + unlock_fsm (); + return result; +} + + +/* This is test on whether the library is in the operational state. In + contrast to _gcry_fips_is_operational this function won't do a + state transition on the fly. */ +int +_gcry_fips_test_operational (void) +{ + int result; + + if (!fips_mode ()) + result = 1; + else + { + lock_fsm (); + result = (current_state == STATE_OPERATIONAL); + unlock_fsm (); + } + return result; +} + + +/* This is a test on whether the library is in the error or + operational state. */ +int +_gcry_fips_test_error_or_operational (void) +{ + int result; + + if (!fips_mode ()) + result = 1; + else + { + lock_fsm (); + result = (current_state == STATE_OPERATIONAL + || current_state == STATE_ERROR); + unlock_fsm (); + } + return result; +} + + +static void +reporter (const char *domain, int algo, const char *what, const char *errtxt) +{ + if (!errtxt && !_gcry_log_verbosity (2)) + return; + + log_info ("libgcrypt selftest: %s %s%s (%d): %s%s%s%s\n", + !strcmp (domain, "hmac")? "digest":domain, + !strcmp (domain, "hmac")? "HMAC-":"", + !strcmp (domain, "cipher")? _gcry_cipher_algo_name (algo) : + !strcmp (domain, "digest")? _gcry_md_algo_name (algo) : + !strcmp (domain, "hmac")? _gcry_md_algo_name (algo) : + !strcmp (domain, "pubkey")? _gcry_pk_algo_name (algo) : "", + algo, errtxt? errtxt:"Okay", + what?" (":"", what? what:"", what?")":""); +} + +/* Run self-tests for all required cipher algorithms. Return 0 on + success. */ +static int +run_cipher_selftests (int extended) +{ + static int algos[] = + { + GCRY_CIPHER_3DES, + GCRY_CIPHER_AES128, + GCRY_CIPHER_AES192, + GCRY_CIPHER_AES256, + 0 + }; + int idx; + gpg_error_t err; + int anyerr = 0; + + for (idx=0; algos[idx]; idx++) + { + err = _gcry_cipher_selftest (algos[idx], extended, reporter); + reporter ("cipher", algos[idx], NULL, + err? gpg_strerror (err):NULL); + if (err) + anyerr = 1; + } + return anyerr; +} + + +/* Run self-tests for all required hash algorithms. Return 0 on + success. */ +static int +run_digest_selftests (int extended) +{ + static int algos[] = + { + GCRY_MD_SHA1, + GCRY_MD_SHA224, + GCRY_MD_SHA256, + GCRY_MD_SHA384, + GCRY_MD_SHA512, + 0 + }; + int idx; + gpg_error_t err; + int anyerr = 0; + + for (idx=0; algos[idx]; idx++) + { + err = _gcry_md_selftest (algos[idx], extended, reporter); + reporter ("digest", algos[idx], NULL, + err? gpg_strerror (err):NULL); + if (err) + anyerr = 1; + } + return anyerr; +} + + +/* Run self-tests for all HMAC algorithms. Return 0 on success. */ +static int +run_hmac_selftests (int extended) +{ + static int algos[] = + { + GCRY_MD_SHA1, + GCRY_MD_SHA224, + GCRY_MD_SHA256, + GCRY_MD_SHA384, + GCRY_MD_SHA512, + GCRY_MD_SHA3_224, + GCRY_MD_SHA3_256, + GCRY_MD_SHA3_384, + GCRY_MD_SHA3_512, + 0 + }; + int idx; + gpg_error_t err; + int anyerr = 0; + + for (idx=0; algos[idx]; idx++) + { + err = _gcry_hmac_selftest (algos[idx], extended, reporter); + reporter ("hmac", algos[idx], NULL, + err? gpg_strerror (err):NULL); + if (err) + anyerr = 1; + } + return anyerr; +} + + +/* Run self-tests for all required public key algorithms. Return 0 on + success. */ +static int +run_pubkey_selftests (int extended) +{ + static int algos[] = + { + GCRY_PK_RSA, + GCRY_PK_DSA, + GCRY_PK_ECC, + 0 + }; + int idx; + gpg_error_t err; + int anyerr = 0; + + for (idx=0; algos[idx]; idx++) + { + err = _gcry_pk_selftest (algos[idx], extended, reporter); + reporter ("pubkey", algos[idx], NULL, + err? gpg_strerror (err):NULL); + if (err) + anyerr = 1; + } + return anyerr; +} + + +/* Run self-tests for the random number generator. Returns 0 on + success. */ +static int +run_random_selftests (void) +{ + gpg_error_t err; + + err = _gcry_random_selftest (reporter); + reporter ("random", 0, NULL, err? gpg_strerror (err):NULL); + + return !!err; +} + +#ifdef ENABLE_HMAC_BINARY_CHECK +static int +get_library_path(const char *libname, const char *symbolname, char *path, size_t pathlen) +{ + Dl_info info; + void *dl, *sym; + int rv = -1; + + dl = dlopen(libname, RTLD_LAZY); + if (dl == NULL) { + return -1; + } + + sym = dlsym(dl, symbolname); + + if (sym != NULL && dladdr(sym, &info)) { + strncpy(path, info.dli_fname, pathlen-1); + path[pathlen-1] = '\0'; + rv = 0; + } + + dlclose(dl); + + return rv; +} +#endif + +/* Run an integrity check on the binary. Returns 0 on success. */ +static int +check_binary_integrity (void) +{ +#ifdef ENABLE_HMAC_BINARY_CHECK + gpg_error_t err; + char libpath[4096]; + unsigned char digest[32]; + int dlen; + char *fname = NULL; + const char key[] = "orboDeJITITejsirpADONivirpUkvarP"; + + if (get_library_path ("libgcrypt.so.20", "gcry_check_version", libpath, sizeof(libpath))) + err = gpg_error_from_syserror (); + else + { + dlen = _gcry_hmac256_file (digest, sizeof digest, libpath, + key, strlen (key)); + if (dlen < 0) + err = gpg_error_from_syserror (); + else if (dlen != 32) + err = gpg_error (GPG_ERR_INTERNAL); + else + { + fname = xtrymalloc (strlen (libpath) + 1 + 5 + 1 ); + if (!fname) + err = gpg_error_from_syserror (); + else + { + FILE *fp; + char *p; + + /* Prefix the basename with a dot. */ + strcpy (fname, libpath); + p = strrchr (fname, '/'); + if (p) + p++; + else + p = fname; + memmove (p+1, p, strlen (p)+1); + *p = '.'; + strcat (fname, ".hmac"); + + /* Open the file. */ + fp = fopen (fname, "r"); + if (!fp) + err = gpg_error_from_syserror (); + else + { + /* A buffer of 64 bytes plus one for a LF and one to + detect garbage. */ + unsigned char buffer[64+1+1]; + const unsigned char *s; + int n; + + /* The HMAC files consists of lowercase hex digits + with an optional trailing linefeed or optional + with two trailing spaces. The latter format + allows the use of the usual sha1sum format. Fail + if there is any garbage. */ + err = gpg_error (GPG_ERR_SELFTEST_FAILED); + n = fread (buffer, 1, sizeof buffer, fp); + if (n == 64 + || (n == 65 && buffer[64] == '\n') + || (n == 66 && buffer[64] == ' ' && buffer[65] == ' ')) + { + buffer[64] = 0; + for (n=0, s= buffer; + n < 32 && loxdigit_p (s) && loxdigit_p (s+1); + n++, s += 2) + buffer[n] = loxtoi_2 (s); + if ( n == 32 && !memcmp (digest, buffer, 32) ) + err = 0; + } + fclose (fp); + } + } + } + } + reporter ("binary", 0, fname, err? gpg_strerror (err):NULL); +#ifdef HAVE_SYSLOG + if (err) + syslog (LOG_USER|LOG_ERR, "Libgcrypt error: " + "integrity check using `%s' failed: %s", + fname? fname:"[?]", gpg_strerror (err)); +#endif /*HAVE_SYSLOG*/ + xfree (fname); + return !!err; +#else + return 0; +#endif +} + + +/* Run the self-tests. If EXTENDED is true, extended versions of the + selftest are run, that is more tests than required by FIPS. */ +gpg_err_code_t +_gcry_fips_run_selftests (int extended) +{ + enum module_states result = STATE_ERROR; + gcry_err_code_t ec = GPG_ERR_SELFTEST_FAILED; + int in_poweron; + + lock_fsm (); + in_poweron = (current_state == STATE_POWERON); + unlock_fsm (); + + fips_new_state (STATE_SELFTEST); + + /* We first check the integrity of the binary. + If run from the constructor we are in POWERON state, + we return and finish the remaining selftests before + real use of the library. It will be in the POWERON + state meanwhile. */ + if (in_poweron) + if (check_binary_integrity ()) + goto leave; + + if (in_poweron) + return 0; + + if (run_cipher_selftests (extended)) + goto leave; + + if (run_digest_selftests (extended)) + goto leave; + + if (run_hmac_selftests (extended)) + goto leave; + + /* Run random tests before the pubkey tests because the latter + require random. */ + if (run_random_selftests ()) + goto leave; + + if (run_pubkey_selftests (extended)) + goto leave; + + /* All selftests passed. */ + result = STATE_OPERATIONAL; + ec = 0; + + leave: + fips_new_state (result); + + return ec; +} + + +/* This function is used to tell the FSM about errors in the library. + The FSM will be put into an error state. This function should not + be called directly but by one of the macros + + fips_signal_error (description) + fips_signal_fatal_error (description) + + where DESCRIPTION is a string describing the error. */ +void +_gcry_fips_signal_error (const char *srcfile, int srcline, const char *srcfunc, + int is_fatal, const char *description) +{ + if (!fips_mode ()) + return; /* Not required. */ + + /* Set new state before printing an error. */ + fips_new_state (is_fatal? STATE_FATALERROR : STATE_ERROR); + + /* Print error. */ + log_info ("%serror in libgcrypt, file %s, line %d%s%s: %s\n", + is_fatal? "fatal ":"", + srcfile, srcline, + srcfunc? ", function ":"", srcfunc? srcfunc:"", + description? description : "no description available"); +#ifdef HAVE_SYSLOG + syslog (LOG_USER|LOG_ERR, "Libgcrypt error: " + "%serror in file %s, line %d%s%s: %s", + is_fatal? "fatal ":"", + srcfile, srcline, + srcfunc? ", function ":"", srcfunc? srcfunc:"", + description? description : "no description available"); +#endif /*HAVE_SYSLOG*/ +} + + +/* Perform a state transition to NEW_STATE. If this is an invalid + transition, the module will go into a fatal error state. */ +static void +fips_new_state (enum module_states new_state) +{ + int ok = 0; + enum module_states last_state; + + lock_fsm (); + + last_state = current_state; + switch (current_state) + { + case STATE_POWERON: + if (new_state == STATE_INIT + || new_state == STATE_SELFTEST + || new_state == STATE_ERROR + || new_state == STATE_FATALERROR) + ok = 1; + break; + + case STATE_INIT: + if (new_state == STATE_SELFTEST + || new_state == STATE_ERROR + || new_state == STATE_FATALERROR) + ok = 1; + break; + + case STATE_SELFTEST: + if (new_state == STATE_OPERATIONAL + || new_state == STATE_INIT + || new_state == STATE_SELFTEST + || new_state == STATE_ERROR + || new_state == STATE_FATALERROR) + ok = 1; + break; + + case STATE_OPERATIONAL: + if (new_state == STATE_SHUTDOWN + || new_state == STATE_SELFTEST + || new_state == STATE_ERROR + || new_state == STATE_FATALERROR) + ok = 1; + break; + + case STATE_ERROR: + if (new_state == STATE_SHUTDOWN + || new_state == STATE_ERROR + || new_state == STATE_FATALERROR + || new_state == STATE_SELFTEST) + ok = 1; + break; + + case STATE_FATALERROR: + if (new_state == STATE_SHUTDOWN ) + ok = 1; + break; + + case STATE_SHUTDOWN: + /* We won't see any transition *from* Shutdown because the only + allowed new state is Power-Off and that one can't be + represented. */ + break; + + } + + if (ok) + { + current_state = new_state; + } + + unlock_fsm (); + + if (!ok || _gcry_log_verbosity (2)) + log_info ("libgcrypt state transition %s => %s %s\n", + state2str (last_state), state2str (new_state), + ok? "granted":"denied"); + + if (!ok) + { + /* Invalid state transition. Halting library. */ +#ifdef HAVE_SYSLOG + syslog (LOG_USER|LOG_ERR, + "Libgcrypt error: invalid state transition %s => %s", + state2str (last_state), state2str (new_state)); +#endif /*HAVE_SYSLOG*/ + fips_noreturn (); + } + else if (new_state == STATE_ERROR || new_state == STATE_FATALERROR) + { +#ifdef HAVE_SYSLOG + syslog (LOG_USER|LOG_WARNING, + "Libgcrypt notice: state transition %s => %s", + state2str (last_state), state2str (new_state)); +#endif /*HAVE_SYSLOG*/ + } +} + + + + +/* This function should be called to ensure that the execution shall + not continue. */ +void +_gcry_fips_noreturn (void) +{ +#ifdef HAVE_SYSLOG + syslog (LOG_USER|LOG_ERR, "Libgcrypt terminated the application"); +#endif /*HAVE_SYSLOG*/ + fflush (NULL); + abort (); + /*NOTREACHED*/ +}