diff --git a/man/man8/swtpm_cert.pod b/man/man8/swtpm_cert.pod index 3e9c748c5..5d8fe771b 100644 --- a/man/man8/swtpm_cert.pod +++ b/man/man8/swtpm_cert.pod @@ -19,7 +19,7 @@ The following options are supported: =over 4 -=item B<--type {ek|platform}> +=item B<--type {ek|platform|iak|idevid}> The type of certificate to create; by default an EK certificate is created. @@ -114,6 +114,11 @@ The TPM model (part number). The TPM's firmware version. +=item B<--tpm-serial-num > + +The TPM's serial number. This option is required for creating IAK and IDevID +certificates. + =item B<--platform-manufacturer > The name of the platform manufacturer. @@ -156,7 +161,9 @@ The output may contain the following: "type": "swtpm_cert", "features": [ "cmdarg-signkey-pwd", - "cmdarg-parentkey-pwd" + "cmdarg-parentkey-pwd", + "cmdarg-tpm-serial-num", + "supports-iak-idevid" ], "version": "0.7.0" } @@ -175,6 +182,14 @@ The I<--signkey-pwd> option is supported. The I<--parentkey-pwd> option is supported. +=item B (since v0.9) + +The I<--tpm-serial-num> option is supported. + +=item B (since v0.9) + +Creation of IAK and IDevID certificates is supported. + =back =item B<--help, -h> diff --git a/man/man8/swtpm_localca.pod b/man/man8/swtpm_localca.pod index e9771adaf..48c53317e 100644 --- a/man/man8/swtpm_localca.pod +++ b/man/man8/swtpm_localca.pod @@ -41,7 +41,7 @@ The following options are supported: =item B<--type type> This parameter indicates the type of certificate to create. The type parameter may -be one of the following: I, or I +be one of the following: I, I, I, I =item B<--dir dir> @@ -87,6 +87,11 @@ TPM specification parameters that describe the specification that was followed for the TPM implementation. The parameters will be passed to swtpm_cert for the creation of the EK certificate. +=item B<--tpm-serial-num> + +The TPM's serial number that will become part of the certificate. This parameter +is necessary for IAK and IDevID certificates. + =item B<--tpm2> Create TPM 2 compliant certificates. diff --git a/man/man8/swtpm_setup.pod b/man/man8/swtpm_setup.pod index 295eaea4a..0425659e5 100644 --- a/man/man8/swtpm_setup.pod +++ b/man/man8/swtpm_setup.pod @@ -291,7 +291,8 @@ The output may contain the following: "tpm2-rsa-keysize-2048", "tpm2-rsa-keysize-3072", "cmdarg-profile", - "cmdarg-profile-remove-disabled" + "cmdarg-profile-remove-disabled", + "cmdarg-no-iak" ], "version": "0.7.0" } @@ -323,6 +324,12 @@ The I<--create-config-files> option is supported. The I<--reconfigure> option is supported and allows the reconfiguration of the active PCR banks. +=item B (since v0.9) + +If I<--create-ek-cert> is given then swtpm_setup also creates IAK and IDevID +keys and certificates. When I<--no-iak> is passed then neither one of them is +created. + =item B (since v0.4) The shown RSA key sizes are supported for a TPM 2's EK key. If none of the @@ -401,6 +408,10 @@ systems this may be the case when a user is part of the 'tss' group. In this case it is recommended that the user replace the swtpm-localca.conf created with this command with a symbolic link to /etc/swtpm-localca.conf. +=item <--no-iak> (since v0.9) + +Do not create IAK and IDevID keys and certificates. + =item B<--help, -h> Display the help screen diff --git a/src/swtpm_cert/ek-cert.c b/src/swtpm_cert/ek-cert.c index 6fa477abe..c6dfa6c93 100644 --- a/src/swtpm_cert/ek-cert.c +++ b/src/swtpm_cert/ek-cert.c @@ -69,6 +69,8 @@ enum cert_type_t { CERT_TYPE_EK = 1, CERT_TYPE_PLATFORM, CERT_TYPE_AIK, + CERT_TYPE_IAK, + CERT_TYPE_IDEVID, }; /* some flags */ @@ -132,9 +134,11 @@ usage(const char *prg) " -1 for no expiration\n" "--pem : Write certificate in PEM format; default is DER\n" "--type : The type of certificate to create; default is ek\n" + " Other options are platform, iak, idevid\n" "--tpm-manufacturer : The name of the TPM manufacturer\n" "--tpm-model : The TPM model (part number)\n" "--tpm-version : The TPM version (firmware version)\n" + "--tpm-serial-num : The TPM serial number; required for IAK and IDevID\n" "--platform-manufacturer : The name of the Platform manufacturer\n" "--platform-model : The Platform model (part number)\n" "--platform-version : The Platform version (firmware version)\n" @@ -802,6 +806,59 @@ create_tpm_specification_info(const char *spec_family, return err; } +static int +create_iak_info(gnutls_datum_t *asn1, + const char *hwSerialNum) +{ + asn1_node at = NULL; + int err; + + err = asn_init(); + if (err != ASN1_SUCCESS) { + goto cleanup; + } + + err = asn1_create_element(_tpm_asn, "TPM.TPMIAKSanInfo", &at); + if (err != ASN1_SUCCESS) { + fprintf(stderr, "1cia. asn1_create_element error: %d\n", err); + goto cleanup; + } + + err = asn1_write_value(at, "tpmIAKSanInfoSeq.id", "1.3.6.1.5.5.7.8.4", 0); + if (err != ASN1_SUCCESS) { + fprintf(stderr, "2cia. asn1_write_value error: %d\n", err); + goto cleanup; + } + + err = asn1_write_value(at, "tpmIAKSanInfoSeq.iakSanInfoSet.hwType", "2.23.133.1.2", 0); + if (err != ASN1_SUCCESS) { + fprintf(stderr, "3cia. asn1_write_value error: %d\n", err); + goto cleanup; + } + + err = asn1_write_value(at, "tpmIAKSanInfoSeq.iakSanInfoSet.hwSerialNum", hwSerialNum, 0); + if (err != ASN1_SUCCESS) { + fprintf(stderr, "4cia. asn1_write_value error: %d\n", err); + goto cleanup; + } + + err = encode_asn1(asn1, at); + +#if 0 + fprintf(stderr, "size=%d\n", asn1->size); + unsigned int i = 0; + for (i = 0; i < asn1->size; i++) { + fprintf(stderr, "%02x ", asn1->data[i]); + } + fprintf(stderr, "\n"); +#endif + + cleanup: + asn1_delete_structure(&at); + return err; +} + + static int create_cert_extended_key_usage(const char *oid, gnutls_datum_t *asn1) { @@ -992,6 +1049,8 @@ static void capabilities_print_json(void) "\"features\": [ " "\"cmdarg-signkey-pwd\"" ", \"cmdarg-parentkey-pwd\"" + ", \"cmdarg-tpm-serial-num\"" + ", \"supports-iak-idevid\"" " ], " "\"version\": \"" VERSION "\" " "}\n"); @@ -1041,6 +1100,7 @@ main(int argc, char *argv[]) const char *tpm_manufacturer = NULL; const char *tpm_version = NULL; const char *tpm_model = NULL; + const char *tpm_serial_num = NULL; const char *platf_manufacturer = NULL; const char *platf_version = NULL; const char *platf_model = NULL; @@ -1071,6 +1131,7 @@ main(int argc, char *argv[]) {"tpm-manufacturer", required_argument, NULL, '1'}, {"tpm-model", required_argument, NULL, '2'}, {"tpm-version", required_argument, NULL, '3'}, + {"tpm-serial-num", required_argument, NULL, '0'}, {"platform-manufacturer", required_argument, NULL, '4'}, {"platform-model", required_argument, NULL, '5'}, {"platform-version", required_argument, NULL, '6'}, @@ -1190,6 +1251,10 @@ main(int argc, char *argv[]) certtype = CERT_TYPE_EK; } else if (!strcasecmp(optarg, "platform")) { certtype = CERT_TYPE_PLATFORM; + } else if (!strcasecmp(optarg, "iak")) { + certtype = CERT_TYPE_IAK; + } else if (!strcasecmp(optarg, "idevid")) { + certtype = CERT_TYPE_IDEVID; } else { fprintf(stderr, "Unknown certificate type '%s'.\n", optarg); @@ -1205,6 +1270,9 @@ main(int argc, char *argv[]) case '3': /* --tpm-version */ tpm_version = optarg; break; + case '0': /* --tpm-serial-num */ + tpm_serial_num = optarg; + break; case '4': /* --platform-manufacturer */ platf_manufacturer = optarg; break; @@ -1317,6 +1385,13 @@ main(int argc, char *argv[]) break; case CERT_TYPE_AIK: break; + case CERT_TYPE_IAK: + case CERT_TYPE_IDEVID: + if (tpm_serial_num == NULL) { + fprintf(stderr, "--tpm-serial-num must be provided\n"); + goto cleanup; + } + break; } switch (certtype) { @@ -1340,6 +1415,8 @@ main(int argc, char *argv[]) } break; case CERT_TYPE_AIK: + case CERT_TYPE_IAK: + case CERT_TYPE_IDEVID: break; } @@ -1501,6 +1578,10 @@ if (_err != GNUTLS_E_SUCCESS) { \ case CERT_TYPE_AIK: oid = "1.2.840.113549.1.1.1"; break; + case CERT_TYPE_IAK: + case CERT_TYPE_IDEVID: + oid = NULL; + break; default: fprintf(stderr, "Internal error: unhandle case in line %d\n", __LINE__); @@ -1551,6 +1632,14 @@ if (_err != GNUTLS_E_SUCCESS) { \ break; case CERT_TYPE_AIK: break; + case CERT_TYPE_IAK: + case CERT_TYPE_IDEVID: + err = create_iak_info(&datum, tpm_serial_num); + if (err) { + fprintf(stderr, "Could not create IAK info"); + goto cleanup; + } + break; default: fprintf(stderr, "Internal error: unhandle case in line %d\n", __LINE__); @@ -1558,9 +1647,18 @@ if (_err != GNUTLS_E_SUCCESS) { \ } if (datum.size > 0) { - err = prepend_san_asn1_header(&datum); - CHECK_GNUTLS_ERROR(err, "Could not prepend SAN ASN.1 header: %s\n", - gnutls_strerror(err)) + switch (certtype) { + case CERT_TYPE_EK: + case CERT_TYPE_PLATFORM: + err = prepend_san_asn1_header(&datum); + CHECK_GNUTLS_ERROR(err, "Could not prepend SAN ASN.1 header: %s\n", + gnutls_strerror(err)) + break; + case CERT_TYPE_AIK: + case CERT_TYPE_IAK: + case CERT_TYPE_IDEVID: + break; + } err = gnutls_x509_crt_set_extension_by_oid(crt, GNUTLS_X509EXT_OID_SAN, datum.data, datum.size, @@ -1589,6 +1687,8 @@ if (_err != GNUTLS_E_SUCCESS) { \ break; case CERT_TYPE_PLATFORM: case CERT_TYPE_AIK: + case CERT_TYPE_IAK: + case CERT_TYPE_IDEVID: break; default: fprintf(stderr, "Internal error: unhandled case in line %d\n", @@ -1641,6 +1741,8 @@ if (_err != GNUTLS_E_SUCCESS) { \ } break; case CERT_TYPE_AIK: + case CERT_TYPE_IAK: + case CERT_TYPE_IDEVID: key_usage = GNUTLS_KEY_DIGITAL_SIGNATURE; break; default: @@ -1663,6 +1765,8 @@ if (_err != GNUTLS_E_SUCCESS) { \ oid = "2.23.133.8.2"; break; case CERT_TYPE_AIK: + case CERT_TYPE_IAK: + case CERT_TYPE_IDEVID: break; default: fprintf(stderr, "Internal error: unhandled case in line %d\n", diff --git a/src/swtpm_cert/tpm.asn b/src/swtpm_cert/tpm.asn index 195686ec1..29675e655 100644 --- a/src/swtpm_cert/tpm.asn +++ b/src/swtpm_cert/tpm.asn @@ -113,4 +113,20 @@ TPMEKCertExtendedKeyUsage ::= SEQUENCE { id OBJECT IDENTIFIER } +-- IAK -- + +TPMIAKSanInfo ::= SEQUENCE { + tpmIAKSanInfoSeq [0] IMPLICIT TPMIAKSanInfoSeq +} + +TPMIAKSanInfoSeq ::= SEQUENCE { + id OBJECT IDENTIFIER, + iakSanInfoSet [0] EXPLICIT IAKHardwareModuleName +} + +IAKHardwareModuleName ::= SEQUENCE { + hwType OBJECT IDENTIFIER, + hwSerialNum OCTET STRING +} + END diff --git a/src/swtpm_cert/tpm_asn1.h b/src/swtpm_cert/tpm_asn1.h index 9e7fc0d36..784ee3803 100644 --- a/src/swtpm_cert/tpm_asn1.h +++ b/src/swtpm_cert/tpm_asn1.h @@ -92,7 +92,17 @@ const asn1_static_node tpm_asn1_tab[] = { { "version", 34, NULL }, { "PlatformCertificateSAN", 1610612747, NULL }, { NULL, 13, NULL }, - { "TPMEKCertExtendedKeyUsage", 536870917, NULL }, + { "TPMEKCertExtendedKeyUsage", 1610612741, NULL }, { "id", 12, NULL }, + { "TPMIAKSanInfo", 1610612741, NULL }, + { "tpmIAKSanInfoSeq", 536879106, "TPMIAKSanInfoSeq"}, + { NULL, 4104, "0"}, + { "TPMIAKSanInfoSeq", 1610612741, NULL }, + { "id", 1073741836, NULL }, + { "iakSanInfoSet", 536879106, "IAKHardwareModuleName"}, + { NULL, 2056, "0"}, + { "IAKHardwareModuleName", 536870917, NULL }, + { "hwType", 1073741836, NULL }, + { "hwSerialNum", 7, NULL }, { NULL, 0, NULL } }; diff --git a/src/swtpm_localca/swtpm_localca.c b/src/swtpm_localca/swtpm_localca.c index 920c3e6c9..7aa2bc92f 100644 --- a/src/swtpm_localca/swtpm_localca.c +++ b/src/swtpm_localca/swtpm_localca.c @@ -370,8 +370,8 @@ static int create_cert(unsigned long flags, const gchar *typ, const gchar *direc const gchar **tpm_attr_params, const gchar *signkey, const gchar *signkey_password, const gchar *issuercert, const gchar *parentkey_password, const gchar **swtpm_cert_env, - const gchar *certserial, const gchar *lockfile, - const gchar *optsfile) + const gchar *certserial, const gchar *tpm_serial_num, + const gchar *lockfile, const gchar *optsfile) { gchar ** optsfile_lines = NULL; g_autofree const gchar **options = NULL; @@ -393,6 +393,7 @@ static int create_cert(unsigned long flags, const gchar *typ, const gchar *direc g_autofree gchar *parentkey_pwd_file = NULL; g_autofree gchar *parentkey_pwd_file_param = NULL; gboolean success; + g_autofree gchar *tmp_typ = g_strdup(typ); g_autofree gchar *standard_output = NULL; g_autofree gchar *standard_error = NULL; g_autofree gchar *swtpm_cert_path = NULL; @@ -432,10 +433,21 @@ static int create_cert(unsigned long flags, const gchar *typ, const gchar *direc g_strfreev(split); } - if (vmid != NULL) - subject = g_strdup_printf("CN=%s", vmid); - else - subject = g_strdup("CN=unknown"); + if (strcmp(typ, "ek") == 0 || strcmp(typ, "platform") == 0) { + subject = g_strdup_printf("CN=%s", + vmid ? vmid : "unknown"); + } else if (strcmp(typ, "iak") == 0 || strcmp(typ, "idevid") == 0) { + subject = g_strdup_printf("serialNumber=%s", + vmid ? vmid : "unknown"); + } + + if ((flags & SETUP_TPM2_F) && tpm_serial_num) + options = concat_arrays(options, + (const gchar *[]){ + "--tpm-serial-num", + tpm_serial_num, + NULL + }, TRUE); if (flags & SETUP_TPM2_F) options = concat_arrays(options, (const gchar *[]){"--tpm2", NULL}, TRUE); @@ -502,11 +514,13 @@ static int create_cert(unsigned long flags, const gchar *typ, const gchar *direc cmd = concat_arrays(cmd, tpm_attr_params, TRUE); - if (strcmp(typ, "platform") == 0) { - certfile = g_strjoin(G_DIR_SEPARATOR_S, directory, "platform.cert", NULL); + if (strcmp(typ, "platform") == 0 || strcmp(typ, "iak") == 0 || strcmp(typ, "idevid") == 0) { + g_autofree gchar *certfn = g_strconcat(typ, ".cert", NULL); + + certfile = g_strjoin(G_DIR_SEPARATOR_S, directory, certfn, NULL); cmd = concat_arrays(cmd, (const gchar *[]){ - "--type", "platform", + "--type", tmp_typ, "--out-cert", certfile, NULL}, TRUE); @@ -531,7 +545,7 @@ static int create_cert(unsigned long flags, const gchar *typ, const gchar *direc if (strcmp(typ, "ek") == 0) certtype = "EK"; else - certtype = "platform"; + certtype = typ; #if 0 { g_autofree gchar *join = g_strjoinv(" ", cmd); @@ -593,6 +607,7 @@ static void usage(const char *prgname) "--tpm-manufacturer s The manufacturer of the TPM; e.g., id:00001014\n" "--tpm-model s The model of the TPM; e.g., 'swtpm'\n" "--tpm-version i The (firmware) version of the TPM; e.g., id:20160511\n" + "--tpm-serial-num s The string representing the serial number of the TPM\n" "--tpm2 Generate a certificate for a TPM 2\n" "--allow-signing The TPM 2's EK can be used for signing\n" "--decryption The TPM 2's EK can be used for decryption\n" @@ -623,6 +638,7 @@ int main(int argc, char *argv[]) {"tpm-manufacturer", required_argument, NULL, 'a'}, {"tpm-model", required_argument, NULL, 'm'}, {"tpm-version", required_argument, NULL, 's'}, + {"tpm-serial-num", required_argument, NULL, 'S'}, {"tpm2", no_argument, NULL, '2'}, {"allow-signing", no_argument, NULL, 'i'}, {"decryption", no_argument, NULL, 'y'}, @@ -645,6 +661,7 @@ int main(int argc, char *argv[]) g_autofree gchar *parentkey_password = NULL; g_autofree gchar *issuercert = NULL; g_autofree gchar *certserial = NULL; + g_autofree gchar *tpm_serial_num = NULL; gchar **tpm_spec_params = NULL; gchar **tpm_attr_params = NULL; gchar **config_file_lines = NULL; @@ -712,6 +729,10 @@ int main(int argc, char *argv[]) NULL }, TRUE); break; + case 'S': /* --tpm-serial-num */ + g_free(tpm_serial_num); + tpm_serial_num = g_strdup(optarg); + break; case '2': /* --tpm2 */ flags |= SETUP_TPM2_F; break; @@ -913,7 +934,8 @@ int main(int argc, char *argv[]) ret = create_cert(flags, typ, directory, key_params, vmid, (const char **)tpm_spec_params, (const char **)tpm_attr_params, signkey, signkey_password, issuercert, parentkey_password, - (const char **)swtpm_cert_env, certserial, lockfile, optsfile); + (const char **)swtpm_cert_env, certserial, tpm_serial_num, + lockfile, optsfile); out: error: diff --git a/src/swtpm_setup/Makefile.am b/src/swtpm_setup/Makefile.am index 1a8ce0887..557c7ad79 100644 --- a/src/swtpm_setup/Makefile.am +++ b/src/swtpm_setup/Makefile.am @@ -39,7 +39,8 @@ swtpm_setup_LDFLAGS = \ $(HARDENING_LDFLAGS) \ $(GLIB_LIBS) \ $(JSON_GLIB_LIBS) \ - $(LIBCRYPTO_LIBS) + $(LIBCRYPTO_LIBS) \ + $(GNUTLS_LIBS) swtpm_setup_CFLAGS = \ -I$(top_builddir)/include \ @@ -51,7 +52,8 @@ swtpm_setup_CFLAGS = \ $(CFLAGS) \ $(HARDENING_CFLAGS) \ $(GLIB_CFLAGS) \ - $(JSON_GLIB_CFLAGS) + $(JSON_GLIB_CFLAGS) \ + $(GNUTLS_CFLAGS) EXTRA_DIST = \ README diff --git a/src/swtpm_setup/swtpm.c b/src/swtpm_setup/swtpm.c index 0d5ecdb31..466db1a86 100644 --- a/src/swtpm_setup/swtpm.c +++ b/src/swtpm_setup/swtpm.c @@ -418,6 +418,7 @@ static const struct swtpm_cops swtpm_cops = { #define TPM2_ALG_SHA3_512 0x0029 #define TPM2_ALG_NULL 0x0010 #define TPM2_ALG_SM3 0x0012 +#define TPM2_ALG_ECDSA 0x0018 #define TPM2_ALG_ECC 0x0023 #define TPM2_ALG_CFB 0x0043 @@ -447,6 +448,9 @@ static const struct swtpm_cops swtpm_cops = { #define TPM2_NV_INDEX_ECC_SECP384R1_HI_EKCERT 0x01c00016 #define TPM2_NV_INDEX_ECC_SECP384R1_HI_EKTEMPLATE 0x01c00017 +#define TPM2_NV_INDEX_IDEVID_SHA384 0x01c90011 +#define TPM2_NV_INDEX_IAK_SHA384 0x01c90019 + #define TPM2_EK_RSA_HANDLE 0x81010001 #define TPM2_EK_RSA3072_HANDLE 0x8101001c #define TPM2_EK_ECC_SECP384R1_HANDLE 0x81010016 @@ -982,7 +986,8 @@ static int swtpm_tpm2_createprimary_ecc(struct swtpm *self, uint32_t primaryhand const unsigned char *authpolicy, size_t authpolicy_len, const unsigned char *schemedata, size_t schemedata_len, unsigned short curveid, unsigned short hashalg, - const unsigned char *nonce, size_t nonce_len, + const unsigned char *nonce1, size_t nonce1_len, + const unsigned char *nonce2, size_t nonce2_len, size_t off, uint32_t *curr_handle, unsigned char *ektemplate, size_t *ektemplate_len, gchar **ekparam, const gchar **key_description) @@ -1008,8 +1013,8 @@ static int swtpm_tpm2_createprimary_ecc(struct swtpm *self, uint32_t primaryhand authpolicy, authpolicy_len, symkeydata, symkeydata_len, schemedata, schemedata_len, - nonce, nonce_len, - nonce, nonce_len, + nonce1, nonce1_len, + nonce2, nonce2_len, NULL); if (public_len < 0) { logerr(self->logfile, "Internal error in %s: memconcat failed\n", __func__); @@ -1044,6 +1049,17 @@ static int swtpm_tpm2_createprimary_ecc(struct swtpm *self, uint32_t primaryhand tpmresp, &tpmresp_len, TPM2_DURATION_LONG); if (ret != 0) return 1; +#if 0 + { + size_t x; + for (x = 0; x < tpmresp_len; x++) { + if (x % 0x10 == 0) + printf("\n"); + printf("%02x ", tpmresp[x]); + } + printf("\n"); + } +#endif if (curr_handle) { if (tpmresp_len < 10 + sizeof(*curr_handle)) goto err_too_short; @@ -1109,11 +1125,97 @@ static int swtpm_tpm2_createprimary_spk_ecc_nist_p384(struct swtpm *self, size_t schemedata_len = sizeof(schemedata); size_t off = 42; + /* per "TCG TPM v2.0 Provisioning Guidance v1.0" page 37 + * -> "Ek Credential Profile 2.0" rev.14 section 2.1.5.2: + * template for NIST P256 uses 2 identical 32-byte all-zero nonces + * -> Use two 48-byte all-zero nonces for NIST P384. + */ return swtpm_tpm2_createprimary_ecc(self, TPM2_RH_OWNER, keyflags, symkeydata, symkeydata_len, authpolicy, authpolicy_len, schemedata, schemedata_len, TPM2_ECC_NIST_P384, TPM2_ALG_SHA384, - NONCE_ECC_384, sizeof(NONCE_ECC_384), off, curr_handle, - NULL, 0, NULL, NULL); + NONCE_ECC_384, sizeof(NONCE_ECC_384), + NONCE_ECC_384, sizeof(NONCE_ECC_384), + off, curr_handle, NULL, 0, NULL, NULL); +} + +static int createprimary_iak_idevid_ecc_nist_p384(struct swtpm *self, + unsigned int keyflags, + const unsigned char *authpolicy, size_t authpolicy_len, + const unsigned char *id, size_t id_len, + size_t off, uint32_t *curr_handle, + gchar **keyparam, const gchar **key_description) +{ + const unsigned char symkeydata[] = {AS2BE(TPM2_ALG_NULL)}; + size_t symkeydata_len = sizeof(symkeydata); + const unsigned char schemedata[] = { + /* TPMS_ECC_PARAMS: TPMT_ECC_SCHEME .. TPMT_KDF_SCHEME */ + AS2BE(TPM2_ALG_ECDSA), + AS2BE(TPM2_ALG_SHA384), // hashAlg + AS2BE(TPM2_ECC_NIST_P384), // curveID + AS2BE(TPM2_ALG_NULL), // kdf->scheme + }; + size_t schemedata_len = sizeof(schemedata); + + /* FIXME: unclear specification about nonces on ECCs */ + return swtpm_tpm2_createprimary_ecc(self, TPM2_RH_ENDORSEMENT, keyflags, + symkeydata, symkeydata_len, + authpolicy, authpolicy_len, + schemedata, schemedata_len, + TPM2_ECC_NIST_P384, TPM2_ALG_SHA384, + id /* nonce1 */, id_len, + id /* nonce2 */, id_len, + off, curr_handle, + NULL, 0, keyparam, key_description); +} + +static int +swtpm_tpm2_createprimary_idevid_ecc_nist_p384(struct swtpm *self, + uint32_t *curr_handle, + gchar **keyparam, + const gchar **key_description) +{ + const unsigned char authpolicy[48] = { + /* table 19: signing */ + 0x4d, 0xb1, 0xaa, 0x83, 0x6d, 0x0b, 0x56, 0x15, 0xdf, 0x6e, 0xe5, 0x3a, + 0x40, 0xef, 0x70, 0xc6, 0x1c, 0x21, 0x7f, 0x43, 0x03, 0xd4, 0x46, 0x95, + 0x92, 0x59, 0x72, 0xbc, 0x92, 0x70, 0x06, 0xcf, 0xa5, 0xcb, 0xdf, 0x6d, + 0xc1, 0x8c, 0x4d, 0xbe, 0x32, 0x9b, 0x2f, 0x15, 0x42, 0xc3, 0xdd, 0x33 + }; + size_t authpolicy_len = sizeof(authpolicy); + // 7.3.4.1 keyflags: fixedTPM, fixedParent, sensitiveDataOrigin, userWithAuth, + // adminWithPolicy, sign + unsigned int keyflags = 0x000400f2; + const char id[2 + 6] = {AS2BE(6), 0x49, 0x44, 0x45, 0x56, 0x49, 0x44}; /* 7.3.1 Table 2*/ + size_t off = 0x58; + + return createprimary_iak_idevid_ecc_nist_p384(self, keyflags, authpolicy, authpolicy_len, + (const unsigned char *)id, sizeof(id), + off, curr_handle, keyparam, key_description); +} + +static int +swtpm_tpm2_createprimary_iak_ecc_nist_p384(struct swtpm *self, + uint32_t *curr_handle, + gchar **keyparam, + const gchar **key_description) +{ + const unsigned char authpolicy[48] = { + /* table 19: attestation */ + 0x12, 0x9d, 0x94, 0xeb, 0xf8, 0x45, 0x56, 0x65, 0x2c, 0x6e, 0xef, 0x43, + 0xbb, 0xb7, 0x57, 0x51, 0x2a, 0xc8, 0x7e, 0x52, 0xbe, 0x7b, 0x34, 0x9c, + 0xa6, 0xce, 0x4d, 0x82, 0x6f, 0x74, 0x9f, 0xcf, 0x67, 0x2f, 0x51, 0x71, + 0x6c, 0x5c, 0xbb, 0x60, 0x5f, 0x31, 0x3b, 0xf3, 0x45, 0xaa, 0xb3, 0x12 + }; + size_t authpolicy_len = sizeof(authpolicy); + // 7.3.4.1 keyflags: fixedTPM, fixedParent, sensitiveDataOrigin, userWithAuth, + // adminWithPolicy, restricted, sign + unsigned int keyflags = 0x000500f2; + const char id[2 + 3] = {AS2BE(3), 0x49, 0x41, 0x4b}; /* 7.3.1 Table 2 */ + size_t off = 0x58; + + return createprimary_iak_idevid_ecc_nist_p384(self, keyflags, authpolicy, authpolicy_len, + (const unsigned char *)id, sizeof(id), + off, curr_handle, keyparam, key_description); } static int swtpm_tpm2_createprimary_spk_rsa(struct swtpm *self, unsigned int rsa_keysize, @@ -1171,6 +1273,35 @@ static int swtpm_tpm2_create_spk(struct swtpm *self, gboolean isecc, unsigned in return ret; } +static int swtpm_tpm2_create_iak(struct swtpm *self, gchar **ekparam, + const gchar **key_description) +{ + uint32_t curr_handle; + int ret; + + ret = swtpm_tpm2_createprimary_iak_ecc_nist_p384(self, &curr_handle, ekparam, + key_description); + if (ret != 0) + return 1; + + return swtpm_tpm2_flushcontext(self, curr_handle); +} + +static int swtpm_tpm2_create_idevid(struct swtpm *self, gchar **ekparam, + const gchar **key_description) +{ + uint32_t curr_handle; + int ret; + + ret = swtpm_tpm2_createprimary_idevid_ecc_nist_p384(self, &curr_handle, ekparam, + key_description); + if (ret != 0) + return 1; + + return swtpm_tpm2_flushcontext(self, curr_handle); +} + + /* Create an ECC EK key that may be allowed to sign and/or decrypt */ static int swtpm_tpm2_createprimary_ek_ecc_nist_p384(struct swtpm *self, gboolean allowsigning, gboolean decryption, uint32_t *curr_handle, @@ -1222,12 +1353,17 @@ static int swtpm_tpm2_createprimary_ek_ecc_nist_p384(struct swtpm *self, gboolea off = 90; } + /* TCG EK Credential Profile for TPM 2.0; vers 2.5 rev 2: + * Two zero-byte empty nonces per Template H-3. + */ ret = swtpm_tpm2_createprimary_ecc(self, TPM2_RH_ENDORSEMENT, keyflags, symkeydata, symkeydata_len, authpolicy, authpolicy_len, schemedata, schemedata_len, TPM2_ECC_NIST_P384, TPM2_ALG_SHA384, - NONCE_EMPTY, sizeof(NONCE_EMPTY), off, curr_handle, + NONCE_EMPTY, sizeof(NONCE_EMPTY), + NONCE_EMPTY, sizeof(NONCE_EMPTY), + off, curr_handle, ektemplate, ektemplate_len, ekparam, key_description); if (ret != 0) logerr(self->logfile, "%s failed\n", __func__); @@ -1506,8 +1642,72 @@ static char *swtpm_tpm2_get_active_profile(struct swtpm *self) return result; } +static int swtpm_tpm2_write_iak_cert_nvram(struct swtpm *self, gboolean lock_nvram, + const unsigned char *data, size_t data_len) +{ + uint32_t nvindex = TPM2_NV_INDEX_IAK_SHA384; + uint32_t nvindexattrs = TPMA_NV_PLATFORMCREATE | + TPMA_NV_AUTHREAD | + TPMA_NV_OWNERREAD | + TPMA_NV_PPREAD | + TPMA_NV_PPWRITE | + TPMA_NV_NO_DA | + TPMA_NV_WRITEDEFINE; // FIXME: fix flags? + + return swtpm_tpm2_write_cert_nvram(self, nvindex, nvindexattrs, data, data_len, + lock_nvram, "", "IAK certificate"); +} + +static int swtpm_tpm2_write_idevid_cert_nvram(struct swtpm *self, gboolean lock_nvram, + const unsigned char *data, size_t data_len) +{ + uint32_t nvindex = TPM2_NV_INDEX_IDEVID_SHA384; + uint32_t nvindexattrs = TPMA_NV_PLATFORMCREATE | + TPMA_NV_AUTHREAD | + TPMA_NV_OWNERREAD | + TPMA_NV_PPREAD | + TPMA_NV_PPWRITE | + TPMA_NV_NO_DA | + TPMA_NV_WRITEDEFINE; // FIXME: fix flags? + + return swtpm_tpm2_write_cert_nvram(self, nvindex, nvindexattrs, data, data_len, + lock_nvram, "", "IDevID certificate"); +} + +static int swtpm_tpm2_get_capability(struct swtpm *self, uint32_t cap, uint32_t prop, + uint32_t *res) +{ + struct tpm2_get_capability_req { + struct tpm_req_header hdr; + uint32_t cap; + uint32_t prop; + uint32_t count; + } __attribute__((packed)) req = { + .hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_NO_SESSIONS, sizeof(req), TPM2_CC_GETCAPABILITY), + .cap = htobe32(cap), + .prop = htobe32(prop), + .count = htobe32(1), + }; + unsigned char tpmresp[27]; + size_t tpmresp_len = sizeof(tpmresp); + uint32_t val; + int ret; + + ret = transfer(self, &req, sizeof(req), "TPM2_GetCapability", FALSE, + tpmresp, &tpmresp_len, TPM2_DURATION_SHORT); + if (ret != 0) + return 1; + + memcpy(&val, &tpmresp[23], sizeof(val)); + *res = be32toh(val); + + return 0; +} + static const struct swtpm2_ops swtpm_tpm2_ops = { .shutdown = swtpm_tpm2_shutdown, + .create_iak = swtpm_tpm2_create_iak, + .create_idevid = swtpm_tpm2_create_idevid, .create_spk = swtpm_tpm2_create_spk, .create_ek = swtpm_tpm2_create_ek, .get_all_pcr_banks = swtpm_tpm2_get_all_pcr_banks, @@ -1515,6 +1715,9 @@ static const struct swtpm2_ops swtpm_tpm2_ops = { .write_ek_cert_nvram = swtpm_tpm2_write_ek_cert_nvram, .write_platform_cert_nvram = swtpm_tpm2_write_platform_cert_nvram, .get_active_profile = swtpm_tpm2_get_active_profile, + .write_iak_cert_nvram = swtpm_tpm2_write_iak_cert_nvram, + .write_idevid_cert_nvram = swtpm_tpm2_write_idevid_cert_nvram, + .get_capability = swtpm_tpm2_get_capability, }; /* diff --git a/src/swtpm_setup/swtpm.h b/src/swtpm_setup/swtpm.h index ed8d3d4a7..93f6f2ba2 100644 --- a/src/swtpm_setup/swtpm.h +++ b/src/swtpm_setup/swtpm.h @@ -45,6 +45,8 @@ struct swtpm12_ops { /* TPM 2 specific ops */ struct swtpm2_ops { int (*shutdown)(struct swtpm *); + int (*create_iak)(struct swtpm *self, gchar **ekparam, const gchar **key_description); + int (*create_idevid)(struct swtpm *self, gchar **ekparam, const gchar **key_description); int (*create_spk)(struct swtpm *self, gboolean isecc, unsigned int rsa_keysize); int (*create_ek)(struct swtpm *self, gboolean isecc, unsigned int rsa_keysize, gboolean allowsigning, gboolean decryption, gboolean lock_nvram, @@ -57,6 +59,11 @@ struct swtpm2_ops { int (*write_platform_cert_nvram)(struct swtpm *self, gboolean lock_nvram, const unsigned char *data, size_t data_len); char *(*get_active_profile)(struct swtpm *self); + int (*write_iak_cert_nvram)(struct swtpm *self, gboolean lock_nvram, + const unsigned char *data, size_t data_len); + int (*write_idevid_cert_nvram)(struct swtpm *self, gboolean lock_nvram, + const unsigned char *data, size_t data_len); + int (*get_capability)(struct swtpm *self, uint32_t cap, uint32_t prop, uint32_t *res); }; /* common structure for swtpm object */ diff --git a/src/swtpm_setup/swtpm_setup.c b/src/swtpm_setup/swtpm_setup.c index ff7a4aa10..bfcab9eb6 100644 --- a/src/swtpm_setup/swtpm_setup.c +++ b/src/swtpm_setup/swtpm_setup.c @@ -32,10 +32,13 @@ #include +#include + #include "profile.h" #include "swtpm.h" #include "swtpm_conf.h" #include "swtpm_utils.h" +#include "swtpm_setup.h" #include "swtpm_setup_utils.h" #include @@ -62,6 +65,8 @@ #define SETUP_WRITE_EK_CERT_FILES_F (1 << 15) #define SETUP_RECONFIGURE_F (1 << 16) #define SETUP_RSA_KEYSIZE_BY_USER_F (1 << 17) +#define SETUP_IAK_F (1 << 18) +#define SETUP_IDEVID_F (1 << 19) /* default configuration file */ #define SWTPM_SETUP_CONF "swtpm_setup.conf" @@ -79,6 +84,8 @@ static const struct flag_to_certfile { } flags_to_certfiles[] = { {.flag = SETUP_EK_CERT_F , .filename = "ek.cert", .type = "ek" }, {.flag = SETUP_PLATFORM_CERT_F, .filename = "platform.cert", .type = "platform" }, + {.flag = SETUP_IAK_F, .filename = "iak.cert", .type = "iak" }, + {.flag = SETUP_IDEVID_F, .filename = "idevid.cert", .type = "idevid" }, {.flag = 0, .filename = NULL, .type = NULL}, }; @@ -179,7 +186,8 @@ static int tpm_get_specs_and_attributes(struct swtpm *swtpm, gchar ***params) /* Call an external tool to create the certificates */ static int call_create_certs(unsigned long flags, unsigned int cert_flags, const gchar *configfile, const gchar *certsdir, - const gchar *ekparam, const gchar *vmid, struct swtpm *swtpm) + const gchar *key_params, const gchar *vmid, + const gchar *tpm_serial_num, struct swtpm *swtpm) { gchar **config_file_lines = NULL; /* must free */ g_autofree gchar *create_certs_tool = NULL; @@ -188,6 +196,7 @@ static int call_create_certs(unsigned long flags, unsigned int cert_flags, g_autofree const gchar **cmd = NULL; gchar **params = NULL; /* must free */ g_autofree gchar *prgname = NULL; + const char *key_opt = "--key"; gboolean success; gint exit_status; size_t idx, j; @@ -223,10 +232,15 @@ static int call_create_certs(unsigned long flags, unsigned int cert_flags, NULL }, TRUE); } + + /* use the old --ek option when the ek is passed */ + if (cert_flags & (SETUP_EK_CERT_F | SETUP_PLATFORM_CERT_F)) + key_opt = "--ek"; + cmd = concat_arrays((const gchar*[]) { create_certs_tool_path, "--type", "_", /* '_' must be at index '2' ! */ - "--ek", ekparam, + key_opt, key_params, "--dir", certsdir, NULL }, NULL, FALSE); @@ -239,6 +253,8 @@ static int call_create_certs(unsigned long flags, unsigned int cert_flags, cmd = concat_arrays(cmd, (const gchar*[]){"--configfile", create_certs_tool_config, NULL}, TRUE); if (create_certs_tool_options != NULL) cmd = concat_arrays(cmd, (const gchar*[]){"--optsfile", create_certs_tool_options, NULL}, TRUE); + if (tpm_serial_num) /* required for IAK & IDevID */ + cmd = concat_arrays(cmd, (const gchar*[]){"--tpm-serial-num", tpm_serial_num, NULL}, TRUE); s = g_strrstr(create_certs_tool, G_DIR_SEPARATOR_S); if (s) @@ -358,6 +374,52 @@ static int read_certificate_file(const gchar *certsdir, const gchar *filename, return read_file(*certfile, filecontent, filecontent_len); } +/* data extracted from EK certificate */ +struct ek_certificate_data { + unsigned char id[64]; + size_t id_len; + unsigned char serial[20]; + size_t serial_len; +}; + +static int tpm2_extract_certificate_data(gchar *certdata, size_t certdata_len, + struct ek_certificate_data *ecd) +{ + gnutls_x509_crt_t cert; + gnutls_datum_t data = { + .data = (unsigned char *)certdata, + .size = certdata_len, + }; + int err; + int ret = 1; + + if ((err = gnutls_x509_crt_init(&cert)) < 0) { + logerr(gl_LOGFILE, "gnutls_x509_crt_init() failed: %s\n", + gnutls_strerror(err)); + return 1; + } + if ((err = gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_DER)) < 0) { + logerr(gl_LOGFILE, "gnutls_x509_crt_import() failed: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + if ((err = gnutls_x509_crt_get_authority_key_id(cert, ecd->id, &ecd->id_len, NULL)) < 0) { + logerr(gl_LOGFILE, "gnutls_x509_crt_get_authority_key_id() failed: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + if ((err = gnutls_x509_crt_get_serial(cert, ecd->serial, &ecd->serial_len)) < 0) { + logerr(gl_LOGFILE, "gnutls_x509_crt_get_serial() failed: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + ret = 0; + +cleanup: + gnutls_x509_crt_deinit(cert); + return ret; +} + /* * Read the certificate from the file where swtpm_cert left it. * Write the file into the TPM's NVRAM and, if the user wants it, @@ -367,11 +429,13 @@ static int tpm2_persist_certificate(unsigned long flags, const gchar *certsdir, const struct flag_to_certfile *ftc, unsigned int rsa_keysize, struct swtpm2 *swtpm2, const gchar *user_certsdir, const gchar *key_type, - const gchar *key_description) + const gchar *key_description, + struct ek_certificate_data *ecd) { g_autofree gchar *filecontent = NULL; g_autofree gchar *certfile = NULL; size_t filecontent_len; + gboolean preserve; int ret; ret = read_certificate_file(certsdir, ftc->filename, @@ -379,7 +443,21 @@ static int tpm2_persist_certificate(unsigned long flags, const gchar *certsdir, if (ret != 0) goto error_unlink; - if (ftc->flag == SETUP_EK_CERT_F) { + if (ecd) { + ret = tpm2_extract_certificate_data(filecontent, filecontent_len, ecd); + if (ret != 0) + goto error_unlink; + } + + if (ftc->flag == SETUP_IAK_F) { + ret = swtpm2->ops->write_iak_cert_nvram(&swtpm2->swtpm, + !!(flags & SETUP_LOCK_NVRAM_F), + (const unsigned char*)filecontent, filecontent_len); + } else if (ftc->flag == SETUP_IDEVID_F) { + ret = swtpm2->ops->write_idevid_cert_nvram(&swtpm2->swtpm, + !!(flags & SETUP_LOCK_NVRAM_F), + (const unsigned char *)filecontent, filecontent_len); + } else if (ftc->flag == SETUP_EK_CERT_F) { ret = swtpm2->ops->write_ek_cert_nvram(&swtpm2->swtpm, !!(flags & SETUP_TPM2_ECC_F), rsa_keysize, !!(flags & SETUP_LOCK_NVRAM_F), @@ -393,7 +471,9 @@ static int tpm2_persist_certificate(unsigned long flags, const gchar *certsdir, if (ret != 0) goto error_unlink; - return certfile_move_or_delete(flags, !!(ftc->flag & SETUP_EK_CERT_F), + preserve = !!(ftc->flag & (SETUP_EK_CERT_F | SETUP_IAK_F | SETUP_IDEVID_F)); + + return certfile_move_or_delete(flags, preserve, certfile, user_certsdir, key_type, key_description); @@ -406,10 +486,12 @@ static int tpm2_persist_certificate(unsigned long flags, const gchar *certsdir, static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file, const gchar *certsdir, const gchar *vmid, unsigned int rsa_keysize, struct swtpm2 *swtpm2, - const gchar *user_certsdir) + const gchar *user_certsdir, + struct ek_certificate_data *ecd) { + g_autofree gchar *key_params = NULL; + struct ek_certificate_data *ecd_dup; const char *key_description = ""; - g_autofree gchar *ekparam = NULL; unsigned long cert_flags; const gchar *key_type; size_t idx; @@ -420,7 +502,7 @@ static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file !!(flags & SETUP_ALLOW_SIGNING_F), !!(flags & SETUP_DECRYPTION_F), !!(flags & SETUP_LOCK_NVRAM_F), - &ekparam, &key_description); + &key_params, &key_description); if (ret != 0) return 1; } @@ -428,18 +510,26 @@ static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file /* Only look at ek and platform certs here */ cert_flags = flags & (SETUP_EK_CERT_F | SETUP_PLATFORM_CERT_F); if (cert_flags) { - ret = call_create_certs(flags, cert_flags, config_file, certsdir, ekparam, - vmid, &swtpm2->swtpm); + ret = call_create_certs(flags, cert_flags, config_file, certsdir, key_params, + vmid, NULL, &swtpm2->swtpm); if (ret != 0) return 1; for (idx = 0; flags_to_certfiles[idx].filename; idx++) { if (cert_flags & flags_to_certfiles[idx].flag) { - key_type = flags_to_certfiles[idx].flag & SETUP_EK_CERT_F ? "ek" : ""; + + ecd_dup = NULL; + if (flags_to_certfiles[idx].flag & SETUP_EK_CERT_F) { + key_type = "ek"; + if (rsa_keysize) + ecd_dup = ecd; + } else { + key_type = ""; + } ret = tpm2_persist_certificate(flags, certsdir, &flags_to_certfiles[idx], rsa_keysize, swtpm2, user_certsdir, - key_type, key_description); + key_type, key_description, ecd_dup); if (ret) return 1; } @@ -453,21 +543,102 @@ static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file static int tpm2_create_eks_and_certs(unsigned long flags, const gchar *config_file, const gchar *certsdir, const gchar *vmid, unsigned int rsa_keysize, struct swtpm2 *swtpm2, - const gchar *user_certsdir) + const gchar *user_certsdir, + struct ek_certificate_data *ecd) { int ret; /* 1st key will be RSA */ flags = flags & ~SETUP_TPM2_ECC_F; ret = tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2, - user_certsdir); + user_certsdir, ecd); if (ret != 0) return 1; /* 2nd key will be an ECC; no more platform cert */ flags = (flags & ~SETUP_PLATFORM_CERT_F) | SETUP_TPM2_ECC_F; return tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2, - user_certsdir); + user_certsdir, NULL); +} + +static gchar *tpm2_create_tpm_serial_num(struct swtpm2 *swtpm2, const struct ek_certificate_data *ecd) +{ + struct swtpm *swtpm = &swtpm2->swtpm; + g_autofree gchar *cert_ser = NULL; + g_autofree gchar *ca_akid = NULL; + uint32_t res; + char code[sizeof(res) + 1]; + size_t i; + int ret; + + ret = swtpm2->ops->get_capability(swtpm, TPM_CAP_TPM_PROPERTIES, TPM_PT_MANUFACTURER, &res); + if (ret != 0) { + logerr(gl_LOGFILE, "TPM_GetCapability failed\n"); + return NULL; + } + + ca_akid = print_as_hex(ecd->id, ecd->id_len); + cert_ser = print_as_hex(ecd->serial, ecd->serial_len); + for (i = 0; i < sizeof(res); i++) + code[i] = res >> (8 * (3 - i)); + code[4] = 0; + + return g_strdup_printf("%s:%s:%s", code, ca_akid, cert_ser); +} + +/* Create the IAK and cert */ +static int tpm2_create_iak_idevid_and_certs(unsigned long flags, const gchar *config_file, + const gchar *certsdir, const char *vmid, + struct swtpm2 *swtpm2, const gchar *user_certsdir, + const struct ek_certificate_data *ecd) +{ + g_autofree gchar *tpm_serial_num = NULL; + g_autofree gchar *key_params = NULL; + const char *key_description; + const char *key_type = NULL; + unsigned long cert_flags; + size_t idx; + int ret; + + /* Only look at IAK and IDevID certs here */ + cert_flags = flags & (SETUP_IAK_F | SETUP_IDEVID_F); + if (!cert_flags) + return 0; + + tpm_serial_num = tpm2_create_tpm_serial_num(swtpm2, ecd); + + for (idx = 0; flags_to_certfiles[idx].filename; idx++) { + if (cert_flags & flags_to_certfiles[idx].flag) { + + SWTPM_G_FREE(key_params); + + if (flags_to_certfiles[idx].flag == SETUP_IAK_F) { + key_type = "iak"; + ret = swtpm2->ops->create_iak(&swtpm2->swtpm, &key_params, &key_description); + } else if (flags_to_certfiles[idx].flag == SETUP_IDEVID_F) { + key_type = "idevid"; + ret = swtpm2->ops->create_idevid(&swtpm2->swtpm, &key_params, &key_description); + } else { + continue; + } + if (ret != 0) + return 1; + + ret = call_create_certs(flags, flags_to_certfiles[idx].flag, config_file, + certsdir, key_params, vmid, tpm_serial_num, + &swtpm2->swtpm); + if (ret != 0) + return 1; + + ret = tpm2_persist_certificate(flags, certsdir, &flags_to_certfiles[idx], + 0, swtpm2, user_certsdir, + key_type, key_description, NULL); + if (ret) + return 1; + } + } + + return 0; } /* Get the default PCR banks from the config file and if nothing can @@ -603,6 +774,10 @@ static int init_tpm2(unsigned long flags, gchar **swtpm_prg_l, const gchar *conf const gchar *user_certsdir, const gchar *json_profile, int json_profile_fd, const gchar *profile_remove_disabled_param) { + struct ek_certificate_data ecd = { + .id_len = sizeof(ecd.id), + .serial_len = sizeof(ecd.serial), + }; struct swtpm2 *swtpm2; struct swtpm *swtpm; int ret; @@ -632,7 +807,12 @@ static int init_tpm2(unsigned long flags, gchar **swtpm_prg_l, const gchar *conf } ret = tpm2_create_eks_and_certs(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2, - user_certsdir); + user_certsdir, &ecd); + if (ret != 0) + goto destroy; + + ret = tpm2_create_iak_idevid_and_certs(flags, config_file, certsdir, vmid, + swtpm2, user_certsdir, &ecd); if (ret != 0) goto destroy; } @@ -727,7 +907,7 @@ static int tpm12_create_certs(unsigned long flags, const gchar *config_file, cert_flags = flags & (SETUP_EK_CERT_F | SETUP_PLATFORM_CERT_F); ret = call_create_certs(flags, cert_flags, config_file, certsdir, ekparam, - vmid, &swtpm12->swtpm); + vmid, NULL, &swtpm12->swtpm); if (ret != 0) return 1; @@ -1055,6 +1235,8 @@ static void usage(const char *prgname, const char *default_config_file) " check: algorithms are tested.\n" " fips-host: no testing.\n" "\n" + "--no-iak : Do not create IAK and IDevID keys and related certificates.\n" + "\n" "--version : Display version and exit\n" "\n" "--help,-h : Display this help screen\n\n", @@ -1237,7 +1419,7 @@ static int print_capabilities(const char **swtpm_prg_l, gboolean swtpm_has_tpm12 ", \"cmdarg-reconfigure-pcr-banks\"" "%s" ", \"cmdarg-profile\", \"cmdarg-profile-remove-disabled\"" - "" + ", \"cmdarg-no-iak\"" " ], " "\"profiles\": [%s], " "\"version\": \"" VERSION "\" " @@ -1387,6 +1569,7 @@ int main(int argc, char *argv[]) {"profile-file-fd", required_argument, NULL, 'G'}, {"profile-remove-disabled", required_argument, NULL, 'j'}, {"print-profiles", no_argument, NULL, 'M'}, + {"no-iak", no_argument, NULL, 'n'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; @@ -1434,6 +1617,7 @@ int main(int argc, char *argv[]) int fds_to_pass[2] = { -1, -1 }; unsigned n_fds_to_pass = 0; char tmpbuffer[200]; + gboolean no_iak = FALSE; time_t now; struct tm *tm; int ret = 1; @@ -1639,6 +1823,9 @@ int main(int argc, char *argv[]) case 'M': /* --print-profiles */ printprofiles = TRUE; break; + case 'n': /* --no-iak */ + no_iak = TRUE; + break; case '?': case 'h': /* --help */ usage(argv[0], config_file); @@ -1966,6 +2153,8 @@ int main(int argc, char *argv[]) error->message); goto error; } + if (!no_iak) + flags |= SETUP_IAK_F | SETUP_IDEVID_F; } if ((flags & SETUP_TPM2_F) == 0) { diff --git a/src/swtpm_setup/swtpm_setup.h b/src/swtpm_setup/swtpm_setup.h index 913b36f96..f78383779 100644 --- a/src/swtpm_setup/swtpm_setup.h +++ b/src/swtpm_setup/swtpm_setup.h @@ -14,4 +14,7 @@ extern gchar *gl_LOGFILE; +#define TPM_CAP_TPM_PROPERTIES 6 +#define TPM_PT_MANUFACTURER 0x105 + #endif /* SWTPM_SETUP_H */ diff --git a/tests/_test_print_capabilities b/tests/_test_print_capabilities index 8ccf791e8..40b7ea650 100755 --- a/tests/_test_print_capabilities +++ b/tests/_test_print_capabilities @@ -53,7 +53,7 @@ exp='\{ "type": "swtpm_setup", '\ '"tpm12-not-need-root", "cmdarg-write-ek-cert-files", "cmdarg-create-config-files", '\ '"cmdarg-reconfigure-pcr-banks"'\ '(, "tpm2-rsa-keysize-2048")?(, "tpm2-rsa-keysize-3072")?, "cmdarg-profile", '\ -'"cmdarg-profile-remove-disabled" \], '\ +'"cmdarg-profile-remove-disabled", "cmdarg-no-iak" \], '\ '"profiles": \[ [^]]*\], '\ '"version": "[^"]*" \}' if ! [[ ${msg} =~ ${exp} ]]; then @@ -73,7 +73,10 @@ if [ -x "$(type -P "$(echo "${SWTPM_CERT}" | cut -d" " -f1)" )" ]; then exit 1 fi - exp='\{ "type": "swtpm_cert", "features": \[ "cmdarg-signkey-pwd", "cmdarg-parentkey-pwd" \], "version": "[^"]*" \}' + exp='\{ "type": "swtpm_cert", "features": '\ +'\[ "cmdarg-signkey-pwd", "cmdarg-parentkey-pwd", "cmdarg-tpm-serial-num", '\ +'"supports-iak-idevid" \], '\ +'"version": "[^"]*" \}' if ! [[ "${msg}" =~ ${exp} ]]; then echo "Unexpected response from ${SWTPM_CERT} to --print-capabilities:" echo "Actual : ${msg}" diff --git a/tests/_test_tpm2_print_capabilities b/tests/_test_tpm2_print_capabilities index bb8ddc2cc..6cd5c6478 100755 --- a/tests/_test_tpm2_print_capabilities +++ b/tests/_test_tpm2_print_capabilities @@ -55,7 +55,7 @@ exp='\{ "type": "swtpm_setup", '\ '"features": \[( "tpm-1.2",)? "tpm-2.0", "cmdarg-keyfile-fd", "cmdarg-pwdfile-fd", '\ '"tpm12-not-need-root", "cmdarg-write-ek-cert-files", "cmdarg-create-config-files", '\ '"cmdarg-reconfigure-pcr-banks"(, "tpm2-rsa-keysize-2048")?(, "tpm2-rsa-keysize-3072")?, '\ -'"cmdarg-profile", "cmdarg-profile-remove-disabled" \], '\ +'"cmdarg-profile", "cmdarg-profile-remove-disabled", "cmdarg-no-iak" \], '\ '"profiles": \[ [^]]*\], '\ '"version": "[^"]*" \}' if ! [[ ${msg} =~ ${exp} ]]; then @@ -75,7 +75,10 @@ if [ -x "$(type -P "$(echo "${SWTPM_CERT}" | cut -d" " -f1)" )" ]; then exit 1 fi - exp='\{ "type": "swtpm_cert", "features": \[ "cmdarg-signkey-pwd", "cmdarg-parentkey-pwd" \], "version": "[^"]*" \}' + exp='\{ "type": "swtpm_cert", "features": '\ +'\[ "cmdarg-signkey-pwd", "cmdarg-parentkey-pwd", "cmdarg-tpm-serial-num", '\ +'"supports-iak-idevid" \], '\ +'"version": "[^"]*" \}' if ! [[ "${msg}" =~ ${exp} ]]; then echo "Unexpected response from ${SWTPM_CERT} to --print-capabilities:" echo "Actual : ${msg}" diff --git a/tests/create_certs.sh b/tests/create_certs.sh index c3069d98a..e96eedb63 100755 --- a/tests/create_certs.sh +++ b/tests/create_certs.sh @@ -2,8 +2,29 @@ #echo $@ +ek_cert=\ +MIID9TCCAl2gAwIBAgICBL4wDQYJKoZIhvcNAQELBQAwGDEWMBQGA1UEAxMNc3d0cG0tbG9jYWxj\ +YTAgFw0yMzA4MjIwMTM2MDdaGA85OTk5MTIzMTIzNTk1OVowEjEQMA4GA1UEAxMHdW5rbm93bjCC\ +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJSKqmO3O1KnFQFXENX+z8kcL0eIFW8xVhPW\ +eCJrChwRil36KaxJHPf5aqzWkMuZnCHI2U5susPq7BAeCCPXaOb3pwbs3tgEC5eGAEApv6Y+5Yep\ +ia8chBGI58q3CuKL/HcToNnzT1MwwTtq4dCzav9BcmR/tkh9fSUvjOwZyPIDktgvMXAkLFzqhAx3\ +8TKSqdhjWgbUAr8fGsAUGm1bvepphEpycjCfftdC6/GnGEbHsDWjBS944k3sHWD7Aik/VV9wEHLT\ +EIN8r3rKdj5nzqUrGRFqSupN5e25YRhKxS1SB9wFOiOYoevsPR7Bh3/5MdZd33zhnTbrA9f0RDxu\ +qjUCAwEAAaOBzDCByTAQBgNVHSUECTAHBgVngQUIATBSBgNVHREBAf8ESDBGpEQwQjEWMBQGBWeB\ +BQIBDAtpZDowMDAwMTAxNDEQMA4GBWeBBQICDAVzd3RwbTEWMBQGBWeBBQIDDAtpZDoyMDE5MTAy\ +MzAMBgNVHRMBAf8EAjAAMCIGA1UdCQQbMBkwFwYFZ4EFAhAxDjAMDAMyLjACAQACAgCkMB8GA1Ud\ +IwQYMBaAFMFMlKkwREqQv5MU2eVVkxUZIOP0MA4GA1UdDwEB/wQEAwIFIDANBgkqhkiG9w0BAQsF\ +AAOCAYEAihjxS4PmR5xO7jAYsF5hqeMvEh5+wH+OWjkj9oH+a8N6oFlxUJeQgLLFgK+Jyq4zt0kz\ +tdAaFX0XrWfPH/s1AQdwtUzqOHdSHWcBmfPV+MlMCtz1HjfC5GGKmCHPgwDRLiowwzsWyKFRPKlu\ +UtmtP0ukRTbzGa/j3GpBSnIn7l2yTrnXZ6XXeZ/gvHghzyp02aGJ2Ei873X57zOuFmz1z++WwXRN\ +ipRoAjga57NAz/f1RceJuF+zA8aAX7GY2dcvDCVpU1yoBsWt9gXtZ/4ZO400fbwnxz3zVLJEXgpR\ +jd+XbUUxsGMWqWZ3qEApbrkWjS77TXkDmOqK8Nh92mZvLSMHBJa/mzWFJBPpu+MCSPbO9kAhfB5W\ +F2ynlGuQfeBue4ju5PmcID3xs2FbCItWyj8bJhuA2QQDYmUrSnqQJ9zNLj7ibbq7hDWsaeko65/E\ +HYBXBvksWO4cdoR7F9pcuyhsJDMU7jyGAo0RKuRkUrGnN2Aja4GKSXTilXTCeq/5 + + main() { - local typ ek dir vmid + local typ ek dir vmid tpm2=0 while [ $# -ne 0 ]; do #echo $1 @@ -25,6 +46,7 @@ main() { vmid="$1" ;; --tpm2) + tpm2=1 ;; esac shift @@ -32,11 +54,22 @@ main() { case "$typ" in ek) - echo -n "ek" > ${dir}/ek.cert + # ek cert must be parseable for a TPM 2 + if [ "${tpm2}" -ne 0 ]; then + base64 -d <<< "${ek_cert}" > ${dir}/ek.cert + else + echo -n "ek" > ${dir}/ek.cert + fi ;; platform) echo -n "platform" > ${dir}/platform.cert ;; + iak) + echo -n "iak" > ${dir}/iak.cert + ;; + idevid) + echo -n "idevid" > ${dir}/idevid.cert + ;; esac } diff --git a/tests/test_tpm2_swtpm_setup_create_cert b/tests/test_tpm2_swtpm_setup_create_cert index de5b5610b..d346f6900 100755 --- a/tests/test_tpm2_swtpm_setup_create_cert +++ b/tests/test_tpm2_swtpm_setup_create_cert @@ -17,6 +17,8 @@ CERTSERIAL=${workdir}/certserial USER_CERTSDIR=${workdir}/mycerts mkdir -p "${USER_CERTSDIR}" +vmid="test" + PATH=${TOPBUILD}/src/swtpm_bios:$PATH trap "cleanup" SIGTERM EXIT @@ -197,6 +199,7 @@ for pwdfile in "" "${PWDFILE}"; do --logfile "${workdir}/logfile" \ --tpm "${SWTPM_EXE} socket ${SWTPM_TEST_SECCOMP_OPT}" \ --overwrite \ + --vmid "${vmid}" \ --write-ek-cert-files "${workdir}" \ ${pwdfile:+--pwdfile "${pwdfile}"}; then echo "Error: Could not run $SWTPM_SETUP." @@ -220,18 +223,45 @@ for pwdfile in "" "${PWDFILE}"; do exit 1 fi - certfile="${workdir}/ek-secp384r1.crt" - if [ ! -f "${certfile}" ]; then - echo "Error: EK file '${certfile}' was not written." - ls -l "${workdir}" - exit 1 - fi + for certfile in \ + "${workdir}/ek-secp384r1.crt" \ + "${workdir}/iak-secp384r1.crt" \ + "${workdir}/idevid-secp384r1.crt"; do + if [ ! -f "${certfile}" ]; then + echo "Error: Certificate file '${certfile}' was not written." + ls -l "${workdir}" + exit 1 + fi - if ! $CERTTOOL --inder --infile "${certfile}" -i | grep -q "384 bits"; then - echo "Error: EK file '${certfile}' is not an ECC 384 bit key." - $CERTTOOL --inder --infile "${certfile}" -i - exit 1 - fi + if ! $CERTTOOL --inder --infile "${certfile}" -i | grep -q "384 bits"; then + echo "Error: Certificate file '${certfile}' is not an ECC 384 bit key." + $CERTTOOL --inder --infile "${certfile}" -i + exit 1 + fi + # IAK and IDevID need serialNumber to match :: + if [[ ${certfile} =~ (iak|idevid) ]]; then + cert_vmid=$($CERTTOOL --inder --infile "${certfile}" -i \ + | sed -n 's/.*Subject:.*serialNumber=//p') + if [ "${cert_vmid}" != "${vmid}" ]; then + echo "Error: The serialNumber in the cert is wrong" + echo "expected: ${vmid}" + echo "actual : ${cert_vmid}" + exit 1 + fi + # The serial number starts at the 12th ASCII char + serial=$($CERTTOOL --inder --infile "${certfile}" -i \ + | sed -n "s/.*otherName ASCII: [[:print:]]\{11\}//p") + if [ -z "$serial" ] || \ + [ "${serial}" != \ + "$(echo "${serial}" | \ + sed -n 's/\([[:print:]^:]*\):\([[:xdigit:]^:]*\):\([[:xdigit:]^:]*\)/\1:\2:\3/p')" ]; + then + echo "Error: serial Number does not seem to be properly formatted" + echo " serial: ${serial}" + exit 1 + fi + fi + done swtpm_setup_reconfigure "${workdir}" "${pwdfile}" done