On Thu, 2010-12-23 at 18:35 +0100, Roberto Sassu wrote: > This patch introduces a new parameter, called 'format', that defines the > format of data stored by encrypted keys. The 'default' format identifies > encrypted keys containing only the symmetric key, while other formats can > be defined to support additional information. The 'format' parameter is > written in the datablob produced by commands 'keyctl print' or > 'keyctl pipe' and is integrity protected by the HMAC. > > Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxx> Nice! Acked-by: Mimi Zohar <zohar@xxxxxxxxxx> > --- > Documentation/keys-trusted-encrypted.txt | 48 +++++++---- > include/keys/encrypted-type.h | 13 +++- > security/keys/encrypted_defined.c | 139 +++++++++++++++++++++-------- > 3 files changed, 141 insertions(+), 59 deletions(-) > > diff --git a/Documentation/keys-trusted-encrypted.txt b/Documentation/keys-trusted-encrypted.txt > index 8fb79bc..0afcb50 100644 > --- a/Documentation/keys-trusted-encrypted.txt > +++ b/Documentation/keys-trusted-encrypted.txt > @@ -53,12 +53,19 @@ they are only as secure as the user key encrypting them. The master user key > should therefore be loaded in as secure a way as possible, preferably early in > boot. > > +The decrypted portion of encrypted keys can contain either a simple symmetric > +key or a more complex structure. The format of the more complex structure is > +application specific, which is identified by 'format'. > + > Usage: > - keyctl add encrypted name "new key-type:master-key-name keylen" ring > - keyctl add encrypted name "load hex_blob" ring > - keyctl update keyid "update key-type:master-key-name" > + keyctl add encrypted name "new [format] key-type:master-key-name keylen" > + ring > + keyctl add encrypted name "load hex_blob" ring > + keyctl update keyid "update key-type:master-key-name" > + > +format:= 'default' > +key-type:= 'trusted' | 'user' > > -where 'key-type' is either 'trusted' or 'user'. > > Examples of trusted and encrypted key usage: > > @@ -114,15 +121,25 @@ Reseal a trusted key under new pcr values: > 7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef > df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8 > > -Create and save an encrypted key "evm" using the above trusted key "kmk": > +The initial consumer of trusted keys is EVM, which at boot time needs a high > +quality symmetric key for HMAC protection of file metadata. The use of a > +trusted key provides strong guarantees that the EVM key has not been > +compromised by a user level problem, and when sealed to specific boot PCR > +values, protects against boot and offline attacks. Create and save an > +encrypted key "evm" using the above trusted key "kmk": > > +option 1: omitting 'format' > $ keyctl add encrypted evm "new trusted:kmk 32" @u > 159771175 > > +option 2: explicitly defining 'format' as 'default' > + $ keyctl add encrypted evm "new default trusted:kmk 32" @u > + 159771175 > + > $ keyctl print 159771175 > - trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55 > - be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64 > - 5972dcb82ab2dde83376d82b2e3c09ffc > + default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3 > + 82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0 > + 24717c64 5972dcb82ab2dde83376d82b2e3c09ffc > > $ keyctl pipe 159771175 > evm.blob > > @@ -132,14 +149,9 @@ Load an encrypted key "evm" from saved blob: > 831684262 > > $ keyctl print 831684262 > - trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55 > - be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64 > - 5972dcb82ab2dde83376d82b2e3c09ffc > - > + default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3 > + 82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0 > + 24717c64 5972dcb82ab2dde83376d82b2e3c09ffc > > -The initial consumer of trusted keys is EVM, which at boot time needs a high > -quality symmetric key for HMAC protection of file metadata. The use of a > -trusted key provides strong guarantees that the EVM key has not been > -compromised by a user level problem, and when sealed to specific boot PCR > -values, protects against boot and offline attacks. Other uses for trusted and > -encrypted keys, such as for disk and file encryption are anticipated. > +Other uses for trusted and encrypted keys, such as for disk and file encryption > +are anticipated. > diff --git a/include/keys/encrypted-type.h b/include/keys/encrypted-type.h > index 9585501..1d45413 100644 > --- a/include/keys/encrypted-type.h > +++ b/include/keys/encrypted-type.h > @@ -1,6 +1,11 @@ > /* > * Copyright (C) 2010 IBM Corporation > - * Author: Mimi Zohar <zohar@xxxxxxxxxx> > + * Copyright (C) 2010 Politecnico di Torino, Italy > + * TORSEC group -- http://security.polito.it > + * > + * Authors: > + * Mimi Zohar <zohar@xxxxxxxxxx> > + * Roberto Sassu <roberto.sassu@xxxxxxxxx> > * > * 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 > @@ -15,13 +20,17 @@ > > struct encrypted_key_payload { > struct rcu_head rcu; > + char *format; /* datablob: format */ > char *master_desc; /* datablob: master key name */ > char *datalen; /* datablob: decrypted key length */ > u8 *iv; /* datablob: iv */ > u8 *encrypted_data; /* datablob: encrypted data */ > unsigned short datablob_len; /* length of datablob */ > unsigned short decrypted_datalen; /* decrypted data length */ > - u8 decrypted_data[0]; /* decrypted data + datablob + hmac */ > + unsigned short payload_datalen; /* payload data length */ > + unsigned short encrypted_key_format; /* encrypted key format */ > + u8 *decrypted_data; /* decrypted data */ > + u8 payload_data[0]; /* payload data + datablob + hmac */ > }; > > extern struct key_type key_type_encrypted; > diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c > index 2bb2c47..7a914ab 100644 > --- a/security/keys/encrypted_defined.c > +++ b/security/keys/encrypted_defined.c > @@ -1,8 +1,11 @@ > /* > * Copyright (C) 2010 IBM Corporation > + * Copyright (C) 2010 Politecnico di Torino, Italy > + * TORSEC group -- http://security.polito.it > * > - * Author: > + * Authors: > * Mimi Zohar <zohar@xxxxxxxxxx> > + * Roberto Sassu <roberto.sassu@xxxxxxxxx> > * > * 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 > @@ -37,6 +40,7 @@ static const char KEY_USER_PREFIX[] = "user:"; > static const char hash_alg[] = "sha256"; > static const char hmac_alg[] = "hmac(sha256)"; > static const char blkcipher_alg[] = "cbc(aes)"; > +static const char key_format_default[] = "default"; > static unsigned int ivsize; > static int blksize; > > @@ -58,6 +62,15 @@ enum { > Opt_err = -1, Opt_new, Opt_load, Opt_update > }; > > +enum { > + Opt_error = -1, Opt_default > +}; > + > +static const match_table_t key_format_tokens = { > + {Opt_default, "default"}, > + {Opt_error, NULL} > +}; > + > static const match_table_t key_tokens = { > {Opt_new, "new"}, > {Opt_load, "load"}, > @@ -118,8 +131,9 @@ out: > * datablob_parse - parse the keyctl data > * > * datablob format: > - * new <master-key name> <decrypted data length> > - * load <master-key name> <decrypted data length> <encrypted iv + data> > + * new [<format>] <master-key name> <decrypted data length> > + * load [<format>] <master-key name> <decrypted data length> > + * <encrypted iv + data> > * update <new-master-key name> > * > * Tokenizes a copy of the keyctl data, returning a pointer to each token, > @@ -127,12 +141,14 @@ out: > * > * On success returns 0, otherwise -EINVAL. > */ > -static int datablob_parse(char *datablob, char **master_desc, > - char **decrypted_datalen, char **hex_encoded_iv) > +static int datablob_parse(char *datablob, const char **format, > + char **master_desc, char **decrypted_datalen, > + char **hex_encoded_iv) > { > substring_t args[MAX_OPT_ARGS]; > int ret = -EINVAL; > int key_cmd; > + int key_format; > char *p, *keyword; > > keyword = strsep(&datablob, " \t"); > @@ -142,7 +158,24 @@ static int datablob_parse(char *datablob, char **master_desc, > } > key_cmd = match_token(keyword, key_tokens, args); > > - *master_desc = strsep(&datablob, " \t"); > + /* Get optional format: default */ > + p = strsep(&datablob, " \t"); > + if (!p) { > + pr_err("encrypted_key: insufficient parameters specified\n"); > + return ret; > + } > + > + key_format = match_token(p, key_format_tokens, args); > + switch (key_format) { > + case Opt_default: > + *format = p; > + *master_desc = strsep(&datablob, " \t"); > + break; > + case Opt_error: > + *master_desc = p; > + break; > + } > + > if (!*master_desc) { > pr_err("encrypted_key: master key parameter is missing\n"); > goto out; > @@ -219,8 +252,8 @@ static char *datablob_format(struct encrypted_key_payload *epayload, > ascii_buf[asciiblob_len] = '\0'; > > /* copy datablob master_desc and datalen strings */ > - len = sprintf(ascii_buf, "%s %s ", epayload->master_desc, > - epayload->datalen); > + len = sprintf(ascii_buf, "%s %s %s ", epayload->format, > + epayload->master_desc, epayload->datalen); > > /* convert the hex encoded iv, encrypted-data and HMAC to ascii */ > bufp = &ascii_buf[len]; > @@ -461,9 +494,9 @@ static int datablob_hmac_append(struct encrypted_key_payload *epayload, > if (ret < 0) > goto out; > > - digest = epayload->master_desc + epayload->datablob_len; > + digest = epayload->format + epayload->datablob_len; > ret = calc_hmac(digest, derived_key, sizeof derived_key, > - epayload->master_desc, epayload->datablob_len); > + epayload->format, epayload->datablob_len); > if (!ret) > dump_hmac(NULL, digest, HASH_SIZE); > out: > @@ -472,26 +505,35 @@ out: > > /* verify HMAC before decrypting encrypted key */ > static int datablob_hmac_verify(struct encrypted_key_payload *epayload, > - const u8 *master_key, size_t master_keylen) > + const u8 *format, const u8 *master_key, > + size_t master_keylen) > { > u8 derived_key[HASH_SIZE]; > u8 digest[HASH_SIZE]; > int ret; > + char *p; > + unsigned short len; > > ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen); > if (ret < 0) > goto out; > > - ret = calc_hmac(digest, derived_key, sizeof derived_key, > - epayload->master_desc, epayload->datablob_len); > + len = epayload->datablob_len; > + if (!format) { > + p = epayload->master_desc; > + len -= strlen(epayload->format) + 1; > + } else > + p = epayload->format; > + > + ret = calc_hmac(digest, derived_key, sizeof derived_key, p, len); > if (ret < 0) > goto out; > - ret = memcmp(digest, epayload->master_desc + epayload->datablob_len, > + ret = memcmp(digest, epayload->format + epayload->datablob_len, > sizeof digest); > if (ret) { > ret = -EINVAL; > dump_hmac("datablob", > - epayload->master_desc + epayload->datablob_len, > + epayload->format + epayload->datablob_len, > HASH_SIZE); > dump_hmac("calc", digest, HASH_SIZE); > } > @@ -536,13 +578,16 @@ out: > > /* Allocate memory for decrypted key and datablob. */ > static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, > + const char *format, > const char *master_desc, > const char *datalen) > { > struct encrypted_key_payload *epayload = NULL; > unsigned short datablob_len; > unsigned short decrypted_datalen; > + unsigned short payload_datalen; > unsigned int encrypted_datalen; > + unsigned int format_len; > long dlen; > int ret; > > @@ -550,29 +595,32 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, > if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE) > return ERR_PTR(-EINVAL); > > + format_len = (!format) ? strlen(key_format_default) : strlen(format); > decrypted_datalen = dlen; > + payload_datalen = decrypted_datalen; > encrypted_datalen = roundup(decrypted_datalen, blksize); > > - datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1 > - + ivsize + 1 + encrypted_datalen; > + datablob_len = format_len + 1 + strlen(master_desc) + 1 > + + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen; > > - ret = key_payload_reserve(key, decrypted_datalen + datablob_len > + ret = key_payload_reserve(key, payload_datalen + datablob_len > + HASH_SIZE + 1); > if (ret < 0) > return ERR_PTR(ret); > > - epayload = kzalloc(sizeof(*epayload) + decrypted_datalen + > + epayload = kzalloc(sizeof(*epayload) + payload_datalen + > datablob_len + HASH_SIZE + 1, GFP_KERNEL); > if (!epayload) > return ERR_PTR(-ENOMEM); > > + epayload->payload_datalen = payload_datalen; > epayload->decrypted_datalen = decrypted_datalen; > epayload->datablob_len = datablob_len; > return epayload; > } > > static int encrypted_key_decrypt(struct encrypted_key_payload *epayload, > - const char *hex_encoded_iv) > + const char *format, const char *hex_encoded_iv) > { > struct key *mkey; > u8 derived_key[HASH_SIZE]; > @@ -593,14 +641,14 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload, > hex2bin(epayload->iv, hex_encoded_iv, ivsize); > hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen); > > - hmac = epayload->master_desc + epayload->datablob_len; > + hmac = epayload->format + epayload->datablob_len; > hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE); > > mkey = request_master_key(epayload, &master_key, &master_keylen); > if (IS_ERR(mkey)) > return PTR_ERR(mkey); > > - ret = datablob_hmac_verify(epayload, master_key, master_keylen); > + ret = datablob_hmac_verify(epayload, format, master_key, master_keylen); > if (ret < 0) { > pr_err("encrypted_key: bad hmac (%d)\n", ret); > goto out; > @@ -620,14 +668,23 @@ out: > } > > static void __ekey_init(struct encrypted_key_payload *epayload, > - const char *master_desc, const char *datalen) > + const char *format, const char *master_desc, > + const char *datalen) > { > - epayload->master_desc = epayload->decrypted_data > - + epayload->decrypted_datalen; > + unsigned int format_len; > + > + format_len = (!format) ? strlen(key_format_default) : strlen(format); > + epayload->format = epayload->payload_data + epayload->payload_datalen; > + epayload->master_desc = epayload->format + format_len + 1; > epayload->datalen = epayload->master_desc + strlen(master_desc) + 1; > epayload->iv = epayload->datalen + strlen(datalen) + 1; > epayload->encrypted_data = epayload->iv + ivsize + 1; > + epayload->decrypted_data = epayload->payload_data; > > + if (!format) > + memcpy(epayload->format, key_format_default, format_len); > + else > + memcpy(epayload->format, format, format_len); > memcpy(epayload->master_desc, master_desc, strlen(master_desc)); > memcpy(epayload->datalen, datalen, strlen(datalen)); > } > @@ -639,19 +696,19 @@ static void __ekey_init(struct encrypted_key_payload *epayload, > * itself. For an old key, decrypt the hex encoded data. > */ > static int encrypted_init(struct encrypted_key_payload *epayload, > - const char *master_desc, const char *datalen, > - const char *hex_encoded_iv) > + const char *format, const char *master_desc, > + const char *datalen, const char *hex_encoded_iv) > { > int ret = 0; > > - __ekey_init(epayload, master_desc, datalen); > + __ekey_init(epayload, format, master_desc, datalen); > if (!hex_encoded_iv) { > get_random_bytes(epayload->iv, ivsize); > > get_random_bytes(epayload->decrypted_data, > epayload->decrypted_datalen); > } else > - ret = encrypted_key_decrypt(epayload, hex_encoded_iv); > + ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv); > return ret; > } > > @@ -668,6 +725,7 @@ static int encrypted_instantiate(struct key *key, const void *data, > { > struct encrypted_key_payload *epayload = NULL; > char *datablob = NULL; > + const char *format = NULL; > char *master_desc = NULL; > char *decrypted_datalen = NULL; > char *hex_encoded_iv = NULL; > @@ -681,17 +739,18 @@ static int encrypted_instantiate(struct key *key, const void *data, > return -ENOMEM; > datablob[datalen] = 0; > memcpy(datablob, data, datalen); > - ret = datablob_parse(datablob, &master_desc, &decrypted_datalen, > - &hex_encoded_iv); > + ret = datablob_parse(datablob, &format, &master_desc, > + &decrypted_datalen, &hex_encoded_iv); > if (ret < 0) > goto out; > > - epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen); > + epayload = encrypted_key_alloc(key, format, master_desc, > + decrypted_datalen); > if (IS_ERR(epayload)) { > ret = PTR_ERR(epayload); > goto out; > } > - ret = encrypted_init(epayload, master_desc, decrypted_datalen, > + ret = encrypted_init(epayload, format, master_desc, decrypted_datalen, > hex_encoded_iv); > if (ret < 0) { > kfree(epayload); > @@ -728,6 +787,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) > struct encrypted_key_payload *new_epayload; > char *buf; > char *new_master_desc = NULL; > + const char *format = NULL; > int ret = 0; > > if (datalen <= 0 || datalen > 32767 || !data) > @@ -739,7 +799,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) > > buf[datalen] = 0; > memcpy(buf, data, datalen); > - ret = datablob_parse(buf, &new_master_desc, NULL, NULL); > + ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL); > if (ret < 0) > goto out; > > @@ -747,18 +807,19 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) > if (ret < 0) > goto out; > > - new_epayload = encrypted_key_alloc(key, new_master_desc, > - epayload->datalen); > + new_epayload = encrypted_key_alloc(key, epayload->format, > + new_master_desc, epayload->datalen); > if (IS_ERR(new_epayload)) { > ret = PTR_ERR(new_epayload); > goto out; > } > > - __ekey_init(new_epayload, new_master_desc, epayload->datalen); > + __ekey_init(new_epayload, epayload->format, new_master_desc, > + epayload->datalen); > > memcpy(new_epayload->iv, epayload->iv, ivsize); > - memcpy(new_epayload->decrypted_data, epayload->decrypted_data, > - epayload->decrypted_datalen); > + memcpy(new_epayload->payload_data, epayload->payload_data, > + epayload->payload_datalen); > > rcu_assign_pointer(key->payload.data, new_epayload); > call_rcu(&epayload->rcu, encrypted_rcu_free); -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html