/* * use the AUTH2_COMMAND form of unseal, to authorize both key and blob */ static int tpm_unseal(struct tpm_buf *tb, uint32_t keyhandle, const unsigned char *keyauth, const unsigned char *blob, int bloblen, const unsigned char *blobauth, unsigned char *data, unsigned int *datalen) { unsigned char nonceodd[TPM_NONCE_SIZE]; unsigned char enonce1[TPM_NONCE_SIZE]; unsigned char enonce2[TPM_NONCE_SIZE]; unsigned char authdata1[SHA1_DIGEST_SIZE]; unsigned char authdata2[SHA1_DIGEST_SIZE]; uint32_t authhandle1 = 0; uint32_t authhandle2 = 0; unsigned char cont = 0; uint32_t ordinal; uint32_t keyhndl; int ret; /* sessions for unsealing key and data */ ret = oiap(tb, &authhandle1, enonce1); if (ret < 0) { pr_info("trusted_key: oiap failed (%d)\n", ret); return ret; } ret = oiap(tb, &authhandle2, enonce2); if (ret < 0) { pr_info("trusted_key: oiap failed (%d)\n", ret); return ret; } ordinal = htonl(TPM_ORD_UNSEAL); keyhndl = htonl(SRKHANDLE); ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) { pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); return ret; } ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, enonce1, nonceodd, cont, sizeof(uint32_t), &ordinal, bloblen, blob, 0, 0); if (ret < 0) return ret; ret = TSS_authhmac(authdata2, blobauth, TPM_NONCE_SIZE, enonce2, nonceodd, cont, sizeof(uint32_t), &ordinal, bloblen, blob, 0, 0); if (ret < 0) return ret; /* build and send TPM request packet */ INIT_BUF(tb); store16(tb, TPM_TAG_RQU_AUTH2_COMMAND); store32(tb, TPM_UNSEAL_SIZE + bloblen); store32(tb, TPM_ORD_UNSEAL); store32(tb, keyhandle); storebytes(tb, blob, bloblen); store32(tb, authhandle1); storebytes(tb, nonceodd, TPM_NONCE_SIZE); store8(tb, cont); storebytes(tb, authdata1, SHA1_DIGEST_SIZE); store32(tb, authhandle2); storebytes(tb, nonceodd, TPM_NONCE_SIZE); store8(tb, cont); storebytes(tb, authdata2, SHA1_DIGEST_SIZE); ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); if (ret < 0) { pr_info("trusted_key: authhmac failed (%d)\n", ret); return ret; } *datalen = LOAD32(tb->data, TPM_DATA_OFFSET); ret = TSS_checkhmac2(tb->data, ordinal, nonceodd, keyauth, SHA1_DIGEST_SIZE, blobauth, SHA1_DIGEST_SIZE, sizeof(uint32_t), TPM_DATA_OFFSET, *datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0, 0); if (ret < 0) { pr_info("trusted_key: TSS_checkhmac2 failed (%d)\n", ret); return ret; } memcpy(data, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *datalen); return 0; }
/* * Have the TPM seal(encrypt) the trusted key, possibly based on * Platform Configuration Registers (PCRs). AUTH1 for sealing key. */ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, uint32_t keyhandle, const unsigned char *keyauth, const unsigned char *data, uint32_t datalen, unsigned char *blob, uint32_t *bloblen, const unsigned char *blobauth, const unsigned char *pcrinfo, uint32_t pcrinfosize) { struct osapsess sess; struct tpm_digests *td; unsigned char cont; uint32_t ordinal; uint32_t pcrsize; uint32_t datsize; int sealinfosize; int encdatasize; int storedsize; int ret; int i; /* alloc some work space for all the hashes */ td = kmalloc(sizeof *td, GFP_KERNEL); if (!td) return -ENOMEM; /* get session for sealing key */ ret = osap(tb, &sess, keyauth, keytype, keyhandle); if (ret < 0) goto out; dump_sess(&sess); /* calculate encrypted authorization value */ memcpy(td->xorwork, sess.secret, SHA1_DIGEST_SIZE); memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE); ret = TSS_sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash); if (ret < 0) goto out; ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) goto out; ordinal = htonl(TPM_ORD_SEAL); datsize = htonl(datalen); pcrsize = htonl(pcrinfosize); cont = 0; /* encrypt data authorization key */ for (i = 0; i < SHA1_DIGEST_SIZE; ++i) td->encauth[i] = td->xorhash[i] ^ blobauth[i]; /* calculate authorization HMAC value */ if (pcrinfosize == 0) { /* no pcr info specified */ ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, sess.enonce, td->nonceodd, cont, sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, td->encauth, sizeof(uint32_t), &pcrsize, sizeof(uint32_t), &datsize, datalen, data, 0, 0); } else { /* pcr info specified */ ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, sess.enonce, td->nonceodd, cont, sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, td->encauth, sizeof(uint32_t), &pcrsize, pcrinfosize, pcrinfo, sizeof(uint32_t), &datsize, datalen, data, 0, 0); } if (ret < 0) goto out; /* build and send the TPM request packet */ INIT_BUF(tb); store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); store32(tb, TPM_SEAL_SIZE + pcrinfosize + datalen); store32(tb, TPM_ORD_SEAL); store32(tb, keyhandle); storebytes(tb, td->encauth, SHA1_DIGEST_SIZE); store32(tb, pcrinfosize); storebytes(tb, pcrinfo, pcrinfosize); store32(tb, datalen); storebytes(tb, data, datalen); store32(tb, sess.handle); storebytes(tb, td->nonceodd, TPM_NONCE_SIZE); store8(tb, cont); storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE); ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); if (ret < 0) goto out; /* calculate the size of the returned Blob */ sealinfosize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t)); encdatasize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t) + sizeof(uint32_t) + sealinfosize); storedsize = sizeof(uint32_t) + sizeof(uint32_t) + sealinfosize + sizeof(uint32_t) + encdatasize; /* check the HMAC in the response */ ret = TSS_checkhmac1(tb->data, ordinal, td->nonceodd, sess.secret, SHA1_DIGEST_SIZE, storedsize, TPM_DATA_OFFSET, 0, 0); /* copy the returned blob to caller */ if (!ret) { memcpy(blob, tb->data + TPM_DATA_OFFSET, storedsize); *bloblen = storedsize; } out: kfree(td); return ret; }
/* * Load a TPM key from the blob provided by userspace */ static int tpm_loadkey2(struct tpm_buf *tb, uint32_t keyhandle, unsigned char *keyauth, const unsigned char *keyblob, int keybloblen, uint32_t *newhandle) { unsigned char nonceodd[TPM_NONCE_SIZE]; unsigned char enonce[TPM_NONCE_SIZE]; unsigned char authdata[SHA1_DIGEST_SIZE]; uint32_t authhandle = 0; unsigned char cont = 0; uint32_t ordinal; int ret; ordinal = htonl(TPM_ORD_LOADKEY2); /* session for loading the key */ ret = oiap(tb, &authhandle, enonce); if (ret < 0) { pr_info("oiap failed (%d)\n", ret); return ret; } /* generate odd nonce */ ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE); if (ret < 0) { pr_info("tpm_get_random failed (%d)\n", ret); return ret; } /* calculate authorization HMAC value */ ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce, nonceodd, cont, sizeof(uint32_t), &ordinal, keybloblen, keyblob, 0, 0); if (ret < 0) return ret; /* build the request buffer */ INIT_BUF(tb); store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); store32(tb, TPM_LOADKEY2_SIZE + keybloblen); store32(tb, TPM_ORD_LOADKEY2); store32(tb, keyhandle); storebytes(tb, keyblob, keybloblen); store32(tb, authhandle); storebytes(tb, nonceodd, TPM_NONCE_SIZE); store8(tb, cont); storebytes(tb, authdata, SHA1_DIGEST_SIZE); ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); if (ret < 0) { pr_info("authhmac failed (%d)\n", ret); return ret; } ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, keyauth, SHA1_DIGEST_SIZE, 0, 0); if (ret < 0) { pr_info("TSS_checkhmac1 failed (%d)\n", ret); return ret; } *newhandle = LOAD32(tb->data, TPM_DATA_OFFSET); return 0; }
/* Open and lock the cache file. If mode is FCC_OPEN_AND_ERASE, initialize it * with a header. Call with the mutex locked. */ static krb5_error_code open_cache_file(krb5_context context, krb5_ccache id, int mode) { krb5_os_context os_ctx = &context->os_context; krb5_error_code ret; fcc_data *data = id->data; char fcc_fvno[2]; uint16_t fcc_flen, fcc_tag, fcc_taglen; uint32_t time_offset, usec_offset; int f, open_flag, lock_flag, cnt; char buf[1024]; k5_cc_mutex_assert_locked(context, &data->lock); invalidate_cache(data); if (data->fd != NO_FILE) { /* Don't know what state it's in; shut down and start anew. */ (void)krb5_unlock_file(context, data->fd); (void)close(data->fd); data->fd = NO_FILE; } switch (mode) { case FCC_OPEN_AND_ERASE: unlink(data->filename); open_flag = O_CREAT | O_EXCL | O_TRUNC | O_RDWR; break; case FCC_OPEN_RDWR: open_flag = O_RDWR; break; case FCC_OPEN_RDONLY: default: open_flag = O_RDONLY; break; } f = THREEPARAMOPEN(data->filename, open_flag | O_BINARY, 0600); if (f == NO_FILE) { if (errno == ENOENT) { ret = KRB5_FCC_NOFILE; k5_setmsg(context, ret, _("Credentials cache file '%s' not found"), data->filename); return ret; } else { return interpret_errno(context, errno); } } set_cloexec_fd(f); data->mode = mode; if (data->mode == FCC_OPEN_RDONLY) lock_flag = KRB5_LOCKMODE_SHARED; else lock_flag = KRB5_LOCKMODE_EXCLUSIVE; ret = krb5_lock_file(context, f, lock_flag); if (ret) { (void)close(f); return ret; } if (mode == FCC_OPEN_AND_ERASE) { /* write the version number */ store_16_be(context->fcc_default_format, fcc_fvno); data->version = context->fcc_default_format; cnt = write(f, fcc_fvno, 2); if (cnt != 2) { ret = (cnt == -1) ? interpret_errno(context, errno) : KRB5_CC_IO; goto done; } data->fd = f; if (data->version == FVNO_4) { /* V4 of the credentials cache format allows for header tags */ fcc_flen = 0; if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) fcc_flen += 2 + 2 + 4 + 4; /* Write header length. */ ret = store16(context, id, fcc_flen); if (ret) goto done; if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) { /* Write time offset tag. */ fcc_tag = FCC_TAG_DELTATIME; fcc_taglen = 2 * 4; ret = store16(context, id, fcc_tag); if (ret) goto done; ret = store16(context, id, fcc_taglen); if (ret) goto done; ret = store32(context, id, os_ctx->time_offset); if (ret) goto done; ret = store32(context, id, os_ctx->usec_offset); if (ret) goto done; } } invalidate_cache(data); goto done; } /* Verify a valid version number is there. */ invalidate_cache(data); if (read(f, fcc_fvno, 2) != 2) { ret = KRB5_CC_FORMAT; goto done; } data->version = load_16_be(fcc_fvno); if (data->version != FVNO_4 && data->version != FVNO_3 && data->version != FVNO_2 && data->version != FVNO_1) { ret = KRB5_CCACHE_BADVNO; goto done; } data->fd = f; if (data->version == FVNO_4) { if (read16(context, id, &fcc_flen) || fcc_flen > sizeof(buf)) { ret = KRB5_CC_FORMAT; goto done; } while (fcc_flen) { if (fcc_flen < 2 * 2 || read16(context, id, &fcc_tag) || read16(context, id, &fcc_taglen) || fcc_taglen > fcc_flen - 2 * 2) { ret = KRB5_CC_FORMAT; goto done; } switch (fcc_tag) { case FCC_TAG_DELTATIME: if (fcc_taglen != 2 * 4) { ret = KRB5_CC_FORMAT; goto done; } if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) || (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) { if (read_bytes(context, id, buf, fcc_taglen)) { ret = KRB5_CC_FORMAT; goto done; } break; } if (read32(context, id, NULL, &time_offset) || read32(context, id, NULL, &usec_offset)) { ret = KRB5_CC_FORMAT; goto done; } os_ctx->time_offset = time_offset; os_ctx->usec_offset = usec_offset; os_ctx->os_flags = ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) | KRB5_OS_TOFFSET_VALID); break; default: if (fcc_taglen && read_bytes(context, id, buf, fcc_taglen)) { ret = KRB5_CC_FORMAT; goto done; } break; } fcc_flen -= (2 * 2 + fcc_taglen); } } done: if (ret) { data->fd = -1; (void)krb5_unlock_file(context, f); (void)close(f); } return ret; }
/* * Sign a blob provided by userspace (that has had the hash function applied) * using a specific key handle. The handle is assumed to have been previously * loaded by e.g. LoadKey2. * * Note that the key signature scheme of the used key should be set to * TPM_SS_RSASSAPKCS1v15_DER. This allows the hashed input to be of any size * up to key_length_in_bytes - 11 and not be limited to size 20 like the * TPM_SS_RSASSAPKCS1v15_SHA1 signature scheme. */ static int tpm_sign(struct tpm_buf *tb, uint32_t keyhandle, unsigned char *keyauth, const unsigned char *blob, uint32_t bloblen, void *out, uint32_t outlen) { unsigned char nonceodd[TPM_NONCE_SIZE]; unsigned char enonce[TPM_NONCE_SIZE]; unsigned char authdata[SHA1_DIGEST_SIZE]; uint32_t authhandle = 0; unsigned char cont = 0; uint32_t ordinal; uint32_t datalen; int ret; ordinal = htonl(TPM_ORD_SIGN); datalen = htonl(bloblen); /* session for loading the key */ ret = oiap(tb, &authhandle, enonce); if (ret < 0) { pr_info("oiap failed (%d)\n", ret); return ret; } /* generate odd nonce */ ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE); if (ret < 0) { pr_info("tpm_get_random failed (%d)\n", ret); return ret; } /* calculate authorization HMAC value */ ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce, nonceodd, cont, sizeof(uint32_t), &ordinal, sizeof(uint32_t), &datalen, bloblen, blob, 0, 0); if (ret < 0) return ret; /* build the request buffer */ INIT_BUF(tb); store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); store32(tb, TPM_SIGN_SIZE + bloblen); store32(tb, TPM_ORD_SIGN); store32(tb, keyhandle); store32(tb, bloblen); storebytes(tb, blob, bloblen); store32(tb, authhandle); storebytes(tb, nonceodd, TPM_NONCE_SIZE); store8(tb, cont); storebytes(tb, authdata, SHA1_DIGEST_SIZE); ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); if (ret < 0) { pr_info("authhmac failed (%d)\n", ret); return ret; } datalen = LOAD32(tb->data, TPM_DATA_OFFSET); ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, keyauth, SHA1_DIGEST_SIZE, sizeof(uint32_t), TPM_DATA_OFFSET, datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0, 0); if (ret < 0) { pr_info("TSS_checkhmac1 failed (%d)\n", ret); return ret; } memcpy(out, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), min(datalen, outlen)); return datalen; }