#include #include #include "fips.h" #include "../utils.h" #include "../error.h" /* * SSL Format * RSA PUBLIC KEY -> PKCS#1 format * PUBLIC KEY -> PEM Format (SPKI) */ static int DEBUG = 0; int fips(const char *pkey, struct audit_fips *st_audit_fips, struct keyinfo *st_keyinfo, const int type, const int is_pubkey){ int res; if(type == TYPE_RSA){ if (is_pubkey == 1) res = fips_pubkey_rsa(st_audit_fips, st_keyinfo, pkey); else res = fips_privkey_rsa(st_audit_fips, st_keyinfo, pkey); } else if (type == TYPE_ELLIPTIC){ if (is_pubkey){ EC_KEY *ec = fips_load_pubkey_ecc(pkey); if (!ec) return FIPS_ERR_LOAD_ECC_PUBKEY; res = fips_pubkey_ecc(ec, st_audit_fips, st_keyinfo, pkey); } } else if (type == TYPE_X509) res = fips_x509(st_audit_fips, st_keyinfo, pkey); return res; } /********************************************************/ /* RSA part */ /********************************************************/ /* * This function load public RSA key and make an audit on it */ static int fips_pubkey_rsa(struct audit_fips *st_audit_fips, struct keyinfo *st_keyinfo, const char *pkey) { int res; /* * Now, we check if the public key is compliant with FIPS * The program check the length of the key, the exponent */ RSA *rsa = NULL; res = loadkeys_rsa(&rsa, pkey, &st_keyinfo->st_rsa.format); if (res > 0 || !rsa){ printf("Failed to read the public key\n"); return res; } /* We have loaded our RSA key, we can audit it */ audit_rsa_keys(rsa, st_audit_fips, st_keyinfo, pkey); // Cleaning RSA_free(rsa); return 0; } /* * This function audit RSA private key */ static int fips_privkey_rsa(struct audit_fips *st_audit_fips, struct keyinfo *st_keyinfo, const char *pkey) { int res; RSA *rsa = NULL; res = load_priv_rsa_keys(&rsa, pkey); if (res > 0 || !rsa){ printf("Failed to read the private key\n"); return res; } audit_rsa_keys(rsa, st_audit_fips, st_keyinfo, pkey); // Clean RSA_free(rsa); return 0; } /* * This function load the RSA key and store to the RSA * object * Detect and specify the correct RSA format public key */ static int loadkeys_rsa(RSA **rsa, const char *pkey, int *format){ BIO *bio = BIO_new(BIO_s_file()); if (BIO_read_filename(bio, pkey) == 0){ printf("Failed to read BIO\n"); return FIPS_ERR_READ_BIO; } /* * Works with PEM_read_RSAPublicKey, but when we try to read the file * we cannot. This function "block" the access to the file */ /*rsa = PEM_read_RSAPublicKey(f, NULL, NULL, NULL); */ #if OPENSSL_VERSION_NUMBER > 0x03000000f EVP_PKEY *evp = PEM_read_bio_PUBKEY_ex(bio, NULL, NULL, NULL, NULL, NULL); if (!evp){ BIO_free(bio); return FIPS_ERR_LOAD_KEY; } BIO_free(bio); //printf("Keysize: %d\n", EVP_PKEY_bits(evp)); /* EVP_PKEY_get1_RSA is deprecated, need to find another way to get the RSA key */ *rsa = EVP_PKEY_get1_RSA(evp); if (!(*rsa)){ EVP_PKEY_free(evp); return FIPS_ERR_LOAD_RSA_KEY; } EVP_PKEY_free(evp); *format = 0; #else // Deprecated in OpenSSL v3 /* * RSAPublicKey read publickey at the PEM format * RSA_PUBKEY read publickey at the PKCS1 format */ /* In case it's OpenSSL v1, we get the RSA * object from BIO */ *rsa = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL); // If we cannot read it, we try with the PKCS1 format if (!(*rsa)){ // print_error(); /* * We need to reset or reseek the BIO, otherwise, we cannot read it * https://docs.openssl.org/3.0/man3/BIO_ctrl/#synopsis */ //BIO_reset((*rsa)->bio); /* Works too */ BIO_seek(bio, 0); *(rsa) = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL,NULL); if (!(*rsa)){ if (DEBUG) printf("Cannot read the SPKI format of the public key\n"); BIO_free(bio); return FIPS_ERR_LOAD_KEY; } *format = RSA_FORMAT_SPKI; } else *format = RSA_FORMAT_PKCS1; BIO_free(bio); #endif return 0; } /* * This function load RSA Private key */ static int load_priv_rsa_keys(RSA **rsa, const char *pkey){ BIO *bio = BIO_new(BIO_s_file()); if (!bio){ if (DEBUG) printf("Failed to create new BIO\n"); return FIPS_ERR_NEW_BIO; } if(BIO_read_filename(bio, pkey) == 0){ printf("Failed to read BIO\n"); BIO_free(bio); return FIPS_ERR_READ_BIO; } #if OPENSSL_VERSION_NUMBER > 0x03000000f EVP_PKEY *evp = PEM_read_bio_PrivateKey_ex(bio, NULL, NULL, NULL, NULL, NULL); if (!evp){ if (DEBUG) printf("Failed to read BIO PrivateKey\n"); BIO_free(bio); return FIPS_ERR_READ_BIO; } BIO_free(bio); *rsa = EVP_PKEY_get1_RSA(evp); if (!*rsa){ EVP_PKEY_free(evp); return FIPS_ERR_LOAD_RSA_KEY; } EVP_PKEY_free(evp); #else /* For OpenSSL v1 */ *rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL); if (!*rsa){ if (DEBUG) printf("Failed to read BIO RSAPrivateKey\n"); BIO_free(bio); return FIPS_ERR_LOAD_RSA_PRIV_KEY; } BIO_free(bio); #endif return 0; } /* * This function audit the RSA keys, both public and private * For the audit, the function check the exponent (modulus) and the key size */ static void audit_rsa_keys(RSA *rsa, struct audit_fips *st_audit_fips, struct keyinfo *st_keyinfo, const char *pkey){ int res; st_keyinfo->st_rsa.keysize = RSA_size(rsa); st_keyinfo->algo = ALGO_RSA; // The return value is a const, shouldn't be freed const BIGNUM *e = RSA_get0_e(rsa); char *exponent = BN_bn2dec(e); //free(exponent); OPENSSL_free(exponent); /* Exponent has been set up, we can check it */ res = check_exponent(e, st_audit_fips->audit_rsa.audit_exponent.result, &st_keyinfo->st_rsa.exponent); /* * Audit the key size. For a better security, the key size is at least 2048 bits */ if (st_keyinfo->st_rsa.keysize * 8 < 2048){ sprintf(st_audit_fips->audit_rsa.audit_keysize.result, "The key size is lower than 2048. The key should be at least 2048 bits."); st_audit_fips->audit_rsa.audit_keysize.audit = FALSE; } else{ sprintf(st_audit_fips->audit_rsa.audit_keysize.result, "The key size is upper or equal than 2048. The audit is passed with success."); st_audit_fips->audit_rsa.audit_keysize.audit = TRUE; } } /* * In this function, we are going to check the exponent * For testing if the exponent is odd or even, we apply a modulo 2 on the exponent * If the result is 1, means the key has a remainder and the key is odd, if the result is 0, the exponent is even. * Regarding to the FIPS 186-5, the exponent must be odd. * The function check also the size of the exponent. * When the key has been generated with OpenSSL, by default the exponent is 65537. * The exponent e size must be 2 ** 16 < e < 2 ** 256 */ static int check_exponent(const BIGNUM *e, char *buf, unsigned long *exponent){ BIGNUM *rem = BN_new(), *a = BN_new(), *m = BN_new(); char nExponent[4]; BN_CTX *ctx; char *r; int error = 0; sprintf(nExponent, "%d", 2); BN_dec2bn(&m, nExponent); ctx = BN_CTX_new(); BN_div(NULL, rem, e, m, ctx); r = BN_bn2dec(rem); /* * According to the FIPS 186-5, the exponent size must be 2 ** 16 < e < 2 ** 256 * The exponent must be odd too */ char *exp = BN_bn2dec(e); *exponent = (char2dec(exp[0]) * 10000) + (char2dec(exp[1]) * 1000) + (char2dec(exp[2])* 100) + (char2dec(exp[3]) * 10) + (char2dec(exp[4])); if (strcmp(r, "0") == 0){ strncpy(buf, "The exponent is even, should be odd", BUF_SIZE_AUDIT); error += 1; } // Check the exponent size double minSize = pow(2, 16); double maxSize = pow(2, 256); if (*exponent < minSize || *exponent > maxSize){ strncpy(buf, "The exponent size is not correct. The minimum size is 2 ** 16 and maximum size 2 ** 256.", BUF_SIZE_AUDIT); error += 1; } // If no error if (error == 0) strncpy(buf, "The exponent is correct, the FIPS compliance is respected.", BUF_SIZE_AUDIT); // Cleaning free(r); BN_free(rem); BN_free(a); BN_free(m); BN_CTX_free(ctx); OPENSSL_free(exp); return 0; } /********************************************************/ /* ECC part */ /********************************************************/ static int fips_pubkey_ecc(EC_KEY *ec, struct audit_fips *st_audit_fips, struct keyinfo *st_keyinfo, const char *pkey){ st_keyinfo->algo = ALGO_EC; memset(&st_keyinfo->s_ecc, 0, sizeof(struct ecc*)); st_keyinfo->s_ecc.ec = ec; int res = get_domain_parameters(&st_keyinfo->s_ecc); if (res != 0) return res; audit_ecc(st_audit_fips, st_keyinfo->s_ecc.nid); return 0; } /* * This function audit the ECC keys. * According to the RCC 7748 and NIST recommendation, * curve schemes: P-521, Curve25519 or Curve448 should be used * The key length recommended is at least 256 bit. */ static void audit_ecc(struct audit_fips *st_audit, const int nid){ /* * Recommended curve name (See file /usr/include/openssl/obj_mac): * NID 716 = secp521r1 * NID 1034 = X25519 (Curve25519) * NID 1035 = X448 (Curve448) */ if (nid != 716 && nid != 1034 && nid != 1035){ sprintf(st_audit->audit_ecc.audit_curve.result, "The curve scheme should be P-521, Curve25519 or Curve448."); st_audit->audit_ecc.audit_curve.audit = FALSE; } else{ sprintf(st_audit->audit_ecc.audit_curve.result, "The curve scheme is enough strong and respect NIST recommendation."); st_audit->audit_ecc.audit_curve.audit = TRUE; } } /* * This function load the public ECC key and return the key store in the variable EVP_PKEY */ static EC_KEY *fips_load_pubkey_ecc(const char *pkey){ BIO *bio = BIO_new(BIO_s_file()); if (BIO_read_filename(bio, pkey) == 0){ printf("Failed to read BIO\n"); return NULL; } EC_KEY *ec = PEM_read_bio_EC_PUBKEY(bio, NULL, NULL, NULL); if (!ec){ if (DEBUG) printf("Cannot read the ECC Public key\n"); BIO_free(bio); return NULL; } /* We don't use it anymore, we freeing it */ BIO_free(bio); return ec; } /* * This function get domain parameters from the ECC key */ static int get_domain_parameters(struct ecc *st_ecc){ EC_GROUP *group = EC_KEY_get0_group(st_ecc->ec); if(!group){ if (DEBUG) printf("Failed to load ECC Group\n"); EC_KEY_free(st_ecc->ec); return FIPS_ERR_GET_ECC_GROUP; } // Get cofactor BIGNUM *b_cofactor = EC_GROUP_get0_cofactor(group); if(!b_cofactor){ printf("Cannot get cofactor\n"); return FIPS_ERR_GET_ECC_DOMAPARAM; } st_ecc->cofactor = BN_bn2dec(b_cofactor); //BN_free(b_cofactor); // Get field #if OPENSSL_VERSION_NUMBER > 0x03000000f #endif // Get order bit st_ecc->order_bits = EC_GROUP_order_bits(group); // Get order BIGNUM *b_order = EC_GROUP_get0_order(group); if(!b_order){ printf("Cannot get order\n"); return FIPS_ERR_GET_ECC_DOMAPARAM; } st_ecc->order = BN_bn2hex(b_order); //OPENSSL_free(order); //BN_free(b_order); // Get curve name st_ecc->nid = EC_GROUP_get_curve_name(group); st_ecc->curve = OBJ_nid2sn(st_ecc->nid); //OPENSSL_free(name); /* If I free, the program crash, because it's a const ?? */ // Get generator EC_POINT *g = EC_GROUP_get0_generator(group); if (!g){ if (DEBUG) printf("Failed to get ECC generator\n"); return FIPS_ERR_GET_ECC_GENERATOR; } st_ecc->g = EC_POINT_point2hex(group, g, POINT_CONVERSION_UNCOMPRESSED, NULL); /* * It's cannot mandatory to clean EC_GROUP and EC_POINT and other objects * We freeing them in certificate.c file. * Also, for cleaning EC_GROUP and EC_POINT, just freeing EC_KEY is enough */ return 0; } /********************************************************/ /* X.509 part */ /********************************************************/ /* * This function load X509 certificate */ static int fips_x509(struct audit_fips *st_audit_fips, struct keyinfo *st_keyinfo, const char *pkey){ BIO *bio = BIO_new(BIO_s_file()); if (!bio){ printf("Failed to create new BIO\n"); return FIPS_ERR_NEW_BIO; } if (BIO_read_filename(bio, pkey) == 0){ printf("Failed to read BIO\n"); return FIPS_ERR_READ_BIO; } X509 *x = PEM_read_bio_X509(bio, NULL, 0, NULL); if (!x){ printf("Failed to read the X509 certificate\n"); BIO_free(bio); return FIPS_ERR_LOAD_X509; } BIO_free(bio); /* We don't need it anymore, we freeing it */ EVP_PKEY *evp = X509_get_pubkey(x); if (!evp){ printf("Failed to get public certificate\n"); X509_free(x); return FIPS_ERR_LOAD_RSA_KEY; } /* Get certificate info, such as issuer, validity, etc. */ X509_free(x); /* Key type identification */ int type = EVP_PKEY_base_id(evp); switch (type) { case EVP_PKEY_RSA: ; RSA *rsa = EVP_PKEY_get1_RSA(evp); if (!rsa) return FIPS_ERR_LOAD_RSA_KEY; // We have the RSA key, we can audit it audit_rsa_keys(rsa, st_audit_fips, st_keyinfo, pkey); RSA_free(rsa); break; case EVP_PKEY_EC: ; /* We free EC_KEY in certificate.c file */ EC_KEY *ec = EVP_PKEY_get1_EC_KEY(evp); if (!ec) return FIPS_ERR_LOAD_ECC_PUBKEY; int res = fips_pubkey_ecc(ec, st_audit_fips, st_keyinfo, pkey); break; default: break; } EVP_PKEY_free(evp); return 0; } /* * Return 1 if the version is upper than 1 and less than 3 * Return 3 for the version v3 */ static int openssl_version(){ unsigned long version = OPENSSL_VERSION_NUMBER; if (DEBUG) printf("OpenSSL Version: %lx\n", version); if (version <= 0x03000000f) return 1; if (version >= 0x03000000f) return 3; } /* * In case we have an error with OpenSSL librairy, we can print the error message */ static void print_error(){ unsigned long err = ERR_get_error(); char b[256]; ERR_error_string(err, b); printf("%s\n", b); }