1191 lines · 28.0 KB · C
RawDownload
/* * Copyright (C) 2010 IBM Corporation * * Author: * David Safford <safford@us.ibm.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2 of the License. * * See Documentation/security/keys-trusted-encrypted.txt */ #include <linux/uaccess.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/parser.h>#include <linux/string.h>#include <linux/err.h>#include <keys/user-type.h>#include <keys/trusted-type.h>#include <linux/key-type.h>#include <linux/rcupdate.h>#include <linux/crypto.h>#include <crypto/hash.h>#include <crypto/sha.h>#include <linux/capability.h>#include <linux/tpm.h>#include <linux/tpm_command.h> #include "trusted.h" static const char hmac_alg[] = "hmac(sha1)";static const char hash_alg[] = "sha1"; struct sdesc {	struct shash_desc shash;	char ctx[];}; static struct crypto_shash *hashalg;static struct crypto_shash *hmacalg; static struct sdesc *init_sdesc(struct crypto_shash *alg){	struct sdesc *sdesc;	int size; 	size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);	sdesc = kmalloc(size, GFP_KERNEL);	if (!sdesc)		return ERR_PTR(-ENOMEM);	sdesc->shash.tfm = alg;	sdesc->shash.flags = 0x0;	return sdesc;} static int TSS_sha1(const unsigned char *data, unsigned int datalen,		    unsigned char *digest){	struct sdesc *sdesc;	int ret; 	sdesc = init_sdesc(hashalg);	if (IS_ERR(sdesc)) {		pr_info("trusted_key: can't alloc %s\n", hash_alg);		return PTR_ERR(sdesc);	} 	ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);	kfree(sdesc);	return ret;} static int TSS_rawhmac(unsigned char *digest, const unsigned char *key,		       unsigned int keylen, ...){	struct sdesc *sdesc;	va_list argp;	unsigned int dlen;	unsigned char *data;	int ret; 	sdesc = init_sdesc(hmacalg);	if (IS_ERR(sdesc)) {		pr_info("trusted_key: can't alloc %s\n", hmac_alg);		return PTR_ERR(sdesc);	} 	ret = crypto_shash_setkey(hmacalg, key, keylen);	if (ret < 0)		goto out;	ret = crypto_shash_init(&sdesc->shash);	if (ret < 0)		goto out; 	va_start(argp, keylen);	for (;;) {		dlen = va_arg(argp, unsigned int);		if (dlen == 0)			break;		data = va_arg(argp, unsigned char *);		if (data == NULL) {			ret = -EINVAL;			break;		}		ret = crypto_shash_update(&sdesc->shash, data, dlen);		if (ret < 0)			break;	}	va_end(argp);	if (!ret)		ret = crypto_shash_final(&sdesc->shash, digest);out:	kfree(sdesc);	return ret;} /* * calculate authorization info fields to send to TPM */static int TSS_authhmac(unsigned char *digest, const unsigned char *key,			unsigned int keylen, unsigned char *h1,			unsigned char *h2, unsigned char h3, ...){	unsigned char paramdigest[SHA1_DIGEST_SIZE];	struct sdesc *sdesc;	unsigned int dlen;	unsigned char *data;	unsigned char c;	int ret;	va_list argp; 	sdesc = init_sdesc(hashalg);	if (IS_ERR(sdesc)) {		pr_info("trusted_key: can't alloc %s\n", hash_alg);		return PTR_ERR(sdesc);	} 	c = h3;	ret = crypto_shash_init(&sdesc->shash);	if (ret < 0)		goto out;	va_start(argp, h3);	for (;;) {		dlen = va_arg(argp, unsigned int);		if (dlen == 0)			break;		data = va_arg(argp, unsigned char *);		if (!data) {			ret = -EINVAL;			break;		}		ret = crypto_shash_update(&sdesc->shash, data, dlen);		if (ret < 0)			break;	}	va_end(argp);	if (!ret)		ret = crypto_shash_final(&sdesc->shash, paramdigest);	if (!ret)		ret = TSS_rawhmac(digest, key, keylen, SHA1_DIGEST_SIZE,				  paramdigest, TPM_NONCE_SIZE, h1,				  TPM_NONCE_SIZE, h2, 1, &c, 0, 0);out:	kfree(sdesc);	return ret;} /* * verify the AUTH1_COMMAND (Seal) result from TPM */static int TSS_checkhmac1(unsigned char *buffer,			  const uint32_t command,			  const unsigned char *ononce,			  const unsigned char *key,			  unsigned int keylen, ...){	uint32_t bufsize;	uint16_t tag;	uint32_t ordinal;	uint32_t result;	unsigned char *enonce;	unsigned char *continueflag;	unsigned char *authdata;	unsigned char testhmac[SHA1_DIGEST_SIZE];	unsigned char paramdigest[SHA1_DIGEST_SIZE];	struct sdesc *sdesc;	unsigned int dlen;	unsigned int dpos;	va_list argp;	int ret; 	bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);	tag = LOAD16(buffer, 0);	ordinal = command;	result = LOAD32N(buffer, TPM_RETURN_OFFSET);	if (tag == TPM_TAG_RSP_COMMAND)		return 0;	if (tag != TPM_TAG_RSP_AUTH1_COMMAND)		return -EINVAL;	authdata = buffer + bufsize - SHA1_DIGEST_SIZE;	continueflag = authdata - 1;	enonce = continueflag - TPM_NONCE_SIZE; 	sdesc = init_sdesc(hashalg);	if (IS_ERR(sdesc)) {		pr_info("trusted_key: can't alloc %s\n", hash_alg);		return PTR_ERR(sdesc);	}	ret = crypto_shash_init(&sdesc->shash);	if (ret < 0)		goto out;	ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result,				  sizeof result);	if (ret < 0)		goto out;	ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal,				  sizeof ordinal);	if (ret < 0)		goto out;	va_start(argp, keylen);	for (;;) {		dlen = va_arg(argp, unsigned int);		if (dlen == 0)			break;		dpos = va_arg(argp, unsigned int);		ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen);		if (ret < 0)			break;	}	va_end(argp);	if (!ret)		ret = crypto_shash_final(&sdesc->shash, paramdigest);	if (ret < 0)		goto out; 	ret = TSS_rawhmac(testhmac, key, keylen, SHA1_DIGEST_SIZE, paramdigest,			  TPM_NONCE_SIZE, enonce, TPM_NONCE_SIZE, ononce,			  1, continueflag, 0, 0);	if (ret < 0)		goto out; 	if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE))		ret = -EINVAL;out:	kfree(sdesc);	return ret;} /* * verify the AUTH2_COMMAND (unseal) result from TPM */static int TSS_checkhmac2(unsigned char *buffer,			  const uint32_t command,			  const unsigned char *ononce,			  const unsigned char *key1,			  unsigned int keylen1,			  const unsigned char *key2,			  unsigned int keylen2, ...){	uint32_t bufsize;	uint16_t tag;	uint32_t ordinal;	uint32_t result;	unsigned char *enonce1;	unsigned char *continueflag1;	unsigned char *authdata1;	unsigned char *enonce2;	unsigned char *continueflag2;	unsigned char *authdata2;	unsigned char testhmac1[SHA1_DIGEST_SIZE];	unsigned char testhmac2[SHA1_DIGEST_SIZE];	unsigned char paramdigest[SHA1_DIGEST_SIZE];	struct sdesc *sdesc;	unsigned int dlen;	unsigned int dpos;	va_list argp;	int ret; 	bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);	tag = LOAD16(buffer, 0);	ordinal = command;	result = LOAD32N(buffer, TPM_RETURN_OFFSET); 	if (tag == TPM_TAG_RSP_COMMAND)		return 0;	if (tag != TPM_TAG_RSP_AUTH2_COMMAND)		return -EINVAL;	authdata1 = buffer + bufsize - (SHA1_DIGEST_SIZE + 1			+ SHA1_DIGEST_SIZE + SHA1_DIGEST_SIZE);	authdata2 = buffer + bufsize - (SHA1_DIGEST_SIZE);	continueflag1 = authdata1 - 1;	continueflag2 = authdata2 - 1;	enonce1 = continueflag1 - TPM_NONCE_SIZE;	enonce2 = continueflag2 - TPM_NONCE_SIZE; 	sdesc = init_sdesc(hashalg);	if (IS_ERR(sdesc)) {		pr_info("trusted_key: can't alloc %s\n", hash_alg);		return PTR_ERR(sdesc);	}	ret = crypto_shash_init(&sdesc->shash);	if (ret < 0)		goto out;	ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result,				  sizeof result);	if (ret < 0)		goto out;	ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal,				  sizeof ordinal);	if (ret < 0)		goto out; 	va_start(argp, keylen2);	for (;;) {		dlen = va_arg(argp, unsigned int);		if (dlen == 0)			break;		dpos = va_arg(argp, unsigned int);		ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen);		if (ret < 0)			break;	}	va_end(argp);	if (!ret)		ret = crypto_shash_final(&sdesc->shash, paramdigest);	if (ret < 0)		goto out; 	ret = TSS_rawhmac(testhmac1, key1, keylen1, SHA1_DIGEST_SIZE,			  paramdigest, TPM_NONCE_SIZE, enonce1,			  TPM_NONCE_SIZE, ononce, 1, continueflag1, 0, 0);	if (ret < 0)		goto out;	if (memcmp(testhmac1, authdata1, SHA1_DIGEST_SIZE)) {		ret = -EINVAL;		goto out;	}	ret = TSS_rawhmac(testhmac2, key2, keylen2, SHA1_DIGEST_SIZE,			  paramdigest, TPM_NONCE_SIZE, enonce2,			  TPM_NONCE_SIZE, ononce, 1, continueflag2, 0, 0);	if (ret < 0)		goto out;	if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE))		ret = -EINVAL;out:	kfree(sdesc);	return ret;} /* * For key specific tpm requests, we will generate and send our * own TPM command packets using the drivers send function. */static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd,			    size_t buflen){	int rc; 	dump_tpm_buf(cmd);	rc = tpm_send(chip_num, cmd, buflen);	dump_tpm_buf(cmd);	if (rc > 0)		/* Can't return positive return codes values to keyctl */		rc = -EPERM;	return rc;} /* * get a random value from TPM */static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len){	int ret; 	INIT_BUF(tb);	store16(tb, TPM_TAG_RQU_COMMAND);	store32(tb, TPM_GETRANDOM_SIZE);	store32(tb, TPM_ORD_GETRANDOM);	store32(tb, len);	ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data);	if (!ret)		memcpy(buf, tb->data + TPM_GETRANDOM_SIZE, len);	return ret;} static int my_get_random(unsigned char *buf, int len){	struct tpm_buf *tb;	int ret; 	tb = kmalloc(sizeof *tb, GFP_KERNEL);	if (!tb)		return -ENOMEM;	ret = tpm_get_random(tb, buf, len); 	kfree(tb);	return ret;} /* * Lock a trusted key, by extending a selected PCR. * * Prevents a trusted key that is sealed to PCRs from being accessed. * This uses the tpm driver's extend function. */static int pcrlock(const int pcrnum){	unsigned char hash[SHA1_DIGEST_SIZE];	int ret; 	if (!capable(CAP_SYS_ADMIN))		return -EPERM;	ret = my_get_random(hash, SHA1_DIGEST_SIZE);	if (ret < 0)		return ret;	return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0;} /* * Create an object specific authorisation protocol (OSAP) session */static int osap(struct tpm_buf *tb, struct osapsess *s,		const unsigned char *key, uint16_t type, uint32_t handle){	unsigned char enonce[TPM_NONCE_SIZE];	unsigned char ononce[TPM_NONCE_SIZE];	int ret; 	ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE);	if (ret < 0)		return ret; 	INIT_BUF(tb);	store16(tb, TPM_TAG_RQU_COMMAND);	store32(tb, TPM_OSAP_SIZE);	store32(tb, TPM_ORD_OSAP);	store16(tb, type);	store32(tb, handle);	storebytes(tb, ononce, TPM_NONCE_SIZE); 	ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);	if (ret < 0)		return ret; 	s->handle = LOAD32(tb->data, TPM_DATA_OFFSET);	memcpy(s->enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)]),	       TPM_NONCE_SIZE);	memcpy(enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) +				  TPM_NONCE_SIZE]), TPM_NONCE_SIZE);	return TSS_rawhmac(s->secret, key, SHA1_DIGEST_SIZE, TPM_NONCE_SIZE,			   enonce, TPM_NONCE_SIZE, ononce, 0, 0);} /* * Create an object independent authorisation protocol (oiap) session */static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce){	int ret; 	INIT_BUF(tb);	store16(tb, TPM_TAG_RQU_COMMAND);	store32(tb, TPM_OIAP_SIZE);	store32(tb, TPM_ORD_OIAP);	ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);	if (ret < 0)		return ret; 	*handle = LOAD32(tb->data, TPM_DATA_OFFSET);	memcpy(nonce, &tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)],	       TPM_NONCE_SIZE);	return 0;} struct tpm_digests {	unsigned char encauth[SHA1_DIGEST_SIZE];	unsigned char pubauth[SHA1_DIGEST_SIZE];	unsigned char xorwork[SHA1_DIGEST_SIZE * 2];	unsigned char xorhash[SHA1_DIGEST_SIZE];	unsigned char nonceodd[TPM_NONCE_SIZE];}; /* * 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;

Lines 1–500 of 1191.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t