/* * zkey - Generate, re-encipher, and validate secure keys * * Copyright IBM Corp. 2018, 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_panic.h" #include "pkey.h" #include "pvsecrets.h" #include "utils.h" #ifndef AF_ALG #define AF_ALG 38 #endif #ifndef SOL_ALG #define SOL_ALG 279 #endif #define pr_verbose(verbose, fmt...) do { \ if (verbose) \ warnx(fmt); \ } while (0) #define DOUBLE_KEYSIZE_FOR_XTS(keysize, xts) ((xts) ? 2 * (keysize) : (keysize)) #define HALF_KEYSIZE_FOR_XTS(keysize, xts) ((xts) ? (keysize) / 2 : (keysize)) #define MAX_CIPHER_LEN 32 #define INITIAL_APQN_ENTRIES 16 /** * Opens the pkey device and returns its file descriptor. * * @param verbose if true, verbose messages are printed * * @returns the file descriptor or -1 to indicate an error */ int open_pkey_device(bool verbose) { int pkey_fd; pkey_fd = open(PKEYDEVICE, O_RDWR); if (pkey_fd < 0) { warnx("File '%s:' %s\nEnsure that the 'pkey' kernel module " "is loaded", PKEYDEVICE, strerror(errno)); return -1; } pr_verbose(verbose, "Device '%s' has been opened successfully", PKEYDEVICE); return pkey_fd; } /** * Read a secure key file and return the allocated buffer and size. * * @param[in] keyfile the name of the file to read * @param[out] secure_key_size on return, the size of the secure key read * @param[in] verbose if true, verbose messages are printed * * @return a buffer containing the secure key, or NULL in case of an error. * The returned buffer must be freed by the caller. */ u8 *read_secure_key(const char *keyfile, size_t *secure_key_size, bool verbose) { size_t count, size; struct stat sb; char *msg; FILE *fp; u8 *buf; util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); util_assert(secure_key_size != NULL, "Internal error: secure_key_size is NULL"); if (stat(keyfile, &sb)) { warnx("File '%s': %s", keyfile, strerror(errno)); return NULL; } size = sb.st_size; if (size < MIN_SECURE_KEY_SIZE || size > 2 * MAX_SECURE_KEY_SIZE) { warnx("File '%s' has an invalid size: %lu", keyfile, size); return NULL; } fp = fopen(keyfile, "r"); if (fp == NULL) { warnx("File '%s': %s", keyfile, strerror(errno)); return NULL; } buf = util_malloc(size); count = fread(buf, 1, size, fp); if (count != size) { msg = ferror(fp) ? strerror(errno) : "File is too small"; warnx("File '%s': %s", keyfile, msg); free(buf); buf = NULL; goto out; } *secure_key_size = size; if (verbose) { pr_verbose(verbose, "%lu bytes read from file '%s'", size, keyfile); util_hexdump_grp(stderr, NULL, buf, 4, size, 0); } out: fclose(fp); return buf; } /** * Write a secure key file * * @param[in] keyfile the name of the file to write * @param[in] secure_key a buffer containing the secure key * @param[in] secure_key_size the size of the secure key * @param[in] verbose if true, verbose messages are printed * * @returns 0 in case of success, -EIO in case of an error */ int write_secure_key(const char *keyfile, const u8 *secure_key, size_t secure_key_size, bool verbose) { size_t count; FILE *fp; util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); util_assert(secure_key_size > 0, "Internal error: secure_key_size is zero"); fp = fopen(keyfile, "w"); if (fp == NULL) { warnx("File '%s': %s", keyfile, strerror(errno)); return -EIO; } count = fwrite(secure_key, 1, secure_key_size, fp); if (count != secure_key_size) { warnx("File '%s': %s", keyfile, strerror(errno)); fclose(fp); return -EIO; } if (verbose) { pr_verbose(verbose, "%lu bytes written to file '%s'", secure_key_size, keyfile); util_hexdump_grp(stderr, NULL, secure_key, 4, secure_key_size, 0); } fclose(fp); return 0; } /** * Read a clear key file and return the allocated buffer and size * * @param[in] keyfile the name of the file to read * @param[in] keybits the clear key size in bits. When keybits is 0, then * the file size determines the keybits. * @param[in] xts if true an XTS key is to be read * @param[out] clear_key_size on return, the size of the clear key read * @param[in] verbose if true, verbose messages are printed * * @return a buffer containing the clear key, or NULL in case of an error. * The returned buffer must be freed by the caller. */ static u8 *read_clear_key(const char *keyfile, size_t keybits, bool xts, size_t *clear_key_size, bool verbose) { size_t count, size, expected_size; struct stat sb; char *msg; FILE *fp; u8 *buf; util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); util_assert(clear_key_size != NULL, "Internal error: clear_key_size is NULL"); if (stat(keyfile, &sb)) { warnx("File '%s': %s", keyfile, strerror(errno)); return NULL; } size = sb.st_size; if (keybits != 0) { expected_size = DOUBLE_KEYSIZE_FOR_XTS(keybits / 8, xts); if (size != expected_size) { warnx("File '%s' has an invalid size, " "%lu bytes expected", keyfile, expected_size); return NULL; } } else { keybits = HALF_KEYSIZE_FOR_XTS(size * 8, xts); } switch (keybits) { case 128: break; case 192: if (xts) { warnx("File '%s' has an invalid size, " "192 bit keys are not supported with XTS", keyfile); return NULL; } break; case 256: break; default: if (xts) warnx("File '%s' has an invalid size, " "32 or 64 bytes expected", keyfile); else warnx("File '%s' has an invalid size, 16, 24 " "or 32 bytes expected", keyfile); return NULL; } fp = fopen(keyfile, "r"); if (fp == NULL) { warnx("File '%s': %s", keyfile, strerror(errno)); return NULL; } buf = util_malloc(size); count = fread(buf, 1, size, fp); if (count != size) { msg = ferror(fp) ? strerror(errno) : "File is too small"; warnx("File '%s': %s", keyfile, msg); free(buf); buf = NULL; goto out; } *clear_key_size = size; if (verbose) { pr_verbose(verbose, "%lu bytes read from file '%s'", size, keyfile); util_hexdump_grp(stderr, NULL, buf, 4, size, 0); } out: fclose(fp); return buf; } /** * Returns the PKEY_KEYTYPE_xxx value for the specified key size. * * @param[in] keysize the key size in bits * * @returns the PKEY_KEYTYPE_xxx value or 0 for an unknown key size */ static u32 keysize_to_keytype(enum pkey_key_size keysize) { switch (keysize) { case PKEY_SIZE_AES_128: return PKEY_KEYTYPE_AES_128; case PKEY_SIZE_AES_192: return PKEY_KEYTYPE_AES_192; case PKEY_SIZE_AES_256: return PKEY_KEYTYPE_AES_256; default: return 0; } } /** * Returns the PKEY_SIZE_xxx value for the specified keybits. * * @param[in] keybits the key size in bits * * @returns thePKEY_SIZE_xxx value or 0 for an unknown key size */ static enum pkey_key_size keybits_to_keysize(u32 keybits) { switch (keybits) { case 128: return PKEY_SIZE_AES_128; case 192: return PKEY_SIZE_AES_192; case 256: return PKEY_SIZE_AES_256; default: return PKEY_SIZE_UNKNOWN; } } static u32 flags_for_pkey_type(enum pkey_key_type type, bool exportable, bool wrap_with_trusted) { u32 flags = 0; if (!exportable) return 0; switch (type) { case PKEY_TYPE_CCA_CIPHER: flags = PKEY_KEYGEN_XPRT_SYM | PKEY_KEYGEN_XPRT_UASY | PKEY_KEYGEN_XPRT_AASY | PKEY_KEYGEN_XPRT_RAW | PKEY_KEYGEN_XPRT_CPAC | PKEY_KEYGEN_XPRT_DES | PKEY_KEYGEN_XPRT_AES | PKEY_KEYGEN_XPRT_RSA; break; case PKEY_TYPE_EP11: case PKEY_TYPE_EP11_AES: flags = XCP_BLOB_EXTRACTABLE | XCP_BLOB_DECRYPT | XCP_BLOB_ENCRYPT | XCP_BLOB_PROTKEY_EXTRACTABLE; if (wrap_with_trusted) flags |= XCP_BLOB_WRAP_W_TRUSTED; break; default: break; } return flags; } /* * Wrapper for the PKEY_GENSECK/PKEY_GENSECK2 IOCTL to generate a secure * key of any type by random. If the newer PKEY_GENSECK2 IOCTL is not supported * by the pkey device, then it falls back to the older PKEY_GENSECK IOCTL * * @param[in] pkey_fd the pkey file descriptor * @param[in/out] genseck info about key to generate * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ static int pkey_genseck2(int pkey_fd, struct pkey_genseck2 *genseck2, bool verbose) { struct pkey_genseck genseck; int rc; u32 i; util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); util_assert(genseck2 != NULL, "Internal error: genseck2 is NULL"); rc = ioctl(pkey_fd, PKEY_GENSECK2, genseck2); if (rc != 0 && errno != ENOTTY) return -errno; if (rc == 0) return 0; /* New IOCTL is not available, fall back to old one */ pr_verbose(verbose, "ioctl PKEY_GENSECK2 not supported, fall back to " "PKEY_GENSECK"); if (genseck2->type != PKEY_TYPE_CCA_DATA) { warnx("Key-type is not supported"); return -ENOTSUP; } if (genseck2->keylen < AESDATA_KEY_SIZE) return -EINVAL; memset(&genseck, 0, sizeof(genseck)); genseck.keytype = keysize_to_keytype(genseck2->size); if (genseck.keytype == 0) return -EINVAL; for (i = 0; i < genseck2->apqn_entries; i++) { genseck.cardnr = genseck2->apqns[i].card; genseck.domain = genseck2->apqns[i].domain; rc = ioctl(pkey_fd, PKEY_GENSECK, &genseck); if (rc != 0) continue; memcpy(genseck2->key, &genseck.seckey.seckey, AESDATA_KEY_SIZE); genseck2->keylen = AESDATA_KEY_SIZE; return 0; } return -errno; } /* * Wrapper for the PKEY_CLR2SECK/PKEY_CLR2SECK2 IOCTL to generate a secure * key of any type from a clear key. If the newer PKEY_CLR2SECK2 IOCTL is not * supported by the pkey device, then it falls back to the older PKEY_CLR2SECK * IOCTL * * @param[in] pkey_fd the pkey file descriptor * @param[in/out] clr2seck2 info about key to generate * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ static int pkey_clr2seck2(int pkey_fd, struct pkey_clr2seck2 *clr2seck2, bool verbose) { struct pkey_clr2seck clr2seck; int rc; u32 i; util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); util_assert(clr2seck2 != NULL, "Internal error: clr2seck2 is NULL"); rc = ioctl(pkey_fd, PKEY_CLR2SECK2, clr2seck2); if (rc != 0 && errno != ENOTTY) return -errno; if (rc == 0) return 0; /* New IOCTL is not available, fall back to old one */ pr_verbose(verbose, "ioctl PKEY_CLR2SECK2 not supported, fall back to " "PKEY_CLR2SECK"); if (clr2seck2->type != PKEY_TYPE_CCA_DATA) { warnx("Key-type is not supported"); return -ENOTSUP; } if (clr2seck2->keylen < AESDATA_KEY_SIZE) return -EINVAL; memset(&clr2seck, 0, sizeof(clr2seck)); clr2seck.clrkey = clr2seck2->clrkey; clr2seck.keytype = keysize_to_keytype(clr2seck2->size); if (clr2seck.keytype == 0) return -EINVAL; for (i = 0; i < clr2seck2->apqn_entries; i++) { clr2seck.cardnr = clr2seck2->apqns[i].card; clr2seck.domain = clr2seck2->apqns[i].domain; rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2seck); if (rc != 0) continue; memcpy(clr2seck2->key, &clr2seck.seckey.seckey, AESDATA_KEY_SIZE); clr2seck2->keylen = AESDATA_KEY_SIZE; return 0; } return -errno; } /* * Wrapper for the PKEY_VERIFYKEY/PKEY_VERIFYKEY2 IOCTL to verify a secure * key of any type. If the newer PKEY_VERIFYKEY2 IOCTL is not supported * by the pkey device, then it falls back to the older PKEY_VERIFYKEY IOCTL * * @param[in] pkey_fd the pkey file descriptor * @param[in/out] verifykey2 info about key to verify * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ static int pkey_verifyseck2(int pkey_fd, struct pkey_verifykey2 *verifykey2, bool verbose) { struct pkey_verifykey verifykey; int rc; util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); util_assert(verifykey2 != NULL, "Internal error: verifyseck2 is NULL"); rc = ioctl(pkey_fd, PKEY_VERIFYKEY2, verifykey2); if (rc != 0 && errno != ENOTTY) return -errno; if (rc == 0) return 0; /* New IOCTL is not available, fall back to old one */ pr_verbose(verbose, "ioctl PKEY_VERIFYKEY2 not supported, fall back to " "PKEY_VERIFYKEY"); if (!is_cca_aes_data_key(verifykey2->key, verifykey2->keylen)) return -ENODEV; memset(&verifykey, 0, sizeof(verifykey)); memcpy(&verifykey.seckey, verifykey2->key, sizeof(verifykey.seckey)); /* * Note: the old IOCTL does not support to check a specific card and * domain. If falling back to the old IOCTL, this input is silently * ignored, and all APQNs currently available in the system are used. */ rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey); if (rc != 0) return -errno; if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) return -ENODEV; verifykey2->type = PKEY_TYPE_CCA_DATA; verifykey2->cardnr = verifykey.cardnr; verifykey2->domain = verifykey.domain; verifykey2->size = keybits_to_keysize(verifykey.keysize); if (verifykey.attributes & PKEY_VERIFY_ATTR_OLD_MKVP) verifykey2->flags = PKEY_FLAGS_MATCH_ALT_MKVP; else verifykey2->flags = PKEY_FLAGS_MATCH_CUR_MKVP; return 0; } /** * Print a list of APQNs if verbose is set */ static void pr_verbose_apqn_list(bool verbose, struct pkey_apqn *list, u32 num) { u32 i; if (!verbose) return; for (i = 0; i < num ; i++) warnx(" APQN: %02x.%04x", list[i].card, list[i].domain); } /** * Filter a n array list of APQNs (struct pkey_apqn) by a list of APQN strings. * * @param[in] apqn_list a zero terminated array of pointers to C-strings * @param[in/out] apqns A list of APQNs as array of struct pkey_apqn to * filter. The list is modified during filtering. * @param[in/out] apqn_entries Number of entries in the list of APQNs. The * number is modified during filtering. * * @returns 0 on success, a negative errno in case of an error */ static int filter_apqn_list(const char **apqn_list, struct pkey_apqn **apqns, u32 *apqn_entries) { unsigned int count, i, k, card, domain; struct pkey_apqn *list = *apqns; bool found; if (apqn_list == NULL) return 0; for (count = 0; apqn_list[count] != NULL; count++) ; if (count == 0) return 0; for (i = 0; i < *apqn_entries; i++) { found = false; for (k = 0; apqn_list[k] != NULL; k++) { if (sscanf(apqn_list[k], "%x.%x", &card, &domain) != 2) return -EINVAL; if (list[i].card == card && list[i].domain == domain) { found = true; break; } } if (!found) { if (i < *apqn_entries - 1) memmove(&list[i], &list[i+1], (*apqn_entries - i - 1) * sizeof(struct pkey_apqn)); (*apqn_entries)--; i--; } } return 0; } /** * Build a list of APQNs in the form accepted by the pkey IOCTLs from the * List of APQNs as zero terminated array of pointers to C-strings that * are usable for the CCA-AESDATA key type. * * @param[in] apqn_list a zero terminated array of pointers to C-strings * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The * list must be freed by the caller using free(). * @param[out] apqn_entries Number of entries in the list of APQNs * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ static int build_apqn_list_for_aes_data(const char **apqn_list, struct pkey_apqn **apqns, u32 *apqn_entries, bool verbose) { unsigned int card, domain, count = 0; struct pkey_apqn *list = NULL; u32 list_entries = 0; int i; pr_verbose(verbose, "Build a list of APQNs for CCA-AESDATA"); if (apqn_list != NULL) for (count = 0; apqn_list[count] != NULL; count++) ; if (count > 0) { list = util_malloc(count * sizeof(struct pkey_apqn)); list_entries = count; for (i = 0; apqn_list[i] != NULL; i++) { if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2) return -EINVAL; list[i].card = card; list[i].domain = domain; } } else { /* * Although the new pkey IOCTLs do not support APQN entries * with ANY indication, build an ANY-list here. If we get here, * then the new IOCTLs are not available, and it will fall back * to the old IOCTL which do support ANY specifications. */ list = util_malloc(sizeof(struct pkey_apqn)); list_entries = 1; list[0].card = AUTOSELECT; list[0].domain = AUTOSELECT; } *apqns = list; *apqn_entries = list_entries; pr_verbose(verbose, "%u APQNs found", list_entries); pr_verbose_apqn_list(verbose, list, list_entries); return 0; } /** * Build a list of APQNs in the form accepted by the pkey IOCTLs from the * List of APQNs as zero terminated array of pointers to C-strings that * are usable for the specified key type. * * @param[in] pkey_fd the pkey file descriptor * @param[in] type the key type * @param[in] apqn_list a zero terminated array of pointers to C-strings * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The * list must be freed by the caller using free(). * @param[out] apqn_entries Number of entries in the list of APQNs * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ static int build_apqn_list_for_key_type(int pkey_fd, enum pkey_key_type type, const char **apqn_list, struct pkey_apqn **apqns, u32 *apqn_entries, bool verbose) { struct pkey_apqns4keytype apqns4keytype; int rc; util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); util_assert(apqns != NULL, "Internal error: apqns is NULL"); util_assert(apqn_entries != NULL, "Internal error: apqn_entries is NULL"); pr_verbose(verbose, "Build a list of APQNs for key type %d", type); memset(&apqns4keytype, 0, sizeof(apqns4keytype)); apqns4keytype.type = type; apqns4keytype.apqn_entries = INITIAL_APQN_ENTRIES; apqns4keytype.apqns = (struct pkey_apqn *)util_malloc( apqns4keytype.apqn_entries * sizeof(struct pkey_apqn)); do { rc = ioctl(pkey_fd, PKEY_APQNS4KT, &apqns4keytype); if (rc == 0) break; rc = -errno; pr_verbose(verbose, "ioctl PKEY_APQNS4KT rc: %s", strerror(-rc)); switch (rc) { case -ENOSPC: free(apqns4keytype.apqns); apqns4keytype.apqns = (struct pkey_apqn *) util_malloc(apqns4keytype.apqn_entries * sizeof(struct pkey_apqn)); continue; case -ENOTTY: /* * New IOCTL is not available: build the list * manually (Key type CCA-AESDATA only) */ free(apqns4keytype.apqns); if (type != PKEY_TYPE_CCA_DATA) return -ENOTSUP; rc = build_apqn_list_for_aes_data(apqn_list, apqns, apqn_entries, verbose); return rc; case -EINVAL: /* This is usually due to an unsupported key type */ rc = -ENOTSUP; goto out; default: goto out; } } while (rc != 0); if (apqns4keytype.apqn_entries == 0) { pr_verbose(verbose, "No APQN available for key type %d", type); rc = -ENODEV; goto out; } rc = filter_apqn_list(apqn_list, &apqns4keytype.apqns, &apqns4keytype.apqn_entries); if (rc != 0) goto out; if (apqns4keytype.apqn_entries == 0) { pr_verbose(verbose, "No APQN available for key type %d", type); rc = -ENODEV; goto out; } pr_verbose(verbose, "%u APQNs found", apqns4keytype.apqn_entries); pr_verbose_apqn_list(verbose, apqns4keytype.apqns, apqns4keytype.apqn_entries); out: if (rc == 0) { *apqns = apqns4keytype.apqns; *apqn_entries = apqns4keytype.apqn_entries; } else { *apqns = NULL; *apqn_entries = 0; free(apqns4keytype.apqns); } return rc; } /** * Build a list of APQNs in the form accepted by the pkey IOCTLs from the * List of APQNs as zero terminated array of pointers to C-strings that are * usable for the specufied key. * * @param[in] pkey_fd the pkey file descriptor * @param[in] key the key * @param[in] keylen the length of the key * @param[in] flags PKEY_FLAGS_MATCH_xxx flags * @param[in] apqn_list a zero terminated array of pointers to C-strings * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The * list must be freed by the caller using free(). * @param[out] apqn_entries Number of entries in the list of APQNs * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ static int build_apqn_list_for_key(int pkey_fd, u8 *key, u32 keylen, u32 flags, const char **apqn_list, struct pkey_apqn **apqns, u32 *apqn_entries, bool verbose) { struct pkey_apqns4key apqns4key; int rc; util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); util_assert(key != NULL, "Internal error: key is NULL"); util_assert(apqns != NULL, "Internal error: apqns is NULL"); util_assert(apqn_entries != NULL, "Internal error: apqn_entries is NULL"); pr_verbose(verbose, "Build a list of APQNs for the key"); memset(&apqns4key, 0, sizeof(apqns4key)); apqns4key.key = key; apqns4key.keylen = keylen; apqns4key.flags = flags; apqns4key.apqn_entries = INITIAL_APQN_ENTRIES; apqns4key.apqns = (struct pkey_apqn *)util_malloc( apqns4key.apqn_entries * sizeof(struct pkey_apqn)); do { rc = ioctl(pkey_fd, PKEY_APQNS4K, &apqns4key); if (rc == 0) break; rc = -errno; pr_verbose(verbose, "ioctl PKEY_APQNS4K rc: %s", strerror(-rc)); switch (rc) { case -ENOSPC: free(apqns4key.apqns); apqns4key.apqns = (struct pkey_apqn *) util_malloc(apqns4key.apqn_entries * sizeof(struct pkey_apqn)); continue; case -ENOTTY: /* * New IOCTL is not available: build the list manually * (Key type CCA-AESDATA only) */ free(apqns4key.apqns); if (!is_cca_aes_data_key(key, keylen)) return -ENOTSUP; rc = build_apqn_list_for_aes_data(apqn_list, apqns, apqn_entries, verbose); return rc; case -EINVAL: /* This is usually due to an unsupported key type */ rc = -ENOTSUP; goto out; default: goto out; } } while (rc != 0); if (apqns4key.apqn_entries == 0) { pr_verbose(verbose, "No APQN available for the key"); rc = -ENODEV; goto out; } rc = filter_apqn_list(apqn_list, &apqns4key.apqns, &apqns4key.apqn_entries); if (rc != 0) goto out; if (apqns4key.apqn_entries == 0) { pr_verbose(verbose, "No APQN available for the key"); rc = -ENODEV; goto out; } pr_verbose(verbose, "%u APQNs found", apqns4key.apqn_entries); pr_verbose_apqn_list(verbose, apqns4key.apqns, apqns4key.apqn_entries); out: if (rc == 0) { *apqns = apqns4key.apqns; *apqn_entries = apqns4key.apqn_entries; } else { *apqns = NULL; *apqn_entries = 0; free(apqns4key.apqns); } return rc; } /** * Convert the key type string into the pkey enumeration * * @param[in] key_type the type of the key * * @returns the pkey key type or 0 for an u known key type */ static enum pkey_key_type key_type_to_pkey_type(const char *key_type) { if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) return PKEY_TYPE_CCA_DATA; if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) return PKEY_TYPE_CCA_CIPHER; if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) return PKEY_TYPE_EP11_AES; return 0; } /** * Return the size of a key blob for a specific type * * @param[in] type the type of the key * * @returns the size of the key or 0 for an invalid key type */ static size_t key_size_for_type(enum pkey_key_type type) { switch (type) { case PKEY_TYPE_CCA_DATA: return AESDATA_KEY_SIZE; case PKEY_TYPE_CCA_CIPHER: return AESCIPHER_KEY_SIZE; case PKEY_TYPE_EP11: return EP11_KEY_SIZE; case PKEY_TYPE_EP11_AES: return EP11_AES_KEY_SIZE; default: return 0; } } static void print_error_for_ep11_exportable(void) { util_print_indented("Generating an exportable key of type " KEY_TYPE_EP11_AES " requires that access control " "point (ACP) 'XCP_CPB_ALLOW_COMBINED_EXTRACT' is " "'ON' on all used APQNs. This access control point " "is only supported on newer EP11 firmware levels.", 0); } /** * Generate a secure key by random * * @param[in] pkey_fd the pkey file descriptor * @param[in] keyfile the file name of the secure key to generate * @param[in] keybits the cryptographic size of the key in bits * @param[in] xts if true an XTS key is generated * @param[in] key_type the type of the key * @param[in] apqns a zero terminated array of pointers to APQN-strings, * or NULL for AUTOSELECT * @param[in] exportable if true the key shall be exportable * @param[in] wrap_with_trusted if true the key shall be wrapable with a * trusted KEK only * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ int generate_secure_key_random(int pkey_fd, const char *keyfile, size_t keybits, bool xts, const char *key_type, const char **apqns, bool exportable, bool wrap_with_trusted, bool verbose) { struct pkey_genseck2 genseck2; size_t secure_key_size, size; u8 *secure_key = NULL; int rc; util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); util_assert(key_type != NULL, "Internal error: key_type is NULL"); if (keybits == 0) keybits = DEFAULT_KEYBITS; pr_verbose(verbose, "Generate secure key by random"); memset(&genseck2, 0, sizeof(genseck2)); genseck2.type = key_type_to_pkey_type(key_type); if (genseck2.type == 0) { warnx("Key-type not supported: %s", key_type); return -ENOTSUP; } retry: genseck2.size = keybits_to_keysize(keybits); if (genseck2.size == 0) { warnx("Invalid value for '--keybits'/'-c': '%lu'", keybits); return -EINVAL; } if (keybits == 192 && xts) { warnx("Invalid value for '--keybits'|'-c' " "for XTS: '%lu'", keybits); return -EINVAL; } rc = build_apqn_list_for_key_type(pkey_fd, genseck2.type, apqns, &genseck2.apqns, &genseck2.apqn_entries, verbose); if (rc != 0) { if (rc == -ENODEV || rc == -ENOTSUP) warnx("No APQN is available that can generate a secure " "key of type %s", key_type); else warnx("Failed to build a list of APQNs that can " "generate a secure key of type %s: %s", key_type, strerror(-rc)); return rc; } size = key_size_for_type(genseck2.type); secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(size, xts); secure_key = util_zalloc(secure_key_size); genseck2.key = secure_key; genseck2.keylen = size; genseck2.keygenflags = flags_for_pkey_type(genseck2.type, exportable, wrap_with_trusted); rc = pkey_genseck2(pkey_fd, &genseck2, verbose); if (rc == -EINVAL && genseck2.type == PKEY_TYPE_EP11_AES) { /* * Older kernels may not support gensek2 with key type * PKEY_TYPE_EP11_AES, retry with PKEY_TYPE_EP11. */ pr_verbose(verbose, "ioctl PKEY_GENSECK2 does not support " "PKEY_TYPE_EP11_AES, fall back to PKEY_TYPE_EP11"); genseck2.type = PKEY_TYPE_EP11; free(genseck2.apqns); genseck2.apqns = NULL; genseck2.apqn_entries = 0; free(secure_key); goto retry; } if (rc != 0) { warnx("Failed to generate a secure key: %s", strerror(-rc)); if (exportable && strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) print_error_for_ep11_exportable(); goto out; } if (rc == 0 && genseck2.type == PKEY_TYPE_EP11) { if (is_ep11_key_session_bound(secure_key, size)) { warnx("The generated key is session bound. Kernel " "support is required for such keys"); goto out; } } if (xts) { free(genseck2.apqns); genseck2.apqns = NULL; genseck2.apqn_entries = 0; /* * Ensure to generate 2nd key with an APQN that has the same * master key that is used by the 1st key. */ rc = build_apqn_list_for_key(pkey_fd, secure_key, size, PKEY_FLAGS_MATCH_CUR_MKVP, apqns, &genseck2.apqns, &genseck2.apqn_entries, verbose); if (rc != 0) { if (rc == -ENODEV || rc == -ENOTSUP) warnx("No APQN is available that can generate " "a secure key of type %s", key_type); else warnx("Failed to build a list of APQNs that " "can generate a secure key of type %s: " "%s", key_type, strerror(-rc)); goto out; } genseck2.key = secure_key + size; genseck2.keylen = size; genseck2.keygenflags = flags_for_pkey_type(genseck2.type, exportable, wrap_with_trusted); rc = pkey_genseck2(pkey_fd, &genseck2, verbose); if (rc != 0) { warnx("Failed to generate a secure key: %s", strerror(-rc)); if (exportable && strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) print_error_for_ep11_exportable(); goto out; } } pr_verbose(verbose, "Successfully generated a secure key"); rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose); out: free(genseck2.apqns); free(secure_key); return rc; } /* * Generate a secure key from a clear key file * * @param[in] pkey_fd the pkey file descriptor * @param[in] keyfile the file name of the secure key to generate * @param[in] keybits the cryptographic size of the key in bits. When * keybits is 0, then the clear key file size * determines the keybits. * @param[in] xts if true an XTS key is generated * @param[in] clearkeyfile the file name of the clear key to read * @param[in] key_type the type of the key * @param[in] apqns a zero terminated array of pointers to APQN-strings, * or NULL for AUTOSELECT * @param[in] exportable if true the key shall be exportable * @param[in] wrap_with_trusted if true the key shall be wrapable with a * trusted KEK only * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ int generate_secure_key_clear(int pkey_fd, const char *keyfile, size_t keybits, bool xts, const char *clearkeyfile, const char *key_type, const char **apqns, bool exportable, bool wrap_with_trusted, bool verbose) { struct pkey_clr2seck2 clr2seck2; size_t secure_key_size; size_t clear_key_size; u8 *secure_key; u8 *clear_key; size_t size; int rc; util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); util_assert(clearkeyfile != NULL, "Internal error: clearkeyfile is NULL"); util_assert(key_type != NULL, "Internal error: key_type is NULL"); pr_verbose(verbose, "Generate secure key from a clear key"); clear_key = read_clear_key(clearkeyfile, keybits, xts, &clear_key_size, verbose); if (clear_key == NULL) return -EINVAL; memset(&clr2seck2, 0, sizeof(clr2seck2)); memcpy(&clr2seck2.clrkey, clear_key, HALF_KEYSIZE_FOR_XTS(clear_key_size, xts)); clr2seck2.type = key_type_to_pkey_type(key_type); if (clr2seck2.type == 0) { warnx("Key-type not supported: %s", key_type); return -ENOTSUP; } retry: clr2seck2.size = keybits_to_keysize(HALF_KEYSIZE_FOR_XTS( clear_key_size * 8, xts)); if (clr2seck2.size == 0) { warnx("Invalid clear key size: '%lu' bytes", clear_key_size); return -EINVAL; } if (keybits == 192 && xts) { warnx("Invalid clear key size for XTS: '%lu' bytes", clear_key_size); return -EINVAL; } rc = build_apqn_list_for_key_type(pkey_fd, clr2seck2.type, apqns, &clr2seck2.apqns, &clr2seck2.apqn_entries, verbose); if (rc != 0) { if (rc == -ENODEV || rc == -ENOTSUP) warnx("No APQN is available that can generate a secure " "key of type %s", key_type); else warnx("Failed to build a list of APQNs that can " "generate a secure key of type %s: %s", key_type, strerror(-rc)); return rc; } size = key_size_for_type(clr2seck2.type); secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(size, xts); secure_key = util_zalloc(secure_key_size); clr2seck2.key = secure_key; clr2seck2.keylen = size; clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type, exportable, wrap_with_trusted); rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); if (rc == -EINVAL && clr2seck2.type == PKEY_TYPE_EP11_AES) { /* * Older kernels may not support clr2seck2 with key type * PKEY_TYPE_EP11_AES, retry with PKEY_TYPE_EP11. */ pr_verbose(verbose, "ioctl PKEY_CLR2SECK2 does not support " "PKEY_TYPE_EP11_AES, fall back to PKEY_TYPE_EP11"); clr2seck2.type = PKEY_TYPE_EP11; free(clr2seck2.apqns); clr2seck2.apqns = NULL; clr2seck2.apqn_entries = 0; free(secure_key); goto retry; } if (rc != 0) { warnx("Failed to generate a secure key: %s", strerror(-rc)); if (exportable && strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) print_error_for_ep11_exportable(); goto out; } if (rc == 0 && clr2seck2.type == PKEY_TYPE_EP11) { if (is_ep11_key_session_bound(secure_key, size)) { warnx("The generated key is session bound. Kernel " "support is required for such keys"); goto out; } } if (xts) { free(clr2seck2.apqns); clr2seck2.apqns = NULL; clr2seck2.apqn_entries = 0; memcpy(&clr2seck2.clrkey, clear_key + clear_key_size / 2, clear_key_size / 2); /* * Ensure to generate 2nd key with an APQN that has the same * master key that is used by the 1st key. */ rc = build_apqn_list_for_key(pkey_fd, secure_key, size, PKEY_FLAGS_MATCH_CUR_MKVP, apqns, &clr2seck2.apqns, &clr2seck2.apqn_entries, verbose); if (rc != 0) { if (rc == -ENODEV || rc == -ENOTSUP) warnx("No APQN is available that can generate " "a secure key of type %s", key_type); else warnx("Failed to build a list of APQNs that " "can generate a secure key of type %s: " "%s", key_type, strerror(-rc)); goto out; } clr2seck2.key = secure_key + size; clr2seck2.keylen = size; clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type, exportable, wrap_with_trusted); rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); if (rc != 0) { warnx("Failed to generate a secure key: %s", strerror(-rc)); if (exportable && strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) print_error_for_ep11_exportable(); goto out; } } pr_verbose(verbose, "Successfully generated a secure key from a clear key"); rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose); out: memset(&clr2seck2, 0, sizeof(clr2seck2)); memset(clear_key, 0, clear_key_size); free(clear_key); free(secure_key); free(clr2seck2.apqns); return rc; } /** * Validates an XTS secure key (the second part) * * @param[in] pkey_fd the pkey file descriptor * @param[in] apqn the APQN to verify the key with * @param[in] secure_key a buffer containing the secure key * @param[in] secure_key_size the secure key size * @param[in] part1_keysize the key size of the first key part * @param[in] part1_flags the flags of the first key part * @param[out] clear_key_bitsize on return , the cryptographic size of the * clear key * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ static int validate_secure_xts_key(int pkey_fd, struct pkey_apqn *apqn, u8 *secure_key, size_t secure_key_size, enum pkey_key_size part1_keysize, u32 part1_flags, size_t *clear_key_bitsize, bool verbose) { struct pkey_verifykey2 verifykey2; int rc; util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); util_assert(apqn != NULL, "Internal error: apqn is NULL"); memset(&verifykey2, 0, sizeof(verifykey2)); verifykey2.key = secure_key + (secure_key_size / 2); verifykey2.keylen = secure_key_size / 2; verifykey2.cardnr = apqn->card; verifykey2.domain = apqn->domain; rc = pkey_verifyseck2(pkey_fd, &verifykey2, verbose); if (rc < 0) { pr_verbose(verbose, "Failed to validate the 2nd part of the " "XTS secure key on APQN %02x.%04x: %s", apqn->card, apqn->domain, strerror(-rc)); return rc; } if (verifykey2.size != part1_keysize) { pr_verbose(verbose, "XTS secure key contains 2 keys using " "different key sizes"); return -EINVAL; } if (verifykey2.flags != part1_flags) { pr_verbose(verbose, "XTS secure key contains 2 keys using " "different master keys"); return -EINVAL; } if (clear_key_bitsize && verifykey2.size != PKEY_SIZE_UNKNOWN) *clear_key_bitsize += verifykey2.size; return 0; } /** * Validates a secure key * * @param[in] pkey_fd the pkey file descriptor * @param[in] secure_key a buffer containing the secure key * @param[in] secure_key_size the secure key size * @param[out] clear_key_bitsize on return , the cryptographic size of the * clear key * @param[out] is_old_mk in return set to 1 to indicate if the secure key * is currently enciphered by the OLD master key * @param[in] apqns a zero terminated array of pointers to APQN-strings, * or NULL for AUTOSELECT * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ int validate_secure_key(int pkey_fd, u8 *secure_key, size_t secure_key_size, size_t *clear_key_bitsize, int *is_old_mk, const char **apqns, bool verbose) { struct pkey_verifykey2 verifykey2; struct pkey_apqn *list = NULL; bool xts, valid, securekey; u32 i, list_entries = 0; u32 flags = 0; int rc; util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); securekey = is_secure_key(secure_key, secure_key_size); xts = securekey ? is_xts_key(secure_key, secure_key_size) : false; if (securekey) { flags = PKEY_FLAGS_MATCH_CUR_MKVP; if (is_cca_aes_data_key(secure_key, secure_key_size) || is_cca_aes_cipher_key(secure_key, secure_key_size)) flags |= PKEY_FLAGS_MATCH_ALT_MKVP; rc = build_apqn_list_for_key(pkey_fd, secure_key, HALF_KEYSIZE_FOR_XTS( secure_key_size, xts), flags, apqns, &list, &list_entries, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to build a list of APQNs " "that can validate this secure " "key: %s", strerror(-rc)); return rc; } } else { list = util_malloc(sizeof(struct pkey_apqn)); list[0].card = 0; list[0].domain = 0; list_entries = 1; } if (is_old_mk != NULL) *is_old_mk = securekey ? true : false; if (clear_key_bitsize != NULL) *clear_key_bitsize = 0; valid = false; for (i = 0; i < list_entries; i++) { memset(&verifykey2, 0, sizeof(verifykey2)); verifykey2.key = secure_key; verifykey2.keylen = HALF_KEYSIZE_FOR_XTS(secure_key_size, xts); verifykey2.cardnr = list[i].card; verifykey2.domain = list[i].domain; rc = pkey_verifyseck2(pkey_fd, &verifykey2, verbose); if (rc < 0) { pr_verbose(verbose, "Failed to validate the secure key " "on APQN %02x.%04x: %s", list[i].card, list[i].domain, strerror(-rc)); continue; } if (xts) { rc = validate_secure_xts_key(pkey_fd, &list[i], secure_key, secure_key_size, verifykey2.size, verifykey2.flags, clear_key_bitsize, verbose); if (rc != 0) continue; } valid = true; if (clear_key_bitsize) { if (verifykey2.size != PKEY_SIZE_UNKNOWN) *clear_key_bitsize += verifykey2.size; clear_key_bitsize = NULL; /* Set it only once */ } /* * If at least one of the APQNs have a matching current MK, * then don't report OLD, even if some match the old MK. */ if (securekey && is_old_mk && (verifykey2.flags & PKEY_FLAGS_MATCH_CUR_MKVP)) *is_old_mk = false; } if (!valid) return -ENODEV; pr_verbose(verbose, "Secure key validation completed successfully"); if (list != NULL) free(list); return rc; } /** * Generate a key verification pattern of a secure AES key by encrypting the all * zero message with the secure key using the AF_ALG interface * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * @param[in] vp buffer where the verification pattern is returned * @param[in] vp_len the size of the buffer * @param[in] cipher the cipher to use ('cbc([p]aes)' or 'xts([p]aes)'), * or null to determine it from the secure key. * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ int generate_aes_key_verification_pattern(const u8 *key, size_t key_size, char *vp, size_t vp_len, const char *cipher, bool verbose) { int tfmfd = -1, opfd = -1, rc = 0, retry_count = 0; char null_msg[ENC_ZERO_LEN]; char enc_zero[ENC_ZERO_LEN]; struct af_alg_iv *alg_iv; struct cmsghdr *header; uint32_t *type; ssize_t len; size_t i; struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = "skcipher", }; struct iovec iov = { .iov_base = (void *)null_msg, .iov_len = sizeof(null_msg), }; int iv_msg_size = CMSG_SPACE(sizeof(*alg_iv) + PAES_BLOCK_SIZE); char buffer[CMSG_SPACE(sizeof(*type)) + iv_msg_size]; struct msghdr msg = { .msg_control = buffer, .msg_controllen = sizeof(buffer), .msg_iov = &iov, .msg_iovlen = 1, }; if (vp_len < VERIFICATION_PATTERN_LEN) { rc = -EMSGSIZE; goto out; } if (cipher != NULL) { util_strlcpy((char *)sa.salg_name, cipher, sizeof(sa.salg_name)); } else { snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(paes)", is_xts_key(key, key_size) ? "xts" : "cbc"); } tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); if (tfmfd < 0) { rc = -errno; pr_verbose(verbose, "Failed to open an AF_ALG socket"); goto out; } if (bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { rc = -errno; pr_verbose(verbose, "Failed to bind the AF_ALG socket, " "salg_name='%s' ", sa.salg_name); goto out; } retry_setkey: if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key, key_size) < 0) { rc = -errno; pr_verbose(verbose, "Failed to set the key: %s", strerror(-rc)); /* * After a master key change, it can happen that the setkey * operation returns EINVAL or EAGAIN, although the key is * valid. This is a temporary situation and the operation will * succeed, once the firmware has completed some internal * processing related with the master key change. * Delay 1 second and retry up to 10 times. */ if ((rc == -EINVAL || rc == -EAGAIN) && retry_count < 10) { pr_verbose(verbose, "Retrying after 1 second..."); retry_count++; sleep(1); goto retry_setkey; } goto out; } rc = 0; opfd = accept(tfmfd, NULL, NULL); if (opfd < 0) { rc = -errno; pr_verbose(verbose, "Failed to accept on the AF_ALG socket"); goto out; } memset(null_msg, 0, sizeof(null_msg)); memset(buffer, 0, sizeof(buffer)); header = CMSG_FIRSTHDR(&msg); if (header == NULL) { pr_verbose(verbose, "Failed to obtain control message header"); rc = -EINVAL; goto out; } header->cmsg_level = SOL_ALG; header->cmsg_type = ALG_SET_OP; header->cmsg_len = CMSG_LEN(sizeof(*type)); type = (void *)CMSG_DATA(header); *type = ALG_OP_ENCRYPT; header = CMSG_NXTHDR(&msg, header); if (header == NULL) { pr_verbose(verbose, "Failed to obtain control message " "header"); rc = -EINVAL; goto out; } header->cmsg_level = SOL_ALG; header->cmsg_type = ALG_SET_IV; header->cmsg_len = iv_msg_size; alg_iv = (void *)CMSG_DATA(header); alg_iv->ivlen = PAES_BLOCK_SIZE; memcpy(alg_iv->iv, null_msg, PAES_BLOCK_SIZE); len = sendmsg(opfd, &msg, 0); if (len != ENC_ZERO_LEN) { pr_verbose(verbose, "Failed to send to the AF_ALG socket"); rc = -errno; goto out; } len = read(opfd, enc_zero, sizeof(enc_zero)); if (len != ENC_ZERO_LEN) { pr_verbose(verbose, "Failed to receive from the AF_ALG socket"); rc = -errno; goto out; } memset(vp, 0, vp_len); for (i = 0; i < sizeof(enc_zero); i++) sprintf(&vp[i * 2], "%02x", enc_zero[i]); pr_verbose(verbose, "Key verification pattern: %s", vp); out: if (opfd != -1) close(opfd); if (tfmfd != -1) close(tfmfd); if (rc != 0) pr_verbose(verbose, "Failed to generate the key verification " "pattern: %s", strerror(-rc)); return rc; } /** * Generate a key verification pattern of a secure key by encrypting the all * zero message with the secure key using the AF_ALG interface * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * @param[in] vp buffer where the verification pattern is returned * @param[in] vp_len the size of the buffer * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ int generate_key_verification_pattern(const u8 *key, size_t key_size, char *vp, size_t vp_len, bool verbose) { return generate_aes_key_verification_pattern(key, key_size, vp, vp_len, NULL, verbose); } int get_master_key_verification_pattern(const u8 *key, size_t key_size, u8 *mkvp, bool UNUSED(verbose)) { struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key; struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; struct ep11keytoken *ep11key = (struct ep11keytoken *)key; struct ep11keytoken *ep11key2 = (struct ep11keytoken *)(key + sizeof(struct ep11kblob_header)); util_assert(key != NULL, "Internal error: secure_key is NULL"); util_assert(mkvp != NULL, "Internal error: mkvp is NULL"); memset(mkvp, 0, MKVP_LENGTH); if (is_cca_aes_data_key(key, key_size)) memcpy(mkvp, &datakey->mkvp, sizeof(datakey->mkvp)); else if (is_cca_aes_cipher_key(key, key_size)) memcpy(mkvp, &cipherkey->kvp, sizeof(cipherkey->kvp)); else if (is_ep11_aes_key(key, key_size)) memcpy(mkvp, &ep11key->wkvp, sizeof(ep11key->wkvp)); else if (is_ep11_aes_key_with_header(key, key_size)) memcpy(mkvp, &ep11key2->wkvp, sizeof(ep11key2->wkvp)); else return -EINVAL; return 0; } /** * Check if the specified key is a CCA AESDATA key token. * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * * @returns true if the key is an CCA AESDATA token type */ bool is_cca_aes_data_key(const u8 *key, size_t key_size) { struct tokenheader *hdr = (struct tokenheader *)key; if (key == NULL || key_size < AESDATA_KEY_SIZE) return false; if (hdr->type != TOKEN_TYPE_CCA_INTERNAL) return false; if (hdr->version != TOKEN_VERSION_AESDATA) return false; return true; } /** * Check if the specified key is a CCA AESCIPHER key token. * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * * @returns true if the key is an CCA AESCIPHER token type */ bool is_cca_aes_cipher_key(const u8 *key, size_t key_size) { struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; if (key == NULL || key_size < AESCIPHER_KEY_SIZE) return false; if (cipherkey->type != TOKEN_TYPE_CCA_INTERNAL) return false; if (cipherkey->version != TOKEN_VERSION_AESCIPHER) return false; if (cipherkey->length > key_size) return false; if (cipherkey->kms != 0x03) /* key wrapped by master key */ return false; if (cipherkey->kwm != 0x02) /* key wrapped using AESKW */ return false; if (cipherkey->pfv != 0x00 && cipherkey->pfv != 0x01) /* V0 or V1 */ return false; if (cipherkey->adv != 0x01) /* Should have ass. data sect. version 1 */ return false; if (cipherkey->at != 0x02) /* Algorithm: AES */ return false; if (cipherkey->kt != 0x0001) /* Key type: CIPHER */ return false; if (cipherkey->adl != 26) /* Ass. data section length should be 26 */ return false; if (cipherkey->kll != 0) /* Should have no key label */ return false; if (cipherkey->eadl != 0) /* Should have no ext associated data */ return false; if (cipherkey->uadl != 0) /* Should have no user associated data */ return false; if (cipherkey->kufc != 2) /* Should have 2 KUFs */ return false; if (cipherkey->kmfc != 3) /* Should have 3 KMFs */ return false; return true; } /** * Check if the specified key is a EP11 AES key token. * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * * @returns true if the key is an EP11 AES token type */ bool is_ep11_aes_key(const u8 *key, size_t key_size) { struct ep11keytoken *ep11key = (struct ep11keytoken *)key; if (key == NULL || key_size < EP11_KEY_SIZE) return false; if (ep11key->head.type != TOKEN_TYPE_NON_CCA) return false; if (ep11key->head.hver != 0) return false; if (ep11key->head.version != TOKEN_VERSION_EP11_AES) return false; if (ep11key->head.len > key_size) return false; if (ep11key->version != 0x1234) return false; return true; } /** * Check if the specified key is a EP11 AES key token with external header. * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * * @returns true if the key is an EP11 AES token with external header type */ bool is_ep11_aes_key_with_header(const u8 *key, size_t key_size) { struct ep11kblob_header *header = (struct ep11kblob_header *)key; struct ep11keytoken *ep11key = (struct ep11keytoken *)(key + sizeof(struct ep11kblob_header)); if (key == NULL || key_size < EP11_AES_KEY_SIZE) return false; if (header->type != TOKEN_TYPE_NON_CCA) return false; if (header->hver != 0) return false; if (header->version != TOKEN_VERSION_EP11_AES_WITH_HEADER) return false; if (header->len > key_size) return false; if (ep11key->version != 0x1234) return false; return true; } /** * Check if the specified EP11 AES key is session bound. * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * * @returns true if the key is an EP11 AES token type */ bool is_ep11_key_session_bound(const u8 *key, size_t key_size) { struct ep11keytoken *ep11key; if (is_ep11_aes_key(key, key_size)) { ep11key = (struct ep11keytoken *)key; return memcmp(ep11key->session + sizeof(ep11key->head), ZERO_SESSION, sizeof(ep11key->session) - sizeof(ep11key->head)) != 0; } else if (is_ep11_aes_key_with_header(key, key_size)) { ep11key = (struct ep11keytoken *) (key + sizeof(struct ep11kblob_header)); return memcmp(ep11key->session, ZERO_SESSION, sizeof(ep11key->session)) != 0; } else { return false; } } /** * Check if the specified key is a PVSECRET-AES key token. * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * * @returns true if the key is a PVSECRET token type */ bool is_pvsecret_aes_key(const u8 *key, size_t key_size) { struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key; if (key == NULL || key_size < PVSECRET_KEY_SIZE) return false; if (pvsecret->hdr.type != TOKEN_TYPE_NON_CCA) return false; if (pvsecret->hdr.version != TOKEN_VERSION_PVSECRET) return false; switch (pvsecret->secret_type) { case UV_SECRET_TYPE_AES_128: case UV_SECRET_TYPE_AES_192: case UV_SECRET_TYPE_AES_256: case UV_SECRET_TYPE_AES_XTS_128: case UV_SECRET_TYPE_AES_XTS_256: return true; default: return false; } } /** * Check if the specified key is an XTS type key * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * * @returns true if the key is an XTS key type */ bool is_xts_key(const u8 *key, size_t key_size) { struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key; if (is_cca_aes_data_key(key, key_size)) { if (key_size == 2 * AESDATA_KEY_SIZE && is_cca_aes_data_key(key + AESDATA_KEY_SIZE, key_size - AESDATA_KEY_SIZE)) return true; } else if (is_cca_aes_cipher_key(key, key_size)) { if (key_size == 2 * AESCIPHER_KEY_SIZE && is_cca_aes_cipher_key(key + AESCIPHER_KEY_SIZE, key_size - AESCIPHER_KEY_SIZE)) return true; } else if (is_ep11_aes_key(key, key_size)) { if (key_size == 2 * EP11_KEY_SIZE && is_ep11_aes_key(key + EP11_KEY_SIZE, key_size - EP11_KEY_SIZE)) return true; } else if (is_ep11_aes_key_with_header(key, key_size)) { if (key_size == 2 * EP11_AES_KEY_SIZE && is_ep11_aes_key_with_header(key + EP11_AES_KEY_SIZE, key_size - EP11_AES_KEY_SIZE)) return true; } else if (is_pvsecret_aes_key(key, key_size)) { switch (pvsecret->secret_type) { case UV_SECRET_TYPE_AES_XTS_128: case UV_SECRET_TYPE_AES_XTS_256: return true; default: return false; } } return false; } /** * Check if the specified key is a secure key type (i.e. requires a crypto card) * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * * @returns true if the key is a secure key type */ bool is_secure_key(const u8 *key, size_t key_size) { if (is_cca_aes_data_key(key, key_size)) return true; if (is_cca_aes_cipher_key(key, key_size)) return true; if (is_ep11_aes_key(key, key_size)) return true; if (is_ep11_aes_key_with_header(key, key_size)) return true; return false; } /** * Gets the size in bits of the effective key of the specified secure key * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * @param[out] bitsize On return, contains the size in bits of the key. * If the key size can not be determined, then 0 is * passed back as bitsize. * * @returns 0 on success, a negative errno in case of an error */ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) { struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key; struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; struct ep11keytoken *ep11key = (struct ep11keytoken *)key; struct ep11kblob_header *hdr = (struct ep11kblob_header *)key; struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key; util_assert(bitsize != NULL, "Internal error: bitsize is NULL"); if (is_cca_aes_data_key(key, key_size)) { *bitsize = datakey->bitsize; if (key_size == 2 * AESDATA_KEY_SIZE) { datakey = (struct aesdatakeytoken *)(key + AESDATA_KEY_SIZE); *bitsize += datakey->bitsize; } } else if (is_cca_aes_cipher_key(key, key_size)) { if (cipherkey->pfv == 0x00) /* V0 payload */ *bitsize = cipherkey->pl - 384; else *bitsize = 0; /* Unknown */ if (key_size == 2 * AESCIPHER_KEY_SIZE) { cipherkey = (struct aescipherkeytoken *)(key + AESCIPHER_KEY_SIZE); if (cipherkey->pfv == 0x00) /* V0 payload */ *bitsize += cipherkey->pl - 384; } } else if (is_ep11_aes_key(key, key_size)) { *bitsize = ep11key->head.bitlen; if (key_size == 2 * EP11_KEY_SIZE) { ep11key = (struct ep11keytoken *)(key + EP11_KEY_SIZE); *bitsize += ep11key->head.bitlen; } } else if (is_ep11_aes_key_with_header(key, key_size)) { *bitsize = hdr->bitlen; if (key_size == 2 * EP11_AES_KEY_SIZE) { hdr = (struct ep11kblob_header *) (key + EP11_AES_KEY_SIZE); *bitsize += hdr->bitlen; } } else if (is_pvsecret_aes_key(key, key_size)) { switch (pvsecret->secret_type) { case UV_SECRET_TYPE_AES_128: *bitsize = 128; break; case UV_SECRET_TYPE_AES_192: *bitsize = 192; break; case UV_SECRET_TYPE_AES_256: *bitsize = 256; break; case UV_SECRET_TYPE_AES_XTS_128: *bitsize = 128 * 2; break; case UV_SECRET_TYPE_AES_XTS_256: *bitsize = 256 * 2; break; default: return -EINVAL; } } else { return -EINVAL; } return 0; } /** * Returns the type of the key * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * * @returns a static string on success, NULL in case of an error */ const char *get_key_type(const u8 *key, size_t key_size) { if (is_cca_aes_data_key(key, key_size)) return KEY_TYPE_CCA_AESDATA; if (is_cca_aes_cipher_key(key, key_size)) return KEY_TYPE_CCA_AESCIPHER; if (is_ep11_aes_key(key, key_size)) return KEY_TYPE_EP11_AES; if (is_ep11_aes_key_with_header(key, key_size)) return KEY_TYPE_EP11_AES; if (is_pvsecret_aes_key(key, key_size)) return KEY_TYPE_PVSECRET_AES; return NULL; } /** * Returns true if the key type is a secure key type * * @param[in] key_type the type of the key * * @returns true if the key type is a secure key type, false otherwise */ bool is_secure_key_type(const char *key_type) { if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) return true; if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) return true; if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) return true; return false; } /** * Returns the minimum card level for a specific key type * * @param[in] key_type the type of the key * * @returns the minimum card level, or -1 for unknown key types */ int get_min_card_level_for_keytype(const char *key_type) { if (key_type == NULL) return -1; if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) return 3; if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) return 6; if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) return 7; return -1; } const struct fw_version *get_min_fw_version_for_keytype(const char *key_type) { static const struct fw_version ep11_fw_version = { .major = 0, .minor = 0, .api_ordinal = 4, }; if (key_type == NULL) return NULL; if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) return &ep11_fw_version; return NULL; } /** * Returns the card type required for a specific key type * * @param[in] key_type the type of the key * * @returns the card type, or CARD_TYPE_ANY for unknown key types */ enum card_type get_card_type_for_keytype(const char *key_type) { if (key_type == NULL) return CARD_TYPE_ANY; if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) return CARD_TYPE_CCA; if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) return CARD_TYPE_CCA; if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) return CARD_TYPE_EP11; return CARD_TYPE_ANY; } /** * Performs extended checks on an AES CIPHER key. It checks the key usage * fields (KUFs) and key management fields (KMFs) of the key. The function * returns -EINVAL and issues warning messages if a mismatch is detected. * * @param[in] key the secure key token * @param[in] key_size the size of the secure key * @param[in] exportable if true the key shall be exportable * * @returns 0 on success, a negative errno in case of an error */ int check_aes_cipher_key(const u8 *key, size_t key_size, bool exportable) { struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; bool mismatch = false; if (!is_cca_aes_cipher_key(key, key_size)) { warnx("The key is not of type '"KEY_TYPE_CCA_AESCIPHER"'"); return -EINVAL; } if ((cipherkey->kuf1 & 0x8000) == 0) { printf("WARNING: The secure key can not be used for " "encryption\n"); mismatch = true; } if ((cipherkey->kuf1 & 0x4000) == 0) { printf("WARNING: The secure key can not be used for " "decryption\n"); mismatch = true; } if (cipherkey->kuf1 & 0x1000) { printf("WARNING: The secure key can only be used in UDXs\n"); mismatch = true; } if (!exportable && (cipherkey->kmf1 & 0x8000)) { printf("WARNING: The secure key can be exported using a " "symmetric key\n"); mismatch = true; } if (!exportable && (cipherkey->kmf1 & 0x4000)) { printf("WARNING: The secure key can be exported using an " "unauthenticated asymmetric key\n"); mismatch = true; } if (!exportable && (cipherkey->kmf1 & 0x2000)) { printf("WARNING: The secure key can be exported using an " "authenticated asymmetric key\n"); mismatch = true; } if (!exportable && (cipherkey->kmf1 & 0x1000)) { printf("WARNING: The secure key can be exported using a RAW " "key\n"); mismatch = true; } if ((cipherkey->kmf1 & 0x0800) == 0) { printf("WARNING: The secure key can not be transformed into a " "CPACF protected key\n"); mismatch = true; } if (!exportable && (cipherkey->kmf1 & 0x0080) == 0) { printf("WARNING: The secure key can be exported using a DES " "key\n"); mismatch = true; } if (!exportable && (cipherkey->kmf1 & 0x0040) == 0) { printf("WARNING: The secure key can be exported using an AES " "key\n"); mismatch = true; } if (!exportable && (cipherkey->kmf1 & 0x0008) == 0) { printf("WARNING: The secure key can be exported using an RSA " "key\n"); mismatch = true; } if (cipherkey->kmf2 & 0xC000) { printf("WARNING: The secure key is incomplete\n"); mismatch = true; } if (cipherkey->kmf2 & 0x0010) { printf("WARNING: The secure key was previously encrypted with " "an untrusted KEK\n"); mismatch = true; } if (cipherkey->kmf2 & 0x0008) { printf("WARNING: The secure key was previously in a format " "without type or usage attributes\n"); mismatch = true; } if (cipherkey->kmf2 & 0x0004) { printf("WARNING: The secure key was previously encrypted with " "a key weaker than itself\n"); mismatch = true; } if (cipherkey->kmf2 & 0x0002) { printf("WARNING: The secure key was previously in a non-CCA " "format\n"); mismatch = true; } if (cipherkey->kmf2 & 0x0001) { printf("WARNING: The secure key was previously encrypted in " "ECB mode\n"); mismatch = true; } if ((cipherkey->kmf3 & 0xFF00) == 0x0000 || (cipherkey->kmf3 & 0x00FF) == 0x0000) { printf("WARNING: The secure key was created by an unknown " "method\n"); mismatch = true; } if ((cipherkey->kmf3 & 0xFF00) == 0x0400 || (cipherkey->kmf3 & 0x00FF) == 0x0004) { printf("WARNING: The secure key was created from cleartext key " "components\n"); mismatch = true; } if ((cipherkey->kmf3 & 0xFF00) == 0x0500 || (cipherkey->kmf3 & 0x00FF) == 0x0005) { printf("WARNING: The secure key was entered as a cleartext key " "value\n"); mismatch = true; } if (!exportable && (cipherkey->kmf3 & 0x00FF) == 0x0012) { printf("WARNING: The secure key was converted from a CCA " "key-token that had no export control attributes\n"); mismatch = true; } return mismatch ? -EINVAL : 0; } static int reencipher_cca_secure_key(struct cca_lib *cca, u8 *secure_key, size_t secure_key_size, const char *apqns, u8 *mkvp, enum reencipher_method method, bool *apqn_selected, bool verbose) { unsigned int flags; int rc; if (method == REENCIPHER_OLD_TO_CURRENT) flags = FLAG_SEL_CCA_MATCH_OLD_MKVP; else flags = FLAG_SEL_CCA_MATCH_CUR_MKVP | FLAG_SEL_CCA_NEW_MUST_BE_SET; *apqn_selected = true; rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, flags, verbose); if (rc == -ENOTSUP) { rc = 0; *apqn_selected = false; } if (rc != 0) { pr_verbose(verbose, "No APQN found that is suitable " "for re-enciphering this secure key"); return rc; } rc = key_token_change(cca, secure_key, secure_key_size, method == REENCIPHER_OLD_TO_CURRENT ? METHOD_OLD_TO_CURRENT : METHOD_CURRENT_TO_NEW, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to re-encipher secure key: " "%s", strerror(-rc)); return rc; } return 0; } static int reencipher_ep11_secure_key(struct ep11_lib *ep11, u8 *secure_key, size_t secure_key_size, const char *apqns, u8 *mkvp, bool *apqn_selected, bool verbose) { unsigned int card, domain; unsigned int flags; target_t target; int rc; flags = FLAG_SEL_EP11_MATCH_CUR_MKVP | FLAG_SEL_EP11_NEW_MUST_BE_SET; *apqn_selected = true; rc = select_ep11_apqn_by_mkvp(ep11, mkvp, apqns, flags, &target, &card, &domain, verbose); if (rc == -ENOTSUP) { rc = 0; *apqn_selected = false; } if (rc != 0) { pr_verbose(verbose, "No APQN found that is suitable " "for re-enciphering this secure key"); return rc; } rc = reencipher_ep11_key(ep11, target, card, domain, secure_key, secure_key_size, verbose); free_ep11_target_for_apqn(ep11, target); if (rc != 0) { pr_verbose(verbose, "Failed to re-encipher secure key: " "%s", strerror(-rc)); return rc; } return 0; } /** * Re-enciphers a secure key * * @param[in] lib the external library struct * @param[in] secure_key a buffer containing the secure key * @param[in] secure_key_size the secure key size * @param[in] apqns a comma separated list of APQNs. If NULL is * specified, or an empty string, then all online * APQNs of the matching type are subject to be used. * @param[in] method the re-encipher method * @param[out] apqn_selected On return: true if a specific APQN was selected. * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error. * -ENODEV is returned if no APQN could be found with a matching master key. * -EIO is returned if the re-enciphering has failed. */ int reencipher_secure_key(struct ext_lib *lib, u8 *secure_key, size_t secure_key_size, const char *apqns, enum reencipher_method method, bool *apqn_selected, bool verbose) { u8 mkvp[MKVP_LENGTH]; int rc; util_assert(lib != NULL, "Internal error: lib is NULL"); util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); util_assert(apqn_selected != NULL, "Internal error: apqn_selected is NULL"); *apqn_selected = true; rc = get_master_key_verification_pattern(secure_key, secure_key_size, mkvp, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to get the master key verification " "pattern: %s", strerror(-rc)); return rc; } if (is_ep11_aes_key(secure_key, secure_key_size) || is_ep11_aes_key_with_header(secure_key, secure_key_size)) { /* EP11 secure key: need the EP11 host library */ if (lib->ep11->lib_ep11 == NULL) { rc = load_ep11_library(lib->ep11, verbose); if (rc != 0) return rc; } if (method == REENCIPHER_OLD_TO_CURRENT) { util_print_indented("ERROR: An APQN of a IBM " "cryptographic adapter in EP11 " "coprocessor mode does not have an " "OLD master key register. Thus, " "you can not re-encipher a secure " "key of type 'EP11-AES' from the " "OLD to the CURRENT master key " "register.\n", 0); return -EINVAL; } rc = reencipher_ep11_secure_key(lib->ep11, secure_key, secure_key_size, apqns, mkvp, apqn_selected, verbose); } else if (is_cca_aes_data_key(secure_key, secure_key_size) || is_cca_aes_cipher_key(secure_key, secure_key_size)) { /* CCA secure key: need the CCA host library */ if (lib->cca->lib_csulcca == NULL) { rc = load_cca_library(lib->cca, verbose); if (rc != 0) return rc; } rc = reencipher_cca_secure_key(lib->cca, secure_key, secure_key_size, apqns, mkvp, method, apqn_selected, verbose); } else { pr_verbose(verbose, "Invalid key type"); rc = -EINVAL; } return rc; }