[opensuse-kernel] [RFC PATCH 00/18 v2] Signature verification of hibernate snapshot
Hi experts, This patchset is the implementation for signature verification of hibernate snapshot image. The origin idea is from Jiri Kosina: Let EFI bootloader generate key-pair in UEFI secure boot environment, then pass it to kernel for sign/verify S4 image. Due to there have potential threat from the S4 image hacked, it may cause SUSE lost the trust in UEFI secure boot. The hacker attack the S4 snapshot image in swap partition through whatever exploit from another trusted OS, an the exploit may don't need physical access machine. So, this patchset give the ability to kernel for parsing the RSA key-pair from EFI bootloader, then using the private key to generate the signature of S4 snapshot image. Kernel put the signature to snapshot header, then verify the signature when kernel try to recover snapshot image to memory. ============== How To Enable ============== Set enable the CONFIG_SNAPSHOT_VERIFICATION kernel config. And you can also choice which hash algorithm should snapshot be signed with. Then rebuild kernel. Please note this function need UEFI bootloader generate key-pair in UEFI secure boot environment, e.g. shim. current shim implementation by Gary Lin: https://build.opensuse.org/package/show/home:gary_lin:UEFI/shim Please use the shim from the above URL if you want to try. And, Please remember add this shim to db in UEFI because it didn't sign by SUSE or Microsoft key. ========= Behavior ========= The RSA key-pair are generated by EFI bootloader(e.g. shim) in UEFI secure boot environment, so currently this function binding with EFI secure boot enabled. The kernel behavior is: + UEFI Secure Boot ON, Kernel found key-pair from shim: Kernel will do the S4 signature check. + UEFI Secure Boot ON, Kernel didn't find key-pair from shim: Kernel will lock down S4 function. + UEFI Secure Boot OFF Kernel will disable S4 signature check, and ignore any keys from EFI bootloader. On EFI bootloader side, the behavior as following: + First, kernel will check the following 2 EFI variable: S4SignKey-fe141863-c070-478e-b8a3-878a5dc9ef21 [Runtime][Non-Volatile] S4WakeKey-fe141863-c070-478e-b8a3-878a5dc9ef21 [Runtime][Volatile] S4SignKey and S4WakeKey is a RSA key-pair: - S4SignKey is a private key that's used to generate signature of S4 snapshot. The blob format of S4SignKey is PKCS#8 format, it should packaged a RSA private key that's followed PKCS#1. - S4WakeKey is a public key that's used to verify signature of S4 snapshot. The blob format of S4WakeKey is X.509 format, it should packaged a RSA public key that's followed PKCS#1. + EFI bootloader must generate RSA key-pair when system boot: - Bootloader store the public key to EFI boottime variable by itself - Bootloader put The private key to S4SignKey EFI variable for forward to kernel. + Kernel will load the S4SignKey blob to RAM when booting and delete it immediately. This private key will sign snapshot when S4. + When machine resume from hibernate: - EFI bootloader should copy the public key from boottime variable to S4WakeKey EFI variable. - Bootloader need generated a new key-pair for next round S4 usage. It should put new privat ekey to S4SignKey variable. ============== Implementation ============== Whole implementation including 3 part: shim, asymmetric keys and hibernate: + shim: Currently solution implemented by Gary Lin: https://build.opensuse.org/package/show/home:gary_lin:UEFI/shim Please use the shim from the above URL if you want to try. And, Please remember add this shim to db because it didn't sign by SUSE or Microsoft key. + Asymmetric keys: This patchset implemented PKCS#8 and RSA private key parser, it also implement the signature verification operation of RSASSA-PKCS1-v_5 in PKCS#1 spec. [RFC3447 sec 8.2.2] Set CONFIG_PKCS8_PRIVATE_KEY_INFO_PARSER=y will give kernel the abilities to parsing private key and verify signature. + Hibernate: Set CONFIG_SNAPSHOT_VERIFICATION=y will give enable the function of snapshot signature generation and verification. We reserved 512 byes size in snapshot header for store the signature that's generated from the digest with SHA256 algorithms. For adapt S4 signature check to secure boot, I have porting 3 patches from Fedora kernel to openSUSE, authors are Josh Boyer and Matthew Garrett. I also add Cc. to them. Please help review this RFC patchset! Appreciate for any comments! Josh Boyer (1): Secure boot: Add a dummy kernel parameter that will switch on Secure Boot mode Lee, Chun-Yi (15): asymmetric keys: add interface and skeleton for implement signature generation asymmetric keys: implement EMSA_PKCS1-v1_5-ENCODE in rsa asymmetric keys: separate the length checking of octet string from RSA_I2OSP asymmetric keys: implement OS2IP in rsa asymmetric keys: implement RSASP1 asymmetric keys: support parsing PKCS #8 private key information asymmetric keys: explicitly add the leading zero byte to encoded message Hibernate: introduced RSA key-pair to verify signature of snapshot Hibernate: generate and verify signature of snapshot Hibernate: Avoid S4 sign key data included in snapshot image Hibernate: fix the race condition of remove S4 sign key Hibernate: applied SNAPSHOT_VERIFICATION config to switch signature check Hibernate: adapt to UEFI secure boot with signature check Hibernate: show the verification time for monitor performance Hibernate: introduced SNAPSHOT_SIG_HASH config for select hash algorithm Matthew Garrett (2): Secure boot: Add new capability efi: Enable secure boot lockdown automatically when enabled in firmware Documentation/kernel-parameters.txt | 7 + Documentation/x86/zero-page.txt | 2 + arch/x86/boot/compressed/eboot.c | 32 +++ arch/x86/include/asm/bootparam_utils.h | 8 +- arch/x86/include/uapi/asm/bootparam.h | 3 +- arch/x86/kernel/setup.c | 7 + crypto/asymmetric_keys/Kconfig | 11 + crypto/asymmetric_keys/Makefile | 16 ++ crypto/asymmetric_keys/pkcs8.asn1 | 19 ++ crypto/asymmetric_keys/pkcs8_info_parser.c | 152 ++++++++++++++ crypto/asymmetric_keys/pkcs8_parser.h | 23 ++ crypto/asymmetric_keys/pkcs8_private_key.c | 148 ++++++++++++++ crypto/asymmetric_keys/pkcs8_rsakey.asn1 | 29 +++ crypto/asymmetric_keys/private_key.h | 29 +++ crypto/asymmetric_keys/public_key.c | 32 +++ crypto/asymmetric_keys/rsa.c | 283 +++++++++++++++++++++++++-- crypto/asymmetric_keys/signature.c | 28 +++ include/crypto/public_key.h | 28 +++ include/keys/asymmetric-subtype.h | 6 + include/linux/cred.h | 2 + include/linux/efi.h | 1 + include/uapi/linux/capability.h | 6 +- kernel/cred.c | 17 ++ kernel/power/Kconfig | 62 ++++++- kernel/power/Makefile | 1 + kernel/power/hibernate.c | 42 ++++ kernel/power/hibernate_keys.c | 262 ++++++++++++++++++++++++ kernel/power/main.c | 11 +- kernel/power/power.h | 27 +++ kernel/power/snapshot.c | 299 +++++++++++++++++++++++++++- kernel/power/swap.c | 18 ++ kernel/power/user.c | 24 +++ 32 files changed, 1611 insertions(+), 24 deletions(-) create mode 100644 crypto/asymmetric_keys/pkcs8.asn1 create mode 100644 crypto/asymmetric_keys/pkcs8_info_parser.c create mode 100644 crypto/asymmetric_keys/pkcs8_parser.h create mode 100644 crypto/asymmetric_keys/pkcs8_private_key.c create mode 100644 crypto/asymmetric_keys/pkcs8_rsakey.asn1 create mode 100644 crypto/asymmetric_keys/private_key.h create mode 100644 kernel/power/hibernate_keys.c -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
Add generate_signature interface on signature.c, asymmetric-subtype and rsa.c for prepare to implement signature generation. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- crypto/asymmetric_keys/private_key.h | 29 +++++++++++++++++++++++++++++ crypto/asymmetric_keys/public_key.c | 31 +++++++++++++++++++++++++++++++ crypto/asymmetric_keys/rsa.c | 22 ++++++++++++++++++++++ crypto/asymmetric_keys/signature.c | 28 ++++++++++++++++++++++++++++ include/crypto/public_key.h | 25 +++++++++++++++++++++++++ include/keys/asymmetric-subtype.h | 6 ++++++ 6 files changed, 141 insertions(+), 0 deletions(-) create mode 100644 crypto/asymmetric_keys/private_key.h diff --git a/crypto/asymmetric_keys/private_key.h b/crypto/asymmetric_keys/private_key.h new file mode 100644 index 0000000..c022eee --- /dev/null +++ b/crypto/asymmetric_keys/private_key.h @@ -0,0 +1,29 @@ +/* Private key algorithm internals + * + * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved. + * Written by Chun-Yi Lee (jlee@suse.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <crypto/public_key.h> + +extern struct asymmetric_key_subtype private_key_subtype; + +/* + * Private key algorithm definition. + */ +struct private_key_algorithm { + const char *name; + u8 n_pub_mpi; /* Number of MPIs in public key */ + u8 n_sec_mpi; /* Number of MPIs in secret key */ + u8 n_sig_mpi; /* Number of MPIs in a signature */ + struct public_key_signature* (*generate_signature)( + const struct private_key *key, u8 *M, + enum pkey_hash_algo hash_algo, const bool hash); +}; + +extern const struct private_key_algorithm RSA_private_key_algorithm; diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index cb2e291..97ff932 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -19,6 +19,7 @@ #include <linux/seq_file.h> #include <keys/asymmetric-subtype.h> #include "public_key.h" +#include "private_key.h" MODULE_LICENSE("GPL"); @@ -96,6 +97,24 @@ static int public_key_verify_signature(const struct key *key, } /* + * Generate a signature using a private key. + */ +static struct public_key_signature *private_key_generate_signature( + const struct key *key, u8 *M, enum pkey_hash_algo hash_algo, + const bool hash) +{ + const struct private_key *pk = key->payload.data; + + pr_info("private_key_generate_signature start"); + + if (!pk->algo->generate_signature) + return ERR_PTR(-ENOTSUPP); + + return pk->algo->generate_signature(pk, M, hash_algo, hash); + +} + +/* * Public key algorithm asymmetric key subtype */ struct asymmetric_key_subtype public_key_subtype = { @@ -106,3 +125,15 @@ struct asymmetric_key_subtype public_key_subtype = { .verify_signature = public_key_verify_signature, }; EXPORT_SYMBOL_GPL(public_key_subtype); + +/* + * Private key algorithm asymmetric key subtype + */ +struct asymmetric_key_subtype private_key_subtype = { + .owner = THIS_MODULE, + .name = "private_key", + .describe = public_key_describe, + .destroy = public_key_destroy, + .generate_signature = private_key_generate_signature, +}; +EXPORT_SYMBOL_GPL(private_key_subtype); diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c index 4a6a069..95aab83 100644 --- a/crypto/asymmetric_keys/rsa.c +++ b/crypto/asymmetric_keys/rsa.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include "public_key.h" +#include "private_key.h" MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("RSA Public Key Algorithm"); @@ -267,6 +268,18 @@ error: return ret; } +/* + * Perform the generation step [RFC3447 sec 8.2.1]. + */ +static struct public_key_signature *RSA_generate_signature( + const struct private_key *key, u8 *M, + enum pkey_hash_algo hash_algo, const bool hash) +{ + pr_info("RSA_generate_signature start"); + + return 0; +} + const struct public_key_algorithm RSA_public_key_algorithm = { .name = "RSA", .n_pub_mpi = 2, @@ -275,3 +288,12 @@ const struct public_key_algorithm RSA_public_key_algorithm = { .verify_signature = RSA_verify_signature, }; EXPORT_SYMBOL_GPL(RSA_public_key_algorithm); + +const struct private_key_algorithm RSA_private_key_algorithm = { + .name = "RSA", + .n_pub_mpi = 2, + .n_sec_mpi = 3, + .n_sig_mpi = 1, + .generate_signature = RSA_generate_signature, +}; +EXPORT_SYMBOL_GPL(RSA_private_key_algorithm); diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c index 50b3f88..a1bf6be 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -47,3 +47,31 @@ int verify_signature(const struct key *key, return ret; } EXPORT_SYMBOL_GPL(verify_signature); + +/** + * generate_signature - Initiate the use of an asymmetric key to generate a signature + * @key: The asymmetric key to generate against + * @M: The message to be signed, or a hash result. Dependent on the hash parameter + * @hash_algo: The hash algorithm to generate digest + * @hash: true means M is a original mesagse, false means M is a hash result + * + * Returns public_key-signature if successful or else an error. + */ +struct public_key_signature *generate_signature(const struct key *key, u8 *M, + enum pkey_hash_algo hash_algo, const bool hash) +{ + const struct asymmetric_key_subtype *subtype; + + pr_info("==>%s()\n", __func__); + + if (key->type != &key_type_asymmetric) + return ERR_PTR(-EINVAL); + subtype = asymmetric_key_subtype(key); + if (!subtype || !key->payload.data) + return ERR_PTR(-EINVAL); + if (!subtype->generate_signature) + return ERR_PTR(-ENOTSUPP); + + return subtype->generate_signature(key, M, hash_algo, hash); +} +EXPORT_SYMBOL_GPL(generate_signature); diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index f5b0224..d44b29f 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -79,6 +79,29 @@ struct public_key { }; }; +struct private_key { + const struct private_key_algorithm *algo; + u8 capabilities; + enum pkey_id_type id_type:8; + union { + MPI mpi[5]; + struct { + MPI p; /* DSA prime */ + MPI q; /* DSA group order */ + MPI g; /* DSA group generator */ + MPI y; /* DSA public-key value = g^x mod p */ + MPI x; /* DSA secret exponent (if present) */ + } dsa; + struct { + MPI n; /* RSA public modulus */ + MPI e; /* RSA public encryption exponent */ + MPI d; /* RSA secret encryption exponent (if present) */ + MPI p; /* RSA secret prime (if present) */ + MPI q; /* RSA secret prime (if present) */ + } rsa; + }; +}; + extern void public_key_destroy(void *payload); /* @@ -104,5 +127,7 @@ struct public_key_signature { struct key; extern int verify_signature(const struct key *key, const struct public_key_signature *sig); +extern struct public_key_signature *generate_signature(const struct key *key, + u8 *M, enum pkey_hash_algo hash_algo, const bool hash); #endif /* _LINUX_PUBLIC_KEY_H */ diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h index 4b840e8..af79939 100644 --- a/include/keys/asymmetric-subtype.h +++ b/include/keys/asymmetric-subtype.h @@ -18,6 +18,7 @@ #include <keys/asymmetric-type.h> struct public_key_signature; +enum pkey_hash_algo; /* * Keys of this type declare a subtype that indicates the handlers and @@ -37,6 +38,11 @@ struct asymmetric_key_subtype { /* Verify the signature on a key of this subtype (optional) */ int (*verify_signature)(const struct key *key, const struct public_key_signature *sig); + + /* Generate the signature by key of this subtype (optional) */ + struct public_key_signature* (*generate_signature) + (const struct key *key, u8 *M, enum pkey_hash_algo hash_algo, + const bool hash); }; /** -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
Implement EMSA_PKCS1-v1_5-ENCODE [RFC3447 sec 9.2] in rsa.c. It's the first step of signature generation operation (RSASSA-PKCS1-v1_5-SIGN). This patch is temporary set emLen to pks->k, and temporary set EM to pks->S for debugging. We will replace the above values to real signature after implement RSASP1. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- crypto/asymmetric_keys/rsa.c | 158 +++++++++++++++++++++++++++++++++++++++++- include/crypto/public_key.h | 2 + 2 files changed, 158 insertions(+), 2 deletions(-) diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c index 95aab83..6996ff7 100644 --- a/crypto/asymmetric_keys/rsa.c +++ b/crypto/asymmetric_keys/rsa.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <crypto/hash.h> #include "public_key.h" #include "private_key.h" @@ -152,6 +153,125 @@ static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X) } /* + * EMSA_PKCS1-v1_5-ENCODE [RFC3447 sec 9.2] + * @M: message to be signed, and octet string + * @emLen: intended length in octets of the encoded message + * @hash_algo: hash function (option) + * @hash: true means hash M, otherwise M is digest + * @EM: encoded message, an octet string of length emLen + */ +static int EMSA_PKCS1_v1_5_ENCODE(const u8 *M, size_t emLen, + enum pkey_hash_algo hash_algo, const bool hash, + u8 **_EM, struct public_key_signature *pks) +{ + u8 *digest; + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t digest_size, desc_size; + size_t tLen; + u8 *T, *PS, *EM; + int i, ret; + + pr_info("EMSA_PKCS1_v1_5_ENCODE start\n"); + + if (!RSA_ASN1_templates[hash_algo].data) + ret = -ENOTSUPP; + else + pks->pkey_hash_algo = hash_algo; + + /* 1) Apply the hash function to the message M to produce a hash value H */ + tfm = crypto_alloc_shash(pkey_hash_algo[hash_algo], 0, 0); + if (IS_ERR(tfm)) + return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + + ret = -ENOMEM; + + digest = kzalloc(digest_size + desc_size, GFP_KERNEL); + if (!digest) + goto error_digest; + pks->digest = digest; + pks->digest_size = digest_size; + + if (hash) { + desc = (void *) digest + digest_size; + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_shash; + ret = crypto_shash_finup(desc, M, sizeof(M), pks->digest); + if (ret < 0) + goto error_shash; + } else { + memcpy(pks->digest, M, pks->digest_size); + pks->digest_size = digest_size; + } + crypto_free_shash(tfm); + + /* 2) Encode the algorithm ID for the hash function and the hash value into + * an ASN.1 value of type DigestInfo with the DER. Let T be the DER encoding of + * the DigestInfo value and let tLen be the length in octets of T. + */ + tLen = RSA_ASN1_templates[hash_algo].size + pks->digest_size; + T = kmalloc(tLen, GFP_KERNEL); + if (!T) + goto error_T; + + memcpy(T, RSA_ASN1_templates[hash_algo].data, RSA_ASN1_templates[hash_algo].size); + memcpy(T + RSA_ASN1_templates[hash_algo].size, pks->digest, pks->digest_size); + + /* 3) check If emLen < tLen + 11, output "intended encoded message length too short" */ + if (emLen < tLen + 11) { + ret = EINVAL; + goto error_emLen; + } + + /* 4) Generate an octet string PS consisting of emLen - tLen - 3 octets with 0xff. */ + PS = kmalloc(emLen - tLen - 3, GFP_KERNEL); + if (!PS) + goto error_P; + + for (i = 0; i < (emLen - tLen - 3); i++) + PS[i] = 0xff; + + /* 5) Concatenate PS, the DER encoding T, and other padding to form the encoded + * message EM as EM = 0x00 || 0x01 || PS || 0x00 || T + */ + EM = kmalloc(3 + emLen - tLen - 3 + tLen, GFP_KERNEL); + if (!EM) + goto error_EM; + + EM[0] = 0x00; + EM[1] = 0x01; + memcpy(EM + 2, PS, emLen - tLen - 3); + EM[2 + emLen - tLen - 3] = 0x00; + memcpy(EM + 2 + emLen - tLen - 3 + 1, T, tLen); + + *_EM = EM; + + kfree(PS); + kfree(T); + + return 0; + +error_EM: + kfree(PS); +error_P: +error_emLen: + kfree(T); +error_T: +error_shash: + kfree(digest); +error_digest: + crypto_free_shash(tfm); + return ret; +} + +/* * Perform the RSA signature verification. * @H: Value of hash of data and metadata * @EM: The computed signature value @@ -275,9 +395,43 @@ static struct public_key_signature *RSA_generate_signature( const struct private_key *key, u8 *M, enum pkey_hash_algo hash_algo, const bool hash) { - pr_info("RSA_generate_signature start"); + struct public_key_signature *pks; + u8 *EM = NULL; + size_t emLen; + int ret; - return 0; + pr_info("RSA_generate_signature start\n"); + + ret = -ENOMEM; + pks = kzalloc(sizeof(*pks), GFP_KERNEL); + if (!pks) + goto error_no_pks; + + /* 1): EMSA-PKCS1-v1_5 encoding: */ + /* Use the private key modulus size to be EM length */ + emLen = mpi_get_nbits(key->rsa.n); + emLen = (emLen + 7) / 8; + + ret = EMSA_PKCS1_v1_5_ENCODE(M, emLen, hash_algo, hash, &EM, pks); + if (ret < 0) + goto error_v1_5_encode; + + /* TODO 2): m = OS2IP (EM) */ + + /* TODO 3): s = RSASP1 (K, m) */ + + /* TODO 4): S = I2OSP (s, k) */ + + /* TODO: signature S to a u8* S or set to sig->rsa.s? */ + pks->S = EM; /* TODO: temporary set S to EM */ + + return pks; + +error_v1_5_encode: + kfree(pks); +error_no_pks: + pr_info("<==%s() = %d\n", __func__, ret); + return ERR_PTR(ret); } const struct public_key_algorithm RSA_public_key_algorithm = { diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index d44b29f..1cdf457 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -110,6 +110,8 @@ extern void public_key_destroy(void *payload); struct public_key_signature { u8 *digest; u8 digest_size; /* Number of bytes in digest */ + u8 *S; /* signature S of length k octets */ + size_t k; /* length k of signature S */ u8 nr_mpi; /* Occupancy of mpi[] */ enum pkey_hash_algo pkey_hash_algo : 8; union { -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
Due to RSA_I2OSP is not only used by signature verification path but also used in signature generation path. So, separate the length checking of octet string because it's not for generate 0x00 0x01 leading string when used in signature generation. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- crypto/asymmetric_keys/rsa.c | 33 ++++++++++++++++++++++++--------- 1 files changed, 24 insertions(+), 9 deletions(-) diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c index 6996ff7..c26ae77 100644 --- a/crypto/asymmetric_keys/rsa.c +++ b/crypto/asymmetric_keys/rsa.c @@ -121,12 +121,30 @@ static int RSAVP1(const struct public_key *key, MPI s, MPI *_m) /* * Integer to Octet String conversion [RFC3447 sec 4.1] */ -static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X) +static int _RSA_I2OSP(MPI x, unsigned *X_size, u8 **_X) { - unsigned X_size, x_size; int X_sign; u8 *X; + X = mpi_get_buffer(x, X_size, &X_sign); + if (!X) + return -ENOMEM; + if (X_sign < 0) { + kfree(X); + return -EBADMSG; + } + + *_X = X; + return 0; +} + +static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X) +{ + unsigned x_size; + unsigned X_size; + u8 *X = NULL; + int ret; + /* Make sure the string is the right length. The number should begin * with { 0x00, 0x01, ... } so we have to account for 15 leading zero * bits not being reported by MPI. @@ -136,13 +154,10 @@ static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X) if (x_size != xLen * 8 - 15) return -ERANGE; - X = mpi_get_buffer(x, &X_size, &X_sign); - if (!X) - return -ENOMEM; - if (X_sign < 0) { - kfree(X); - return -EBADMSG; - } + ret = _RSA_I2OSP(x, &X_size, &X); + if (ret < 0) + return ret; + if (X_size != xLen - 1) { kfree(X); return -EBADMSG; -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
Implement Octet String to Integer conversion [RFC3447 sec 4.2] in rsa.c. It's the second step of signature generation operation. This patch is temporary set non-RSASP1 message to pks->S for debugging. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- crypto/asymmetric_keys/rsa.c | 29 ++++++++++++++++++++++++----- 1 files changed, 24 insertions(+), 5 deletions(-) diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c index c26ae77..0862018 100644 --- a/crypto/asymmetric_keys/rsa.c +++ b/crypto/asymmetric_keys/rsa.c @@ -168,6 +168,20 @@ static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X) } /* + * Octet String to Integer conversion [RFC3447 sec 4.2] + */ +static int RSA_OS2IP(u8 *X, size_t XLen, MPI *_x) +{ + MPI x; + + x = mpi_alloc((XLen + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB); + mpi_set_buffer(x, X, XLen, 0); + + *_x = x; + return 0; +} + +/* * EMSA_PKCS1-v1_5-ENCODE [RFC3447 sec 9.2] * @M: message to be signed, and octet string * @emLen: intended length in octets of the encoded message @@ -412,6 +426,9 @@ static struct public_key_signature *RSA_generate_signature( { struct public_key_signature *pks; u8 *EM = NULL; + MPI m = NULL; + MPI s = NULL; + unsigned X_size; size_t emLen; int ret; @@ -431,14 +448,16 @@ static struct public_key_signature *RSA_generate_signature( if (ret < 0) goto error_v1_5_encode; - /* TODO 2): m = OS2IP (EM) */ + /* 2): m = OS2IP (EM) */ + ret = RSA_OS2IP(EM, emLen, &m); + if (ret < 0) + goto error_v1_5_encode; /* TODO 3): s = RSASP1 (K, m) */ + s = m; - /* TODO 4): S = I2OSP (s, k) */ - - /* TODO: signature S to a u8* S or set to sig->rsa.s? */ - pks->S = EM; /* TODO: temporary set S to EM */ + /* 4): S = I2OSP (s, k) */ + _RSA_I2OSP(s, &X_size, &pks->S); return pks; -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
Implement RSASP1 and fill-in the following data to public key signature structure: signature length (pkcs->k), signature octet strings (pks->S) and MPI of signature (pks->rsa.s). Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- crypto/asymmetric_keys/rsa.c | 47 +++++++++++++++++++++++++++++++++++++++-- 1 files changed, 44 insertions(+), 3 deletions(-) diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c index 0862018..e60defe 100644 --- a/crypto/asymmetric_keys/rsa.c +++ b/crypto/asymmetric_keys/rsa.c @@ -86,6 +86,39 @@ static const struct { }; /* + * RSASP1() function [RFC3447 sec 5.2.1] + */ +static int RSASP1(const struct private_key *key, MPI m, MPI *_s) +{ + MPI s; + int ret; + + /* (1) Validate 0 <= m < n */ + if (mpi_cmp_ui(m, 0) < 0) { + kleave(" = -EBADMSG [m < 0]"); + return -EBADMSG; + } + if (mpi_cmp(m, key->rsa.n) >= 0) { + kleave(" = -EBADMSG [m >= n]"); + return -EBADMSG; + } + + s = mpi_alloc(0); + if (!s) + return -ENOMEM; + + /* (2) s = m^d mod n */ + ret = mpi_powm(s, m, key->rsa.d, key->rsa.n); + if (ret < 0) { + mpi_free(s); + return ret; + } + + *_s = s; + return 0; +} + +/* * RSAVP1() function [RFC3447 sec 5.2.2] */ static int RSAVP1(const struct public_key *key, MPI s, MPI *_m) @@ -173,9 +206,12 @@ static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X) static int RSA_OS2IP(u8 *X, size_t XLen, MPI *_x) { MPI x; + int ret; x = mpi_alloc((XLen + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB); - mpi_set_buffer(x, X, XLen, 0); + ret = mpi_set_buffer(x, X, XLen, 0); + if (ret < 0) + return ret; *_x = x; return 0; @@ -453,8 +489,13 @@ static struct public_key_signature *RSA_generate_signature( if (ret < 0) goto error_v1_5_encode; - /* TODO 3): s = RSASP1 (K, m) */ - s = m; + /* 3): s = RSASP1 (K, m) */ + RSASP1(key, m, &s); + + pks->rsa.s = s; + pks->nr_mpi = 1; + pks->k = mpi_get_nbits(s); + pks->k = (pks->k + 7) / 8; /* 4): S = I2OSP (s, k) */ _RSA_I2OSP(s, &X_size, &pks->S); -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
Add ASN.1 files and parser to support parsing PKCS #8 private key information. It's better then direct parsing pure private key because PKCS #8 has a privateKeyAlgorithm to indicate the algorithm of private key, e.g. RSA from PKCS #1 Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- crypto/asymmetric_keys/Kconfig | 11 ++ crypto/asymmetric_keys/Makefile | 16 +++ crypto/asymmetric_keys/pkcs8.asn1 | 19 ++++ crypto/asymmetric_keys/pkcs8_info_parser.c | 152 ++++++++++++++++++++++++++++ crypto/asymmetric_keys/pkcs8_parser.h | 23 ++++ crypto/asymmetric_keys/pkcs8_private_key.c | 148 +++++++++++++++++++++++++++ crypto/asymmetric_keys/pkcs8_rsakey.asn1 | 29 ++++++ crypto/asymmetric_keys/public_key.c | 1 + include/crypto/public_key.h | 1 + 9 files changed, 400 insertions(+), 0 deletions(-) create mode 100644 crypto/asymmetric_keys/pkcs8.asn1 create mode 100644 crypto/asymmetric_keys/pkcs8_info_parser.c create mode 100644 crypto/asymmetric_keys/pkcs8_parser.h create mode 100644 crypto/asymmetric_keys/pkcs8_private_key.c create mode 100644 crypto/asymmetric_keys/pkcs8_rsakey.asn1 diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 6d2c2ea..c0ebd57 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -35,4 +35,15 @@ config X509_CERTIFICATE_PARSER data and provides the ability to instantiate a crypto key from a public key packet found inside the certificate. +config PKCS8_PRIVATE_KEY_INFO_PARSER + tristate "PKCS #8 private key info parser" + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select ASN1 + select OID_REGISTRY + select CRYPTO_SHA256 + help + This option provides support for parsing PKCS #8 RSA private key info + format blobs for key data and provides the ability to instantiate a + crypto key from a private key packet. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 0727204..65fbc45 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -23,5 +23,21 @@ $(obj)/x509_cert_parser.o: $(obj)/x509-asn1.h $(obj)/x509_rsakey-asn1.h $(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h +# +# PKCS8 Private Key handling +# +obj-$(CONFIG_PKCS8_PRIVATE_KEY_INFO_PARSER) += pkcs8_key_parser.o +pkcs8_key_parser-y := \ + pkcs8-asn1.o \ + pkcs8_rsakey-asn1.o \ + pkcs8_info_parser.o \ + pkcs8_private_key.o + +$(obj)/pkcs8_info_parser.o: $(obj)/pkcs8-asn1.c $(obj)/pkcs8_rsakey-asn1.h +$(obj)/pkcs8-asn1.o: $(obj)/pkcs8-asn1.c $(obj)/pkcs8-asn1.h +$(obj)/pkcs8_rsakey-asn1.o: $(obj)/pkcs8_rsakey-asn1.c $(obj)/pkcs8_rsakey-asn1.h + clean-files += x509-asn1.c x509-asn1.h clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h +clean-files += pkcs8-asn1.c pkcs8-asn1.h +clean-files += pkcs8_rsakey-asn1.c pkcs8_rsakey-asn1.h diff --git a/crypto/asymmetric_keys/pkcs8.asn1 b/crypto/asymmetric_keys/pkcs8.asn1 new file mode 100644 index 0000000..89e845d --- /dev/null +++ b/crypto/asymmetric_keys/pkcs8.asn1 @@ -0,0 +1,19 @@ +-- +-- Representation of RSA PKCS#8 private key information. +-- + +PrivateKeyInfo ::= SEQUENCE { + version Version, + privateKeyAlgorithm AlgorithmIdentifier, + privateKey OCTET STRING ({ pkcs8_extract_key_data }) + -- Does not support attributes + -- attributes [ 0 ] Attributes OPTIONAL + } + +-- Version ::= INTEGER { two-prime(0), multi(1) } +Version ::= INTEGER + +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER ({ pkcs8_note_OID }), + parameters ANY OPTIONAL + } diff --git a/crypto/asymmetric_keys/pkcs8_info_parser.c b/crypto/asymmetric_keys/pkcs8_info_parser.c new file mode 100644 index 0000000..c62ddc4 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs8_info_parser.c @@ -0,0 +1,152 @@ +/* X.509 certificate parser + * + * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved. + * Written by Lee, Chun-Yi (jlee@suse.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PKCS8: "fmt +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/oid_registry.h> +#include "public_key.h" +#include "pkcs8_parser.h" +#include "pkcs8-asn1.h" +#include "pkcs8_rsakey-asn1.h" + +struct pkcs8_parse_context { + struct pkcs8_info *info; /* Certificate being constructed */ + unsigned long data; /* Start of data */ + const void *key; /* Key data */ + size_t key_size; /* Size of key data */ + enum OID algo_oid; /* Algorithm OID */ + unsigned char nr_mpi; /* Number of MPIs stored */ +}; + +/* + * Free an PKCS #8 private key info + */ +void pkcs8_free_info(struct pkcs8_info *info) +{ + if (info) { + public_key_destroy(info->priv); + kfree(info); + } +} + +/* + * Parse an PKCS #8 Private Key Info + */ +struct pkcs8_info *pkcs8_info_parse(const void *data, size_t datalen) +{ + struct pkcs8_info *info; + struct pkcs8_parse_context *ctx; + long ret; + + ret = -ENOMEM; + info = kzalloc(sizeof(struct pkcs8_info), GFP_KERNEL); + if (!info) + goto error_no_info; + info->priv = kzalloc(sizeof(struct private_key), GFP_KERNEL); + if (!info->priv) + goto error_no_ctx; + ctx = kzalloc(sizeof(struct pkcs8_parse_context), GFP_KERNEL); + if (!ctx) + goto error_no_ctx; + + ctx->info = info; + ctx->data = (unsigned long)data; + + /* Attempt to decode the private key info */ + ret = asn1_ber_decoder(&pkcs8_decoder, ctx, data, datalen); + if (ret < 0) + goto error_decode; + + /* Decode the private key */ + ret = asn1_ber_decoder(&pkcs8_rsakey_decoder, ctx, + ctx->key, ctx->key_size); + if (ret < 0) + goto error_decode; + + kfree(ctx); + return info; + +error_decode: + kfree(ctx); +error_no_ctx: + pkcs8_free_info(info); +error_no_info: + return ERR_PTR(ret); +} + +/* + * Note an OID when we find one for later processing when we know how + * to interpret it. + */ +int pkcs8_note_OID(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs8_parse_context *ctx = context; + + ctx->algo_oid = look_up_OID(value, vlen); + if (ctx->algo_oid == OID__NR) { + char buffer[50]; + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_debug("Unknown OID: [%lu] %s\n", + (unsigned long)value - ctx->data, buffer); + } + return 0; +} + +/* + * Extract the data for the private key algorithm + */ +int pkcs8_extract_key_data(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs8_parse_context *ctx = context; + + if (ctx->algo_oid != OID_rsaEncryption) + return -ENOPKG; + + ctx->info->privkey_algo = PKEY_ALGO_RSA; + ctx->key = value; + ctx->key_size = vlen; + return 0; +} + +/* + * Extract a RSA private key value + */ +int rsa_priv_extract_mpi(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs8_parse_context *ctx = context; + MPI mpi; + + if (ctx->nr_mpi >= ARRAY_SIZE(ctx->info->priv->mpi)) { + /* does not grab exponent1, exponent2 and coefficient */ + if (ctx->nr_mpi > 8) { + pr_err("Too many public key MPIs in pkcs1 private key\n"); + return -EBADMSG; + } else { + ctx->nr_mpi++; + return 0; + } + } + + mpi = mpi_read_raw_data(value, vlen); + if (!mpi) + return -ENOMEM; + + ctx->info->priv->mpi[ctx->nr_mpi++] = mpi; + return 0; +} diff --git a/crypto/asymmetric_keys/pkcs8_parser.h b/crypto/asymmetric_keys/pkcs8_parser.h new file mode 100644 index 0000000..829fd6a --- /dev/null +++ b/crypto/asymmetric_keys/pkcs8_parser.h @@ -0,0 +1,23 @@ +/* PKCS #8 parser internal definitions + * + * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved. + * Written by Lee, Chun-Yi (jlee@suse.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <crypto/public_key.h> + +struct pkcs8_info { + enum pkey_algo privkey_algo:8; /* Private key algorithm */ + struct private_key *priv; /* Private key */ +}; + +/* + * pkcs8_parser.c + */ +extern void pkcs8_free_info(struct pkcs8_info *info); +extern struct pkcs8_info *pkcs8_info_parse(const void *data, size_t datalen); diff --git a/crypto/asymmetric_keys/pkcs8_private_key.c b/crypto/asymmetric_keys/pkcs8_private_key.c new file mode 100644 index 0000000..cf2545b --- /dev/null +++ b/crypto/asymmetric_keys/pkcs8_private_key.c @@ -0,0 +1,148 @@ +/* Instantiate a private key crypto key + * + * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved. + * Written by Chun-Yi Lee (jlee@suse.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PKCS8: "fmt +#include <linux/module.h> +#include <linux/slab.h> +#include <keys/asymmetric-subtype.h> +#include <keys/asymmetric-parser.h> +#include <crypto/hash.h> +#include "private_key.h" +#include "pkcs8-asn1.h" +#include "pkcs8_parser.h" + +#define KEY_PREFIX "Private Key: " +#define FINGERPRINT_HASH "sha256" + +static const +struct private_key_algorithm *pkcs8_private_key_algorithms[PKEY_ALGO__LAST] = { + [PKEY_ALGO_DSA] = NULL, +#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \ + defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE) + [PKEY_ALGO_RSA] = &RSA_private_key_algorithm, +#endif +}; + +/* + * Attempt to parse a data blob for a private key. + */ +static int pkcs8_key_preparse(struct key_preparsed_payload *prep) +{ + struct pkcs8_info *info; + struct crypto_shash *tfm; + struct shash_desc *desc; + u8 *digest; + size_t digest_size, desc_size; + char *fingerprint, *description; + int i, ret; + + pr_info("pkcs8_key_preparse start\n"); + + info = pkcs8_info_parse(prep->data, prep->datalen); + if (IS_ERR(info)) + return PTR_ERR(info); + + info->priv->algo = pkcs8_private_key_algorithms[info->privkey_algo]; + info->priv->id_type = PKEY_ID_PKCS8; + + /* Hash the pkcs #8 blob to generate fingerprint */ + tfm = crypto_alloc_shash(FINGERPRINT_HASH, 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + goto error_shash; + } + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + + ret = -ENOMEM; + + digest = kzalloc(digest_size + desc_size, GFP_KERNEL); + if (!digest) + goto error_digest; + desc = (void *) digest + digest_size; + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_shash_init; + ret = crypto_shash_finup(desc, prep->data, prep->datalen, digest); + if (ret < 0) + goto error_shash_finup; + + fingerprint = kzalloc(digest_size * 2 + 1, GFP_KERNEL); + if (!fingerprint) + goto error_fingerprint; + for (i = 0; i < digest_size; i++) + sprintf(fingerprint + i * 2, "%02x", digest[i]); + + /* Propose a description */ + description = kzalloc(strlen(KEY_PREFIX) + strlen(fingerprint) + 1, GFP_KERNEL); + if (!description) + goto error_description; + sprintf(description, "%s", KEY_PREFIX); + memcpy(description + strlen(KEY_PREFIX), fingerprint, strlen(fingerprint)); + + /* We're pinning the module by being linked against it */ + __module_get(private_key_subtype.owner); + prep->type_data[0] = &private_key_subtype; + prep->type_data[1] = fingerprint; + prep->payload = info->priv; + prep->description = description; + + /* size of 4096 bits private key file is 2.3K */ + prep->quotalen = 700; + + pr_info("pkcs8_key_preparse done\n"); + + /* We've finished with the information */ + kfree(digest); + crypto_free_shash(tfm); + info->priv = NULL; + pkcs8_free_info(info); + + return 0; + +error_description: + kfree(fingerprint); +error_fingerprint: +error_shash_finup: +error_shash_init: + kfree(digest); +error_digest: + crypto_free_shash(tfm); +error_shash: + info->priv = NULL; + pkcs8_free_info(info); + return ret; +} + +static struct asymmetric_key_parser pkcs8_private_key_parser = { + .owner = THIS_MODULE, + .name = "pkcs8", + .parse = pkcs8_key_preparse, +}; + +/* + * Module stuff + */ +static int __init pkcs8_private_key_init(void) +{ + return register_asymmetric_key_parser(&pkcs8_private_key_parser); +} + +static void __exit pkcs8_private_key_exit(void) +{ + unregister_asymmetric_key_parser(&pkcs8_private_key_parser); +} + +module_init(pkcs8_private_key_init); +module_exit(pkcs8_private_key_exit); diff --git a/crypto/asymmetric_keys/pkcs8_rsakey.asn1 b/crypto/asymmetric_keys/pkcs8_rsakey.asn1 new file mode 100644 index 0000000..d997c5e --- /dev/null +++ b/crypto/asymmetric_keys/pkcs8_rsakey.asn1 @@ -0,0 +1,29 @@ +-- +-- Representation of RSA private key with information. +-- + +RSAPrivateKey ::= SEQUENCE { + version Version, + modulus INTEGER ({ rsa_priv_extract_mpi }), -- n + publicExponent INTEGER ({ rsa_priv_extract_mpi }), -- e + privateExponent INTEGER ({ rsa_priv_extract_mpi }), -- d + prime1 INTEGER ({ rsa_priv_extract_mpi }), -- p + prime2 INTEGER ({ rsa_priv_extract_mpi }), -- q + exponent1 INTEGER ({ rsa_priv_extract_mpi }), -- d mod (p-1) + exponent2 INTEGER ({ rsa_priv_extract_mpi }), -- d mod (q-1) + coefficient INTEGER ({ rsa_priv_extract_mpi }) -- (inverse of q) mod p + -- Doesn't support multi-prime + -- otherPrimeInfos [ 0 ] OtherPrimeInfos OPTIONAL + } + +-- Version ::= INTEGER { two-prime(0), multi(1) } +Version ::= INTEGER + +-- OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo +OtherPrimeInfos ::= SEQUENCE OF OtherPrimeInfo + +OtherPrimeInfo ::= SEQUENCE { + prime INTEGER, -- ri + exponent INTEGER, -- di + coefficient INTEGER -- ti +} diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index 97ff932..1636c4c 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -44,6 +44,7 @@ EXPORT_SYMBOL_GPL(pkey_hash_algo); const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = { [PKEY_ID_PGP] = "PGP", [PKEY_ID_X509] = "X509", + [PKEY_ID_PKCS8] = "PKCS8", }; EXPORT_SYMBOL_GPL(pkey_id_type); diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 1cdf457..e51f294 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -41,6 +41,7 @@ extern const char *const pkey_hash_algo[PKEY_HASH__LAST]; enum pkey_id_type { PKEY_ID_PGP, /* OpenPGP generated key ID */ PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */ + PKEY_ID_PKCS8, /* PKCS #8 Private Key */ PKEY_ID_TYPE__LAST }; -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
Per PKCS1 spec, the EMSA-PKCS1-v1_5 encoded message is leading by 0x00 0x01 in its first 2 bytes. The leading zero byte is suppressed by MPI so we pass a pointer to the _preceding_ byte to RSA_verify() in original code, but it has risk for the byte is not zero because it's not in EM buffer's scope, neither RSA_verify() nor mpi_get_buffer() didn't take care the leading byte. To avoid the risk, that's better we explicitly add the leading zero byte to EM for pass to RSA_verify(). This patch allocate a _EM buffer to capture the result from RSA_I2OSP(), then set the first byte to zero in EM and copy the remaining bytes from _EM. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- crypto/asymmetric_keys/rsa.c | 14 ++++++++++---- 1 files changed, 10 insertions(+), 4 deletions(-) diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c index e60defe..4baf431 100644 --- a/crypto/asymmetric_keys/rsa.c +++ b/crypto/asymmetric_keys/rsa.c @@ -401,6 +401,7 @@ static int RSA_verify_signature(const struct public_key *key, /* Variables as per RFC3447 sec 8.2.2 */ const u8 *H = sig->digest; u8 *EM = NULL; + u8 *_EM = NULL; MPI m = NULL; size_t k; @@ -435,14 +436,19 @@ static int RSA_verify_signature(const struct public_key *key, /* (2c) Convert the message representative (m) to an encoded message * (EM) of length k octets. * - * NOTE! The leading zero byte is suppressed by MPI, so we pass a - * pointer to the _preceding_ byte to RSA_verify()! + * NOTE! The leading zero byte is suppressed by MPI, so we add it + * back to EM before input to RSA_verify()! */ - ret = RSA_I2OSP(m, k, &EM); + ret = RSA_I2OSP(m, k, &_EM); if (ret < 0) goto error; - ret = RSA_verify(H, EM - 1, k, sig->digest_size, + EM = kmalloc(k, GFP_KERNEL); + memset(EM, 0, 1); + memcpy(EM + 1, _EM, k-1); + kfree(_EM); + + ret = RSA_verify(H, EM, k, sig->digest_size, RSA_ASN1_templates[sig->pkey_hash_algo].data, RSA_ASN1_templates[sig->pkey_hash_algo].size); -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matthew Garrett <mjg@redhat.com> Secure boot adds certain policy requirements, including that root must not be able to do anything that could cause the kernel to execute arbitrary code. The simplest way to handle this would seem to be to add a new capability and gate various functionality on that. We'll then strip it from the initial capability set if required. Signed-off-by: Matthew Garrett <mjg@redhat.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- include/uapi/linux/capability.h | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-) diff --git a/include/uapi/linux/capability.h b/include/uapi/linux/capability.h index ba478fa..7109e65 100644 --- a/include/uapi/linux/capability.h +++ b/include/uapi/linux/capability.h @@ -343,7 +343,11 @@ struct vfs_cap_data { #define CAP_BLOCK_SUSPEND 36 -#define CAP_LAST_CAP CAP_BLOCK_SUSPEND +/* Allow things that trivially permit root to modify the running kernel */ + +#define CAP_COMPROMISE_KERNEL 37 + +#define CAP_LAST_CAP CAP_COMPROMISE_KERNEL #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
On Fri, Aug 09, 2013 at 12:37:45PM +0800, Lee, Chun-Yi wrote:
From: Matthew Garrett <mjg@redhat.com>
Secure boot adds certain policy requirements, including that root must not be able to do anything that could cause the kernel to execute arbitrary code. The simplest way to handle this would seem to be to add a new capability and gate various functionality on that. We'll then strip it from the initial capability set if required.
Signed-off-by: Matthew Garrett <mjg@redhat.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- include/uapi/linux/capability.h | 6 +++++-
I know this has been submitted upstream, do you know what the status of it being accepted it? Oh, and nice job on this whole patchset. I might not agree that it's something we should care about, but some people might, so the option to have it is nice. thanks, greg k-h -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
On Thu, Aug 08, 2013 at 09:48:45PM -0700, Greg KH wrote:
On Fri, Aug 09, 2013 at 12:37:45PM +0800, Lee, Chun-Yi wrote:
From: Matthew Garrett <mjg@redhat.com>
Secure boot adds certain policy requirements, including that root must not be able to do anything that could cause the kernel to execute arbitrary code. The simplest way to handle this would seem to be to add a new capability and gate various functionality on that. We'll then strip it from the initial capability set if required.
Signed-off-by: Matthew Garrett <mjg@redhat.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- include/uapi/linux/capability.h | 6 +++++-
I know this has been submitted upstream, do you know what the status of it being accepted it?
It's run into the problem that capabilities are, broadly, unusable. Adding a new capability means adding new capability checks for fairly obvious reasons. But existing capability-aware applications will drop all privileges except the ones they know they need, which means they won't have the new capability and so fail the new capability checks. This means it's effectively impossible to add a new capability outside a small number of corner cases - it's fine adding capabilities to let you do things you can't currently do, but removing privileges just breaks things. So, despite the fact that I'm the one who proposed this originally, I don't think it's a viable approach. So what are the other options? We can add if (secure_boot) guards, but it's been made clear that people want something that has general benefit rather than being special-cased. All I've really been able to come up with is implementing a BSD-like securelevel. I hear the 90s are this decade's 80s. -- Matthew Garrett | mjg59@srcf.ucam.org -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
於 四,2013-08-08 於 21:48 -0700,Greg KH 提到:
On Fri, Aug 09, 2013 at 12:37:45PM +0800, Lee, Chun-Yi wrote:
From: Matthew Garrett <mjg@redhat.com>
Secure boot adds certain policy requirements, including that root must not be able to do anything that could cause the kernel to execute arbitrary code. The simplest way to handle this would seem to be to add a new capability and gate various functionality on that. We'll then strip it from the initial capability set if required.
Signed-off-by: Matthew Garrett <mjg@redhat.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- include/uapi/linux/capability.h | 6 +++++-
I know this has been submitted upstream, do you know what the status of it being accepted it?
As I know this patch and derivative patches are still pending on upstream.
Oh, and nice job on this whole patchset. I might not agree that it's something we should care about, but some people might, so the option to have it is nice.
thanks,
greg k-h
I understood! Thanks for your quick response! Joey Lee -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Josh Boyer <jwboyer@redhat.com> This forcibly drops CAP_COMPROMISE_KERNEL from both cap_permitted and cap_bset in the init_cred struct, which everything else inherits from. This works on any machine and can be used to develop even if the box doesn't have UEFI. Signed-off-by: Josh Boyer <jwboyer@redhat.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- Documentation/kernel-parameters.txt | 7 +++++++ kernel/cred.c | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 0 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 15356ac..6ad8292 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2784,6 +2784,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Note: increases power consumption, thus should only be enabled if running jitter sensitive (HPC/RT) workloads. + secureboot_enable= + [KNL] Enables an emulated UEFI Secure Boot mode. This + locks down various aspects of the kernel guarded by the + CAP_COMPROMISE_KERNEL capability. This includes things + like /dev/mem, IO port access, and other areas. It can + be used on non-UEFI machines for testing purposes. + security= [SECURITY] Choose a security module to enable at boot. If this boot parameter is not specified, only the first security module asking for security registration will be diff --git a/kernel/cred.c b/kernel/cred.c index e0573a4..c3f4e3e 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -565,6 +565,23 @@ void __init cred_init(void) 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); } +void __init secureboot_enable() +{ + pr_info("Secure boot enabled\n"); + cap_lower((&init_cred)->cap_bset, CAP_COMPROMISE_KERNEL); + cap_lower((&init_cred)->cap_permitted, CAP_COMPROMISE_KERNEL); +} + +/* Dummy Secure Boot enable option to fake out UEFI SB=1 */ +static int __init secureboot_enable_opt(char *str) +{ + int sb_enable = !!simple_strtol(str, NULL, 0); + if (sb_enable) + secureboot_enable(); + return 1; +} +__setup("secureboot_enable=", secureboot_enable_opt); + /** * prepare_kernel_cred - Prepare a set of credentials for a kernel service * @daemon: A userspace daemon to be used as a reference -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matthew Garrett <mjg@redhat.com> The firmware has a set of flags that indicate whether secure boot is enabled and enforcing. Use them to indicate whether the kernel should lock itself down. We also indicate the machine is in secure boot mode by adding the EFI_SECURE_BOOT bit for use with efi_enabled. Signed-off-by: Matthew Garrett <mjg@redhat.com> Signed-off-by: Josh Boyer <jwboyer@redhat.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- Documentation/x86/zero-page.txt | 2 ++ arch/x86/boot/compressed/eboot.c | 32 ++++++++++++++++++++++++++++++++ arch/x86/include/asm/bootparam_utils.h | 8 ++++++-- arch/x86/include/uapi/asm/bootparam.h | 3 ++- arch/x86/kernel/setup.c | 7 +++++++ include/linux/cred.h | 2 ++ include/linux/efi.h | 1 + 7 files changed, 52 insertions(+), 3 deletions(-) diff --git a/Documentation/x86/zero-page.txt b/Documentation/x86/zero-page.txt index 199f453..ff651d3 100644 --- a/Documentation/x86/zero-page.txt +++ b/Documentation/x86/zero-page.txt @@ -30,6 +30,8 @@ Offset Proto Name Meaning 1E9/001 ALL eddbuf_entries Number of entries in eddbuf (below) 1EA/001 ALL edd_mbr_sig_buf_entries Number of entries in edd_mbr_sig_buffer (below) +1EB/001 ALL kbd_status Numlock is enabled +1EC/001 ALL secure_boot Kernel should enable secure boot lockdowns 1EF/001 ALL sentinel Used to detect broken bootloaders 290/040 ALL edd_mbr_sig_buffer EDD MBR signatures 2D0/A00 ALL e820_map E820 memory map table diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index d606463..9baee3e 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -861,6 +861,36 @@ fail: return status; } +static int get_secure_boot(efi_system_table_t *_table) +{ + u8 sb, setup; + unsigned long datasize = sizeof(sb); + efi_guid_t var_guid = EFI_GLOBAL_VARIABLE_GUID; + efi_status_t status; + + status = efi_call_phys5(sys_table->runtime->get_variable, + L"SecureBoot", &var_guid, NULL, &datasize, &sb); + + if (status != EFI_SUCCESS) + return 0; + + if (sb == 0) + return 0; + + + status = efi_call_phys5(sys_table->runtime->get_variable, + L"SetupMode", &var_guid, NULL, &datasize, + &setup); + + if (status != EFI_SUCCESS) + return 0; + + if (setup == 1) + return 0; + + return 1; +} + /* * Because the x86 boot code expects to be passed a boot_params we * need to create one ourselves (usually the bootloader would create @@ -1169,6 +1199,8 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) goto fail; + boot_params->secure_boot = get_secure_boot(sys_table); + setup_graphics(boot_params); setup_efi_pci(boot_params); diff --git a/arch/x86/include/asm/bootparam_utils.h b/arch/x86/include/asm/bootparam_utils.h index 653668d..69a6c08 100644 --- a/arch/x86/include/asm/bootparam_utils.h +++ b/arch/x86/include/asm/bootparam_utils.h @@ -38,9 +38,13 @@ static void sanitize_boot_params(struct boot_params *boot_params) memset(&boot_params->olpc_ofw_header, 0, (char *)&boot_params->efi_info - (char *)&boot_params->olpc_ofw_header); - memset(&boot_params->kbd_status, 0, + memset(&boot_params->kbd_status, 0, sizeof(boot_params->kbd_status)); + /* don't clear boot_params->secure_boot. we set that ourselves + * earlier. + */ + memset(&boot_params->_pad5[0], 0, (char *)&boot_params->hdr - - (char *)&boot_params->kbd_status); + (char *)&boot_params->_pad5[0]); memset(&boot_params->_pad7[0], 0, (char *)&boot_params->edd_mbr_sig_buffer[0] - (char *)&boot_params->_pad7[0]); diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h index c15ddaf..85d7685 100644 --- a/arch/x86/include/uapi/asm/bootparam.h +++ b/arch/x86/include/uapi/asm/bootparam.h @@ -131,7 +131,8 @@ struct boot_params { __u8 eddbuf_entries; /* 0x1e9 */ __u8 edd_mbr_sig_buf_entries; /* 0x1ea */ __u8 kbd_status; /* 0x1eb */ - __u8 _pad5[3]; /* 0x1ec */ + __u8 secure_boot; /* 0x1ec */ + __u8 _pad5[2]; /* 0x1ed */ /* * The sentinel is set to a nonzero value (0xff) in header.S. * diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index f8ec578..2a8168a 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1129,6 +1129,13 @@ void __init setup_arch(char **cmdline_p) io_delay_init(); + if (boot_params.secure_boot) { +#ifdef CONFIG_EFI + set_bit(EFI_SECURE_BOOT, &x86_efi_facility); +#endif + secureboot_enable(); + } + /* * Parse the ACPI tables for possible boot-time SMP configuration. */ diff --git a/include/linux/cred.h b/include/linux/cred.h index 04421e8..9e69542 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -156,6 +156,8 @@ extern int set_security_override_from_ctx(struct cred *, const char *); extern int set_create_files_as(struct cred *, struct inode *); extern void __init cred_init(void); +extern void secureboot_enable(void); + /* * check for validity of credentials */ diff --git a/include/linux/efi.h b/include/linux/efi.h index 5f8f176..febce85 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -634,6 +634,7 @@ extern int __init efi_setup_pcdp_console(char *); #define EFI_RUNTIME_SERVICES 3 /* Can we use runtime services? */ #define EFI_MEMMAP 4 /* Can we use EFI memory map? */ #define EFI_64BIT 5 /* Is the firmware 64-bit? */ +#define EFI_SECURE_BOOT 6 /* Are we in Secure Boot mode? */ #ifdef CONFIG_EFI # ifdef CONFIG_X86 -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
Introduced a hibernate_key.c file to query the key pair from EFI variables and maintain key pair for check signature of S4 snapshot image. We loaded the private key when snapshot image stored success. This patch introduced 2 EFI variables for store the key to sign S4 image and verify signature when S4 wake up. The names and GUID are: S4SignKey-fe141863-c070-478e-b8a3-878a5dc9ef21 S4WakeKey-fe141863-c070-478e-b8a3-878a5dc9ef21 S4SignKey is used by EFI bootloader to pass the RSA private key that packaged by PKCS#8 format, kernel will read and parser it when system boot and reload it when S4 resume. EFI bootloader need gnerate a new private key when every time system boot. S4WakeKey is used to pass the RSA public key that packaged by X.509 certificate, kernel will read and parser it for check the signature of S4 snapshot image when S4 resume. The follow-up patch will remove S4SignKey and S4WakeKey after load them to kernel for avoid anyone can access it through efivarfs. v2: Add CONFIG_SNAPSHOT_VERIFICATION for build of hibernate_keys.c depend on Kconfig. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> Cc: Takashi Iwai <tiwai@suse.de> --- kernel/power/Makefile | 2 +- kernel/power/hibernate.c | 5 + kernel/power/hibernate_keys.c | 262 +++++++++++++++++++++++++++++++++++++++++ kernel/power/power.h | 10 ++ 4 files changed, 278 insertions(+), 1 deletions(-) create mode 100644 kernel/power/hibernate_keys.c Index: linux/kernel/power/Makefile =================================================================== --- linux.orig/kernel/power/Makefile +++ linux/kernel/power/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_SNAPSHOT_VERIFICATION) += hibernate_keys.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o Index: linux/kernel/power/hibernate.c =================================================================== --- linux.orig/kernel/power/hibernate.c +++ linux/kernel/power/hibernate.c @@ -28,6 +28,7 @@ #include <linux/syscore_ops.h> #include <linux/ctype.h> #include <linux/genhd.h> +#include <linux/key.h> #include "power.h" @@ -631,6 +632,7 @@ static void power_down(void) int hibernate(void) { int error; + int skey_error; lock_system_sleep(); /* The snapshot device should not be opened while we're running */ @@ -680,6 +682,9 @@ int hibernate(void) pm_restore_gfp_mask(); } else { pr_debug("PM: Image restored successfully.\n"); + skey_error = load_sign_key_data(); + if (skey_error) + pr_err("Load S4 sign key fail: %d", skey_error); } Thaw: Index: linux/kernel/power/hibernate_keys.c =================================================================== --- /dev/null +++ linux/kernel/power/hibernate_keys.c @@ -0,0 +1,262 @@ +#include <linux/sched.h> +#include <linux/efi.h> +#include <linux/mpi.h> +#include <crypto/public_key.h> +#include <keys/asymmetric-type.h> + +#include "power.h" + +#define EFI_HIBERNATE_GUID \ + EFI_GUID(0xfe141863, 0xc070, 0x478e, 0xb8, 0xa3, 0x87, 0x8a, 0x5d, 0xc9, 0xef, 0x21) +static efi_char16_t efi_s4_sign_key_name[10] = { 'S', '4', 'S', 'i', 'g', 'n', 'K', 'e', 'y', 0 }; +static efi_char16_t efi_s4_wake_key_name[10] = { 'S', '4', 'W', 'a', 'k', 'e', 'K', 'e', 'y', 0 }; + +static void *skey_page_addr; +static unsigned long skey_dsize; + +static int efi_status_to_err(efi_status_t status) +{ + int err; + + switch (status) { + case EFI_INVALID_PARAMETER: + err = -EINVAL; + break; + case EFI_OUT_OF_RESOURCES: + err = -ENOSPC; + break; + case EFI_DEVICE_ERROR: + err = -EIO; + break; + case EFI_WRITE_PROTECTED: + err = -EROFS; + break; + case EFI_SECURITY_VIOLATION: + err = -EACCES; + break; + case EFI_NOT_FOUND: + err = -EIO; + break; + default: + err = -EINVAL; + } + + return err; +} + +bool swsusp_page_is_sign_key(struct page *page) +{ + unsigned long skey_page_addr_pfn; + bool ret; + + if (!skey_page_addr || IS_ERR(skey_page_addr)) + return false; + + skey_page_addr_pfn = page_to_pfn(virt_to_page(skey_page_addr)); + ret = (page_to_pfn(page) == skey_page_addr_pfn) ? true : false; + if (ret) + pr_info("PM: Avoid snapshot the page of S4 sign key.\n"); + + return ret; +} + +static void *efi_key_load_data(efi_char16_t *var_name, unsigned long *datasize, + bool clean) +{ + u32 attributes; + void *data_page; + efi_status_t status; + + if (!efi_enabled(EFI_RUNTIME_SERVICES)) + return ERR_PTR(-EPERM); + + /* obtain the size */ + *datasize = 0; + status = efi.get_variable(var_name, &EFI_HIBERNATE_GUID, + NULL, datasize, NULL); + if (status != EFI_BUFFER_TOO_SMALL) { + data_page = ERR_PTR(efi_status_to_err(status)); + pr_err("PM: Couldn't get key data size: 0x%lx\n", status); + goto error_size; + } + if (*datasize > PAGE_SIZE) { + data_page = ERR_PTR(-EBADMSG); + goto error_size; + } + + data_page = (void *)get_zeroed_page(GFP_KERNEL); + if (!data_page) { + data_page = ERR_PTR(-ENOMEM); + goto error_page; + } + status = efi.get_variable(var_name, &EFI_HIBERNATE_GUID, + &attributes, datasize, data_page); + if (status) { + data_page = ERR_PTR(efi_status_to_err(status)); + pr_err("PM: Get key data error: %ld\n", PTR_ERR(data_page)); + goto error_get; + } + + if (!clean) + goto no_clean; + + /* clean S4 key data from EFI variable */ + status = efi.set_variable(var_name, &EFI_HIBERNATE_GUID, attributes, 0, NULL); + if (status != EFI_SUCCESS) + pr_warn("PM: Clean key data error: %lx, %d\n", status, efi_status_to_err(status)); + else + pr_info("PM: Clean key data success!"); + +no_clean: + return data_page; + +error_get: + free_page((unsigned long) data_page); + *datasize = 0; +error_page: +error_size: + return data_page; +} + +int load_sign_key_data(void) +{ + void *page_addr; + unsigned long data_size; + int ret = 0; + + data_size = 0; + page_addr = efi_key_load_data(efi_s4_sign_key_name, &data_size, true); + if (IS_ERR(page_addr)) { + ret = PTR_ERR(page_addr); + pr_err("PM: Load s4 sign key data error: %d\n", ret); + } else { + if (skey_page_addr && !IS_ERR(skey_page_addr)) { + memset(skey_page_addr, 0, skey_dsize); + free_page((unsigned long) skey_page_addr); + } + skey_dsize = data_size; + skey_page_addr = page_addr; + pr_info("PM: Load s4 sign key data success!\n"); + } + + return ret; +} + +bool sign_key_data_loaded(void) +{ + return skey_page_addr && !IS_ERR(skey_page_addr); +} + +struct key *get_sign_key(void) +{ + const struct cred *cred = current_cred(); + struct key *skey; + int err; + + if (!skey_page_addr || IS_ERR(skey_page_addr)) + return ERR_PTR(-EBADMSG); + + skey = key_alloc(&key_type_asymmetric, "s4_sign_key", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, + cred, 0, KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(skey)) { + pr_err("PM: Allocate s4 sign key error: %ld\n", PTR_ERR(skey)); + goto error_keyalloc; + } + err = key_instantiate_and_link(skey, skey_page_addr, skey_dsize, NULL, NULL); + if (err < 0) { + pr_err("PM: S4 sign key instantiate error: %d\n", err); + if (skey) + key_put(skey); + skey = ERR_PTR(err); + goto error_keyinit; + } + + return skey; + +error_keyinit: + free_page((unsigned long)skey_page_addr); + skey_dsize = 0; +error_keyalloc: + return skey; +} + +void destroy_sign_key(struct key *skey) +{ + if (!skey_page_addr || IS_ERR(skey_page_addr)) + return; + + memset(skey_page_addr, 0, skey_dsize); + free_page((unsigned long)skey_page_addr); + skey_dsize = 0; + if (skey) + key_put(skey); +} + +int find_wake_key_data(void) +{ + unsigned long datasize = 0; + efi_status_t status; + int ret = 0; + + if (!efi_enabled(EFI_RUNTIME_SERVICES)) + return -EPERM; + + /* obtain the size */ + status = efi.get_variable(efi_s4_wake_key_name, &EFI_HIBERNATE_GUID, + NULL, &datasize, NULL); + if (status != EFI_BUFFER_TOO_SMALL) { + ret = efi_status_to_err(status); + pr_err("PM: Couldn't find key data size: 0x%lx\n", status); + } + + return ret; +} + +struct key *load_wake_key(void) +{ + const struct cred *cred = current_cred(); + void *page_addr; + unsigned long datasize = 0; + struct key *wkey; + int err; + + page_addr = efi_key_load_data(efi_s4_wake_key_name, &datasize, false); + if (IS_ERR(page_addr)) { + wkey = (struct key *)page_addr; + goto error_data; + } + + wkey = key_alloc(&key_type_asymmetric, "s4_wake_key", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, + cred, 0, KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(wkey)) { + pr_err("PM: Allocate s4 wake key error: %ld\n", PTR_ERR(wkey)); + goto error_keyalloc; + } + err = key_instantiate_and_link(wkey, page_addr, datasize, NULL, NULL); + if (err < 0) { + pr_err("PM: S4 wake key instantiate error: %d\n", err); + if (wkey) + key_put(wkey); + wkey = ERR_PTR(err); + } + +error_keyalloc: + free_page((unsigned long)page_addr); +error_data: + return wkey; +} + +size_t get_key_length(const struct key *key) +{ + const struct public_key *pk = key->payload.data; + size_t len; + + /* TODO: better check the RSA type */ + + len = mpi_get_nbits(pk->rsa.n); + len = (len + 7) / 8; + + return len; +} Index: linux/kernel/power/power.h =================================================================== --- linux.orig/kernel/power/power.h +++ linux/kernel/power/power.h @@ -160,6 +160,28 @@ extern void swsusp_close(fmode_t); extern int swsusp_unmark(void); #endif +/* kernel/power/hibernate_key.c */ +extern struct key *get_sign_key(void); +extern bool sign_key_data_loaded(void); +extern void destroy_sign_key(struct key *key); +extern int find_wake_key_data(void); +extern struct key *load_wake_key(void); +extern size_t get_key_length(const struct key *key); + +#ifdef CONFIG_SNAPSHOT_VERIFICATION +extern int load_sign_key_data(void); +extern bool swsusp_page_is_sign_key(struct page *page); +#else /* !CONFIG_SUSPEND */ +static inline int load_sign_key_data(void) +{ + return 0; +} +static inline bool swsusp_page_is_sign_key(struct page *page) +{ + return false; +} +#endif /* !CONFIG_SNAPSHOT_VERIFICATION */ + /* kernel/power/block_io.c */ extern struct block_device *hib_resume_bdev; Index: linux/kernel/power/Kconfig =================================================================== --- linux.orig/kernel/power/Kconfig +++ linux/kernel/power/Kconfig @@ -66,8 +66,17 @@ config HIBERNATION For more information take a look at <file:Documentation/power/swsusp.txt>. -config ARCH_SAVE_PAGE_KEYS - bool +config SNAPSHOT_VERIFICATION + bool "Hibernate snapshot verification" + depends on HIBERNATION + depends on EFI + select CRYPTO_SHA256 + select PKCS8_PRIVATE_KEY_INFO_PARSER + help + This option provides support for generate anad verify the signautre by + RSA key-pair against hibernate snapshot image. Current mechanism + dependent on UEFI environment. EFI bootloader should generate the + key-pair. config PM_STD_PARTITION string "Default resume partition" @@ -91,6 +100,9 @@ config PM_STD_PARTITION suspended image to. It will simply pick the first available swap device. +config ARCH_SAVE_PAGE_KEYS + bool + config PM_SLEEP def_bool y depends on SUSPEND || HIBERNATE_CALLBACKS -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
This patch add the code for generate/verify signature of snapshot, it put the signature to snapshot header. This approach can support both on userspace hibernate and in-kernel hibernate. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- kernel/power/power.h | 5 + kernel/power/snapshot.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++- kernel/power/swap.c | 10 ++ kernel/power/user.c | 11 ++ 4 files changed, 261 insertions(+), 4 deletions(-) diff --git a/kernel/power/power.h b/kernel/power/power.h index 03d7d45..56ef656 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -3,6 +3,9 @@ #include <linux/utsname.h> #include <linux/freezer.h> +/* The maximum length of snapshot signature */ +#define SIG_LENG 512 + struct swsusp_info { struct new_utsname uts; u32 version_code; @@ -11,6 +14,7 @@ struct swsusp_info { unsigned long image_pages; unsigned long pages; unsigned long size; + u8 signature[SIG_LENG]; } __attribute__((aligned(PAGE_SIZE))); #ifdef CONFIG_HIBERNATION @@ -134,6 +138,7 @@ extern int snapshot_read_next(struct snapshot_handle *handle); extern int snapshot_write_next(struct snapshot_handle *handle); extern void snapshot_write_finalize(struct snapshot_handle *handle); extern int snapshot_image_loaded(struct snapshot_handle *handle); +extern int snapshot_image_verify(void); /* If unset, the snapshot device cannot be open. */ extern atomic_t snapshot_device_available; diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 349587b..7a817c2 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -27,6 +27,9 @@ #include <linux/highmem.h> #include <linux/list.h> #include <linux/slab.h> +#include <crypto/hash.h> +#include <crypto/public_key.h> +#include <keys/asymmetric-type.h> #include <asm/uaccess.h> #include <asm/mmu_context.h> @@ -1031,11 +1034,49 @@ static inline void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn) } #endif /* CONFIG_HIGHMEM */ -static void +#define SNAPSHOT_HASH "sha256" + +/* + * Signature of snapshot for check. + */ +static u8 signature[SIG_LENG]; + +static int copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm) { struct zone *zone; - unsigned long pfn; + unsigned long pfn, dst_pfn; + struct page *d_page; + void *hash_buffer = NULL; + struct crypto_shash *tfm; + struct shash_desc *desc; + u8 *digest; + size_t digest_size, desc_size; + struct key *s4_sign_key; + struct public_key_signature *pks; + int ret; + + ret = -ENOMEM; + tfm = crypto_alloc_shash(SNAPSHOT_HASH, 0, 0); + if (IS_ERR(tfm)) { + pr_err("IS_ERR(tfm): %ld", PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + digest = kzalloc(digest_size + desc_size, GFP_KERNEL); + if (!digest) { + pr_err("digest allocate fail"); + ret = -ENOMEM; + goto error_digest; + } + desc = (void *) digest + digest_size; + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_shash; for_each_populated_zone(zone) { unsigned long max_zone_pfn; @@ -1052,8 +1093,65 @@ copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm) pfn = memory_bm_next_pfn(orig_bm); if (unlikely(pfn == BM_END_OF_MAP)) break; - copy_data_page(memory_bm_next_pfn(copy_bm), pfn); + dst_pfn = memory_bm_next_pfn(copy_bm); + copy_data_page(dst_pfn, pfn); + + /* Generate digest */ + d_page = pfn_to_page(dst_pfn); + if (PageHighMem(d_page)) { + void *kaddr; + kaddr = kmap_atomic(d_page); + copy_page(buffer, kaddr); + kunmap_atomic(kaddr); + hash_buffer = buffer; + } else { + hash_buffer = page_address(d_page); + } + ret = crypto_shash_update(desc, hash_buffer, PAGE_SIZE); + if (ret) + goto error_shash; + } + + crypto_shash_final(desc, digest); + if (ret) + goto error_shash; + + /* Generate signature by private key */ + s4_sign_key = get_sign_key(); + if (!s4_sign_key || IS_ERR(s4_sign_key)) { + pr_err("Get S4 sign key fail: %ld\n", PTR_ERR(s4_sign_key)); + ret = PTR_ERR(s4_sign_key); + goto error_key; } + + pks = generate_signature(s4_sign_key, digest, PKEY_HASH_SHA256, false); + if (IS_ERR(pks)) { + pr_err("Generate signature fail: %lx", PTR_ERR(pks)); + ret = PTR_ERR(pks); + goto error_sign; + } else + memcpy(signature, pks->S, pks->k); + + destroy_sign_key(s4_sign_key); + + if (pks && pks->digest) + kfree(pks->digest); + if (pks && pks->rsa.s) + mpi_free(pks->rsa.s); + kfree(pks); + kfree(digest); + crypto_free_shash(tfm); + + return 0; + +error_sign: + destroy_sign_key(s4_sign_key); +error_key: +error_shash: + kfree(digest); +error_digest: + crypto_free_shash(tfm); + return ret; } /* Total number of image pages */ @@ -1580,6 +1678,7 @@ swsusp_alloc(struct memory_bitmap *orig_bm, struct memory_bitmap *copy_bm, asmlinkage int swsusp_save(void) { unsigned int nr_pages, nr_highmem; + int ret; printk(KERN_INFO "PM: Creating hibernation image:\n"); @@ -1602,7 +1701,9 @@ asmlinkage int swsusp_save(void) * Kill them. */ drain_local_pages(NULL); - copy_data_pages(©_bm, &orig_bm); + ret = copy_data_pages(©_bm, &orig_bm); + if (ret) + return ret; /* * End of critical section. From now on, we can write to memory, @@ -1657,6 +1758,7 @@ static int init_header(struct swsusp_info *info) info->pages = snapshot_get_image_size(); info->size = info->pages; info->size <<= PAGE_SHIFT; + memcpy(info->signature, signature, SIG_LENG); return init_header_complete(info); } @@ -1819,6 +1921,7 @@ load_header(struct swsusp_info *info) if (!error) { nr_copy_pages = info->image_pages; nr_meta_pages = info->pages - info->image_pages - 1; + memcpy(signature, info->signature, SIG_LENG); } return error; } @@ -2194,6 +2297,8 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) return pbe->address; } +void **h_buf; + /** * snapshot_write_next - used for writing the system memory snapshot. * @@ -2236,6 +2341,13 @@ int snapshot_write_next(struct snapshot_handle *handle) if (error) return error; + /* Allocate void * array to keep buffer point for generate hash, + * h_buf will freed in snapshot_image_verify(). + */ + h_buf = kmalloc(sizeof(void *) * nr_copy_pages, GFP_KERNEL); + if (!h_buf) + pr_err("Allocate hash buffer fail!"); + error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY); if (error) return error; @@ -2262,6 +2374,8 @@ int snapshot_write_next(struct snapshot_handle *handle) handle->sync_read = 0; if (IS_ERR(handle->buffer)) return PTR_ERR(handle->buffer); + if (h_buf) + *h_buf = handle->buffer; } } else { copy_last_highmem_page(); @@ -2272,6 +2386,8 @@ int snapshot_write_next(struct snapshot_handle *handle) return PTR_ERR(handle->buffer); if (handle->buffer != buffer) handle->sync_read = 0; + if (h_buf) + *(h_buf + (handle->cur - nr_meta_pages - 1)) = handle->buffer; } handle->cur++; return PAGE_SIZE; @@ -2304,6 +2420,121 @@ int snapshot_image_loaded(struct snapshot_handle *handle) handle->cur <= nr_meta_pages + nr_copy_pages); } +int snapshot_verify_signature(u8 *digest, size_t digest_size) +{ + struct key *s4_wake_key; + struct public_key_signature *pks; + int ret; + MPI mpi; + + /* load public key */ + s4_wake_key = load_wake_key(); + if (!s4_wake_key || IS_ERR(s4_wake_key)) { + pr_err("Get S4 wake key fail: %ld\n", PTR_ERR(s4_wake_key)); + return PTR_ERR(s4_wake_key); + } + + pks = kzalloc(digest_size + sizeof(*pks), GFP_KERNEL); + if (!pks) { + pr_err("Allocate public key signature fail!"); + return -ENOMEM; + } + pks->pkey_hash_algo = PKEY_HASH_SHA256; + pks->digest = digest; + pks->digest_size = digest_size; + + mpi = mpi_read_raw_data(signature, get_key_length(s4_wake_key)); + if (!mpi) { + pr_err("PM: mpi_read_raw_data fail!\n"); + ret = -ENOMEM; + goto error_mpi; + } + pks->mpi[0] = mpi; + pks->nr_mpi = 1; + + /* RSA signature check */ + ret = verify_signature(s4_wake_key, pks); + if (ret) { + pr_err("snapshot S4 signature verification fail: %d\n", ret); + goto error_verify; + } else + pr_info("snapshot S4 signature verification pass!\n"); + + if (pks->rsa.s) + mpi_free(pks->rsa.s); + kfree(pks); + + return 0; + +error_verify: + if (pks->rsa.s) + mpi_free(pks->rsa.s); +error_mpi: + kfree(pks); + return ret; +} + +int snapshot_image_verify(void) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + u8 *digest; + size_t digest_size, desc_size; + int ret, i; + + if (!h_buf) + return 0; + + tfm = crypto_alloc_shash(SNAPSHOT_HASH, 0, 0); + if (IS_ERR(tfm)) { + pr_err("IS_ERR(tfm): %ld", PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + digest = kzalloc(digest_size + desc_size, GFP_KERNEL); + if (!digest) { + pr_err("digest allocate fail"); + ret = -ENOMEM; + goto error_digest; + } + desc = (void *) digest + digest_size; + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_shash; + + for (i = 0; i < nr_copy_pages; i++) { + ret = crypto_shash_update(desc, *(h_buf + i), PAGE_SIZE); + if (ret) + goto error_shash; + } + + ret = crypto_shash_final(desc, digest); + if (ret) + goto error_shash; + + ret = snapshot_verify_signature(digest, digest_size); + if (ret) + goto error_verify; + + kfree(h_buf); + kfree(digest); + crypto_free_shash(tfm); + return 0; + +error_verify: +error_shash: + kfree(h_buf); + kfree(digest); +error_digest: + crypto_free_shash(tfm); + return ret; +} + #ifdef CONFIG_HIGHMEM /* Assumes that @buf is ready and points to a "safe" page */ static inline void diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 7c33ed2..7dc54e6 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -1004,6 +1004,11 @@ static int load_image(struct swap_map_handle *handle, snapshot_write_finalize(snapshot); if (!snapshot_image_loaded(snapshot)) ret = -ENODATA; + ret = snapshot_image_verify(); + if (ret) + pr_info("PM: snapshot signature check FAIL: %d\n", ret); + else + pr_info("PM: snapshot signature check SUCCESS!\n"); } swsusp_show_speed(&start, &stop, nr_to_read, "Read"); return ret; @@ -1358,6 +1363,11 @@ out_finish: } } } + ret = snapshot_image_verify(); + if (ret) + pr_info("PM: snapshot signature check FAIL: %d\n", ret); + else + pr_info("PM: snapshot signature check SUCCESS!\n"); } swsusp_show_speed(&start, &stop, nr_to_read, "Read"); out_clean: diff --git a/kernel/power/user.c b/kernel/power/user.c index 4ed81e7..31bf500 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -196,6 +196,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, struct snapshot_data *data; loff_t size; sector_t offset; + int skey_error = 0; if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC) return -ENOTTY; @@ -228,6 +229,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, if (!data->frozen || data->ready) break; pm_restore_gfp_mask(); + skey_error = load_sign_key_data(); + if (skey_error) + pr_err("Load private key fail: %d", skey_error); thaw_processes(); data->frozen = 0; break; @@ -253,6 +257,13 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, error = -EPERM; break; } + if (!snapshot_image_verify()) + pr_info("PM: snapshot signature check SUCCESS!\n"); + else { + pr_info("PM: snapshot signature check FAIL!\n"); + error = -EPERM; + break; + } error = hibernation_restore(data->platform_support); break; -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
This patch add swsusp_page_is_sign_key() method to hibernate_key.c and check the page is S4 sign key data when collect saveable page in snapshot.c to avoid sign key data included in snapshot image. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- kernel/power/snapshot.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 7a817c2..fc9ae9f 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -860,6 +860,9 @@ static struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn) BUG_ON(!PageHighMem(page)); + if (swsusp_page_is_sign_key(page)) + return NULL; + if (swsusp_page_is_forbidden(page) || swsusp_page_is_free(page) || PageReserved(page)) return NULL; @@ -922,6 +925,9 @@ static struct page *saveable_page(struct zone *zone, unsigned long pfn) BUG_ON(PageHighMem(page)); + if (swsusp_page_is_sign_key(page)) + return NULL; + if (swsusp_page_is_forbidden(page) || swsusp_page_is_free(page)) return NULL; -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
This patch fix the race condition of remove S4 sign key. It moved the code of reload sign key to software_resume() in hibernate.c after we confirmed the snapshot image doesn't in swap partition. We ignore the errors, -ENOENT, -ENODEV and -NENXIO to ignore the checking result before swap partition available. It avoid the S4 sign key removed before we load it after S4 resume success. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- kernel/power/hibernate.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 692c790..840c711 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -844,6 +844,8 @@ static int software_resume(void) Unlock: mutex_unlock(&pm_mutex); pr_debug("PM: Hibernation image not present or could not be loaded.\n"); + if (error && error != -ENOENT && error != -ENODEV && error != -ENXIO) + load_sign_key_data(); return error; close_finish: swsusp_close(FMODE_READ); -- 1.6.4.2 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
This patch applied SNAPSHOT_VERIFICATION kernel config for switching signature check of hibernate snapshot image. v2: Moved CONFIG_SNAPSHOT_VERIFICATION to earlier patch. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> Cc: Takashi Iwai <tiwai@suse.de> --- kernel/power/hibernate.c | 6 ++++++ kernel/power/snapshot.c | 23 +++++++++++++++++++++++ kernel/power/swap.c | 28 ++++++++++++++++++---------- kernel/power/user.c | 6 ++++++ 4 files changed, 69 insertions(+), 13 deletions(-) Index: linux/kernel/power/snapshot.c =================================================================== --- linux.orig/kernel/power/snapshot.c +++ linux/kernel/power/snapshot.c @@ -1052,6 +1052,8 @@ copy_data_pages(struct memory_bitmap *co { struct zone *zone; unsigned long pfn, dst_pfn; + +#ifdef CONFIG_SNAPSHOT_VERIFICATION struct page *d_page; void *hash_buffer = NULL; struct crypto_shash *tfm; @@ -1083,6 +1085,7 @@ copy_data_pages(struct memory_bitmap *co ret = crypto_shash_init(desc); if (ret < 0) goto error_shash; +#endif /* CONFIG_SNAPSHOT_VERIFICATION */ for_each_populated_zone(zone) { unsigned long max_zone_pfn; @@ -1102,6 +1105,7 @@ copy_data_pages(struct memory_bitmap *co dst_pfn = memory_bm_next_pfn(copy_bm); copy_data_page(dst_pfn, pfn); +#ifdef CONFIG_SNAPSHOT_VERIFICATION /* Generate digest */ d_page = pfn_to_page(dst_pfn); if (PageHighMem(d_page)) { @@ -1116,8 +1120,10 @@ copy_data_pages(struct memory_bitmap *co ret = crypto_shash_update(desc, hash_buffer, PAGE_SIZE); if (ret) goto error_shash; +#endif } +#ifdef CONFIG_SNAPSHOT_VERIFICATION crypto_shash_final(desc, digest); if (ret) goto error_shash; @@ -1147,9 +1153,11 @@ copy_data_pages(struct memory_bitmap *co kfree(pks); kfree(digest); crypto_free_shash(tfm); +#endif /* CONFIG_SNAPSHOT_VERIFICATION */ return 0; +#ifdef CONFIG_SNAPSHOT_VERIFICATION error_sign: destroy_sign_key(s4_sign_key); error_key: @@ -1158,6 +1166,7 @@ error_shash: error_digest: crypto_free_shash(tfm); return ret; +#endif } /* Total number of image pages */ @@ -2303,7 +2312,9 @@ static void *get_buffer(struct memory_bi return pbe->address; } +#ifdef CONFIG_SNAPSHOT_VERIFICATION void **h_buf; +#endif /** * snapshot_write_next - used for writing the system memory snapshot. @@ -2347,12 +2358,14 @@ int snapshot_write_next(struct snapshot_ if (error) return error; +#ifdef CONFIG_SNAPSHOT_VERIFICATION /* Allocate void * array to keep buffer point for generate hash, * h_buf will freed in snapshot_image_verify(). */ h_buf = kmalloc(sizeof(void *) * nr_copy_pages, GFP_KERNEL); if (!h_buf) pr_err("Allocate hash buffer fail!"); +#endif error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY); if (error) @@ -2380,8 +2393,10 @@ int snapshot_write_next(struct snapshot_ handle->sync_read = 0; if (IS_ERR(handle->buffer)) return PTR_ERR(handle->buffer); +#ifdef CONFIG_SNAPSHOT_VERIFICATION if (h_buf) *h_buf = handle->buffer; +#endif } } else { copy_last_highmem_page(); @@ -2392,8 +2407,10 @@ int snapshot_write_next(struct snapshot_ return PTR_ERR(handle->buffer); if (handle->buffer != buffer) handle->sync_read = 0; +#ifdef CONFIG_SNAPSHOT_VERIFICATION if (h_buf) *(h_buf + (handle->cur - nr_meta_pages - 1)) = handle->buffer; +#endif } handle->cur++; return PAGE_SIZE; @@ -2426,6 +2443,7 @@ int snapshot_image_loaded(struct snapsho handle->cur <= nr_meta_pages + nr_copy_pages); } +#ifdef CONFIG_SNAPSHOT_VERIFICATION int snapshot_verify_signature(u8 *digest, size_t digest_size) { struct key *s4_wake_key; @@ -2540,6 +2558,7 @@ error_digest: crypto_free_shash(tfm); return ret; } +#endif /* CONFIG_SNAPSHOT_VERIFICATION */ #ifdef CONFIG_HIGHMEM /* Assumes that @buf is ready and points to a "safe" page */ Index: linux/kernel/power/swap.c =================================================================== --- linux.orig/kernel/power/swap.c +++ linux/kernel/power/swap.c @@ -1004,11 +1004,15 @@ static int load_image(struct swap_map_ha snapshot_write_finalize(snapshot); if (!snapshot_image_loaded(snapshot)) ret = -ENODATA; - ret = snapshot_image_verify(); - if (ret) - pr_info("PM: snapshot signature check FAIL: %d\n", ret); - else - pr_info("PM: snapshot signature check SUCCESS!\n"); +#ifdef CONFIG_SNAPSHOT_VERIFICATION + else { + ret = snapshot_image_verify(); + if (ret) + pr_info("PM: snapshot signature check FAIL: %d\n", ret); + else + pr_info("PM: snapshot signature check SUCCESS!\n"); + } +#endif } swsusp_show_speed(&start, &stop, nr_to_read, "Read"); return ret; @@ -1363,11 +1367,15 @@ out_finish: } } } - ret = snapshot_image_verify(); - if (ret) - pr_info("PM: snapshot signature check FAIL: %d\n", ret); - else - pr_info("PM: snapshot signature check SUCCESS!\n"); +#ifdef CONFIG_SNAPSHOT_VERIFICATION + if (!ret) { + ret = snapshot_image_verify(); + if (ret) + pr_info("PM: snapshot signature check FAIL: %d\n", ret); + else + pr_info("PM: snapshot signature check SUCCESS!\n"); + } +#endif } swsusp_show_speed(&start, &stop, nr_to_read, "Read"); out_clean: Index: linux/kernel/power/user.c =================================================================== --- linux.orig/kernel/power/user.c +++ linux/kernel/power/user.c @@ -257,6 +257,7 @@ static long snapshot_ioctl(struct file * error = -EPERM; break; } +#ifdef CONFIG_SNAPSHOT_VERIFICATION if (!snapshot_image_verify()) pr_info("PM: snapshot signature check SUCCESS!\n"); else { @@ -264,6 +265,7 @@ static long snapshot_ioctl(struct file * error = -EPERM; break; } +#endif error = hibernation_restore(data->platform_support); break; -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
In current solution, the snapshot signature check used the RSA key-pair that are generated by bootloader(e.g. shim) and pass the key-pair to kernel through EFI variables. I choice to binding the snapshot signature check mechanism with UEFI secure boot for provide stronger protection of hibernate. Current behavior is following: + UEFI Secure Boot ON, Kernel found key-pair from shim: Will do the S4 signature check. + UEFI Secure Boot ON, Kernel didn't find key-pair from shim: Will lock down S4 function. + UEFI Secure Boot OFF Will NOT do the S4 signature check. Ignore any keys from bootloader. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- kernel/power/hibernate.c | 35 +++++++++++++++++ kernel/power/main.c | 11 +++++- kernel/power/snapshot.c | 95 ++++++++++++++++++++++++++-------------------- kernel/power/swap.c | 4 +- kernel/power/user.c | 11 +++++ 5 files changed, 112 insertions(+), 44 deletions(-) Index: linux/kernel/power/hibernate.c =================================================================== --- linux.orig/kernel/power/hibernate.c +++ linux/kernel/power/hibernate.c @@ -29,6 +29,7 @@ #include <linux/ctype.h> #include <linux/genhd.h> #include <linux/key.h> +#include <linux/efi.h> #include "power.h" @@ -634,6 +635,14 @@ int hibernate(void) int error; int skey_error; +#ifdef CONFIG_SNAPSHOT_VERIFICATION + if (!capable(CAP_COMPROMISE_KERNEL) && !sign_key_data_loaded()) { +#else + if (!capable(CAP_COMPROMISE_KERNEL)) { +#endif + return -EPERM; + } + lock_system_sleep(); /* The snapshot device should not be opened while we're running */ if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { @@ -801,6 +810,15 @@ static int software_resume(void) if (error) goto Unlock; +#ifdef CONFIG_SNAPSHOT_VERIFICATION + if (!capable(CAP_COMPROMISE_KERNEL) && find_wake_key_data()) { +#else + if (!capable(CAP_COMPROMISE_KERNEL)) { +#endif + mutex_unlock(&pm_mutex); + return -EPERM; + } + /* The snapshot device should not be opened while we're running */ if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { error = -EBUSY; @@ -896,6 +914,15 @@ static ssize_t disk_show(struct kobject int i; char *start = buf; +#ifdef CONFIG_SNAPSHOT_VERIFICATION + if (efi_enabled(EFI_SECURE_BOOT) && !sign_key_data_loaded()) { +#else + if (efi_enabled(EFI_SECURE_BOOT)) { +#endif + buf += sprintf(buf, "[%s]\n", "disabled"); + return buf-start; + } + for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { if (!hibernation_modes[i]) continue; @@ -930,6 +957,14 @@ static ssize_t disk_store(struct kobject char *p; int mode = HIBERNATION_INVALID; +#ifdef CONFIG_SNAPSHOT_VERIFICATION + if (!capable(CAP_COMPROMISE_KERNEL) && !sign_key_data_loaded()) { +#else + if (!capable(CAP_COMPROMISE_KERNEL)) { +#endif + return -EPERM; + } + p = memchr(buf, '\n', n); len = p ? p - buf : n; Index: linux/kernel/power/main.c =================================================================== --- linux.orig/kernel/power/main.c +++ linux/kernel/power/main.c @@ -15,6 +15,7 @@ #include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/efi.h> #include "power.h" @@ -301,7 +302,15 @@ static ssize_t state_show(struct kobject } #endif #ifdef CONFIG_HIBERNATION - s += sprintf(s, "%s\n", "disk"); + if (!efi_enabled(EFI_SECURE_BOOT)) { + s += sprintf(s, "%s\n", "disk"); +#ifdef CONFIG_SNAPSHOT_VERIFICATION + } else if (sign_key_data_loaded()) { + s += sprintf(s, "%s\n", "disk"); +#endif + } else { + s += sprintf(s, "\n"); + } #else if (s != buf) /* convert the last space to a newline */ Index: linux/kernel/power/snapshot.c =================================================================== --- linux.orig/kernel/power/snapshot.c +++ linux/kernel/power/snapshot.c @@ -860,7 +860,8 @@ static struct page *saveable_highmem_pag BUG_ON(!PageHighMem(page)); - if (swsusp_page_is_sign_key(page)) + if (!capable(CAP_COMPROMISE_KERNEL) && + swsusp_page_is_sign_key(page)) return NULL; if (swsusp_page_is_forbidden(page) || swsusp_page_is_free(page) || @@ -925,7 +926,8 @@ static struct page *saveable_page(struct BUG_ON(PageHighMem(page)); - if (swsusp_page_is_sign_key(page)) + if (!capable(CAP_COMPROMISE_KERNEL) && + swsusp_page_is_sign_key(page)) return NULL; if (swsusp_page_is_forbidden(page) || swsusp_page_is_free(page)) @@ -1056,35 +1058,37 @@ copy_data_pages(struct memory_bitmap *co #ifdef CONFIG_SNAPSHOT_VERIFICATION struct page *d_page; void *hash_buffer = NULL; - struct crypto_shash *tfm; - struct shash_desc *desc; - u8 *digest; + struct crypto_shash *tfm = NULL; + struct shash_desc *desc = NULL; + u8 *digest = NULL; size_t digest_size, desc_size; struct key *s4_sign_key; struct public_key_signature *pks; int ret; ret = -ENOMEM; - tfm = crypto_alloc_shash(SNAPSHOT_HASH, 0, 0); - if (IS_ERR(tfm)) { - pr_err("IS_ERR(tfm): %ld", PTR_ERR(tfm)); - return PTR_ERR(tfm); - } - - desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - digest_size = crypto_shash_digestsize(tfm); - digest = kzalloc(digest_size + desc_size, GFP_KERNEL); - if (!digest) { - pr_err("digest allocate fail"); - ret = -ENOMEM; - goto error_digest; - } - desc = (void *) digest + digest_size; - desc->tfm = tfm; - desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; - ret = crypto_shash_init(desc); - if (ret < 0) - goto error_shash; + if (!capable(CAP_COMPROMISE_KERNEL)) { + tfm = crypto_alloc_shash(SNAPSHOT_HASH, 0, 0); + if (IS_ERR(tfm)) { + pr_err("IS_ERR(tfm): %ld", PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + digest = kzalloc(digest_size + desc_size, GFP_KERNEL); + if (!digest) { + pr_err("digest allocate fail"); + ret = -ENOMEM; + goto error_digest; + } + desc = (void *) digest + digest_size; + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_shash; + } #endif /* CONFIG_SNAPSHOT_VERIFICATION */ for_each_populated_zone(zone) { @@ -1106,24 +1110,29 @@ copy_data_pages(struct memory_bitmap *co copy_data_page(dst_pfn, pfn); #ifdef CONFIG_SNAPSHOT_VERIFICATION - /* Generate digest */ - d_page = pfn_to_page(dst_pfn); - if (PageHighMem(d_page)) { - void *kaddr; - kaddr = kmap_atomic(d_page); - copy_page(buffer, kaddr); - kunmap_atomic(kaddr); - hash_buffer = buffer; - } else { - hash_buffer = page_address(d_page); + if (!capable(CAP_COMPROMISE_KERNEL)) { + /* Generate digest */ + d_page = pfn_to_page(dst_pfn); + if (PageHighMem(d_page)) { + void *kaddr; + kaddr = kmap_atomic(d_page); + copy_page(buffer, kaddr); + kunmap_atomic(kaddr); + hash_buffer = buffer; + } else { + hash_buffer = page_address(d_page); + } + ret = crypto_shash_update(desc, hash_buffer, PAGE_SIZE); + if (ret) + goto error_shash; } - ret = crypto_shash_update(desc, hash_buffer, PAGE_SIZE); - if (ret) - goto error_shash; #endif } #ifdef CONFIG_SNAPSHOT_VERIFICATION + if (capable(CAP_COMPROMISE_KERNEL)) + goto skip_sign; + crypto_shash_final(desc, digest); if (ret) goto error_shash; @@ -1153,6 +1162,8 @@ copy_data_pages(struct memory_bitmap *co kfree(pks); kfree(digest); crypto_free_shash(tfm); + +skip_sign: #endif /* CONFIG_SNAPSHOT_VERIFICATION */ return 0; @@ -2362,9 +2373,11 @@ int snapshot_write_next(struct snapshot_ /* Allocate void * array to keep buffer point for generate hash, * h_buf will freed in snapshot_image_verify(). */ - h_buf = kmalloc(sizeof(void *) * nr_copy_pages, GFP_KERNEL); - if (!h_buf) - pr_err("Allocate hash buffer fail!"); + if (!capable(CAP_COMPROMISE_KERNEL)) { + h_buf = kmalloc(sizeof(void *) * nr_copy_pages, GFP_KERNEL); + if (!h_buf) + pr_err("Allocate hash buffer fail!"); + } #endif error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY); @@ -2394,7 +2407,7 @@ int snapshot_write_next(struct snapshot_ if (IS_ERR(handle->buffer)) return PTR_ERR(handle->buffer); #ifdef CONFIG_SNAPSHOT_VERIFICATION - if (h_buf) + if (!capable(CAP_COMPROMISE_KERNEL) && h_buf) *h_buf = handle->buffer; #endif } @@ -2408,7 +2421,7 @@ int snapshot_write_next(struct snapshot_ if (handle->buffer != buffer) handle->sync_read = 0; #ifdef CONFIG_SNAPSHOT_VERIFICATION - if (h_buf) + if (!capable(CAP_COMPROMISE_KERNEL) && h_buf) *(h_buf + (handle->cur - nr_meta_pages - 1)) = handle->buffer; #endif } Index: linux/kernel/power/swap.c =================================================================== --- linux.orig/kernel/power/swap.c +++ linux/kernel/power/swap.c @@ -1005,7 +1005,7 @@ static int load_image(struct swap_map_ha if (!snapshot_image_loaded(snapshot)) ret = -ENODATA; #ifdef CONFIG_SNAPSHOT_VERIFICATION - else { + else if (!capable(CAP_COMPROMISE_KERNEL)) { ret = snapshot_image_verify(); if (ret) pr_info("PM: snapshot signature check FAIL: %d\n", ret); @@ -1368,7 +1368,7 @@ out_finish: } } #ifdef CONFIG_SNAPSHOT_VERIFICATION - if (!ret) { + if (!ret && !capable(CAP_COMPROMISE_KERNEL)) { ret = snapshot_image_verify(); if (ret) pr_info("PM: snapshot signature check FAIL: %d\n", ret); Index: linux/kernel/power/user.c =================================================================== --- linux.orig/kernel/power/user.c +++ linux/kernel/power/user.c @@ -48,6 +48,14 @@ static int snapshot_open(struct inode *i struct snapshot_data *data; int error; +#ifdef CONFIG_SNAPSHOT_VERIFICATION + if (!capable(CAP_COMPROMISE_KERNEL) && find_wake_key_data()) { +#else + if (!capable(CAP_COMPROMISE_KERNEL)) { +#endif + return -EPERM; + } + lock_system_sleep(); if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { @@ -258,6 +266,8 @@ static long snapshot_ioctl(struct file * break; } #ifdef CONFIG_SNAPSHOT_VERIFICATION + if (capable(CAP_COMPROMISE_KERNEL)) + goto skip_verify; if (!snapshot_image_verify()) pr_info("PM: snapshot signature check SUCCESS!\n"); else { @@ -265,6 +275,7 @@ static long snapshot_ioctl(struct file * error = -EPERM; break; } +skip_verify: #endif error = hibernation_restore(data->platform_support); break; -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
Show the verification time for monitor the performance of SHA256 and RSA verification. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- kernel/power/snapshot.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) Index: linux/kernel/power/snapshot.c =================================================================== --- linux.orig/kernel/power/snapshot.c +++ linux/kernel/power/snapshot.c @@ -2513,6 +2513,8 @@ error_mpi: int snapshot_image_verify(void) { + struct timeval start; + struct timeval stop; struct crypto_shash *tfm; struct shash_desc *desc; u8 *digest; @@ -2540,6 +2542,8 @@ int snapshot_image_verify(void) desc->tfm = tfm; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + do_gettimeofday(&start); + ret = crypto_shash_init(desc); if (ret < 0) goto error_shash; @@ -2558,6 +2562,9 @@ int snapshot_image_verify(void) if (ret) goto error_verify; + do_gettimeofday(&stop); + swsusp_show_speed(&start, &stop, nr_copy_pages, "Verified"); + kfree(h_buf); kfree(digest); crypto_free_shash(tfm); -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
This patch introduced SNAPSHOT_SIG_HASH config for user to select which hash algorithm will be used during signature generation of snapshot. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- kernel/power/Kconfig | 46 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/power/snapshot.c | 25 ++++++++++++++++++++----- 2 files changed, 66 insertions(+), 5 deletions(-) Index: linux/kernel/power/Kconfig =================================================================== --- linux.orig/kernel/power/Kconfig +++ linux/kernel/power/Kconfig @@ -78,6 +78,52 @@ config SNAPSHOT_VERIFICATION dependent on UEFI environment. EFI bootloader should generate the key-pair. +choice + prompt "Which hash algorithm should snapshot be signed with?" + depends on SNAPSHOT_VERIFICATION + help + This determines which sort of hashing algorithm will be used during + signature generation of snapshot. This algorithm _must_ be built into + the kernel directly so that signature verification can take place. + It is not possible to load a signed snapshot containing the algorithm + to check the signature on that module. + +config SNAPSHOT_SIG_SHA1 + bool "Sign modules with SHA-1" + select CRYPTO_SHA1 + select CRYPTO_SHA1_SSSE3 if X86_64 + +config SNAPSHOT_SIG_SHA224 + bool "Sign modules with SHA-224" + select CRYPTO_SHA256 + select CRYPTO_SHA256_SSSE3 if X86_64 + +config SNAPSHOT_SIG_SHA256 + bool "Sign modules with SHA-256" + select CRYPTO_SHA256 + select CRYPTO_SHA256_SSSE3 if X86_64 + +config SNAPSHOT_SIG_SHA384 + bool "Sign modules with SHA-384" + select CRYPTO_SHA512 + select CRYPTO_SHA512_SSSE3 if X86_64 + +config SNAPSHOT_SIG_SHA512 + bool "Sign modules with SHA-512" + select CRYPTO_SHA512 + select CRYPTO_SHA512_SSSE3 if X86_64 + +endchoice + +config SNAPSHOT_SIG_HASH + string + depends on SNAPSHOT_VERIFICATION + default "sha1" if SNAPSHOT_SIG_SHA1 + default "sha224" if SNAPSHOT_SIG_SHA224 + default "sha256" if SNAPSHOT_SIG_SHA256 + default "sha384" if SNAPSHOT_SIG_SHA384 + default "sha512" if SNAPSHOT_SIG_SHA512 + config PM_STD_PARTITION string "Default resume partition" depends on HIBERNATION Index: linux/kernel/power/snapshot.c =================================================================== --- linux.orig/kernel/power/snapshot.c +++ linux/kernel/power/snapshot.c @@ -1042,7 +1042,22 @@ static inline void copy_data_page(unsign } #endif /* CONFIG_HIGHMEM */ -#define SNAPSHOT_HASH "sha256" +static const char *snapshot_hash = CONFIG_SNAPSHOT_SIG_HASH; + +static int pkey_hash(void) +{ + int i, ret; + + ret = -1; + for (i = 0; i < PKEY_HASH__LAST; i++) { + if (!strcmp(pkey_hash_algo[i], snapshot_hash)) { + ret = i; + break; + } + } + + return ret; +} /* * Signature of snapshot for check. @@ -1068,7 +1083,7 @@ copy_data_pages(struct memory_bitmap *co ret = -ENOMEM; if (!capable(CAP_COMPROMISE_KERNEL)) { - tfm = crypto_alloc_shash(SNAPSHOT_HASH, 0, 0); + tfm = crypto_alloc_shash(snapshot_hash, 0, 0); if (IS_ERR(tfm)) { pr_err("IS_ERR(tfm): %ld", PTR_ERR(tfm)); return PTR_ERR(tfm); @@ -1145,7 +1160,7 @@ copy_data_pages(struct memory_bitmap *co goto error_key; } - pks = generate_signature(s4_sign_key, digest, PKEY_HASH_SHA256, false); + pks = generate_signature(s4_sign_key, digest, pkey_hash(), false); if (IS_ERR(pks)) { pr_err("Generate signature fail: %lx", PTR_ERR(pks)); ret = PTR_ERR(pks); @@ -2476,7 +2491,7 @@ int snapshot_verify_signature(u8 *digest pr_err("Allocate public key signature fail!"); return -ENOMEM; } - pks->pkey_hash_algo = PKEY_HASH_SHA256; + pks->pkey_hash_algo = pkey_hash(); pks->digest = digest; pks->digest_size = digest_size; @@ -2524,7 +2539,7 @@ int snapshot_image_verify(void) if (!h_buf) return 0; - tfm = crypto_alloc_shash(SNAPSHOT_HASH, 0, 0); + tfm = crypto_alloc_shash(snapshot_hash, 0, 0); if (IS_ERR(tfm)) { pr_err("IS_ERR(tfm): %ld", PTR_ERR(tfm)); return PTR_ERR(tfm); -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
On Fri, Aug 09, 2013 at 12:37:37PM +0800, Lee, Chun-Yi wrote:
+ When machine resume from hibernate: - EFI bootloader should copy the public key from boottime variable to S4WakeKey EFI variable. - Bootloader need generated a new key-pair for next round S4 usage. It should put new privat ekey to S4SignKey variable.
So, first, this is brilliant. Thank you for putting the work into this. The only potential problem is the generation of a new key pair on every reboot. Some hardware vendors have expressed concerns about writing variables on every boot, so if we can avoid that somehow then life would probably be better. Options for that would seem to be (1) set a flag on S4 and only regenerate keys if that flag has been set (although I need to think about the security considerations of that), or (2) use a magic GUID space that all kernels (including Windows) refuse to expose to userspace. (2) is obviously conditional upon Microsoft, but let's have a chat with them to see if there's already some special-casing in Windows. It wouldn't surprise me. I'll review the rest of these over the next few days. I've been gradually merging in the shim changes to upstream, so do feel free to send me a pull request for the S4 stuff there, too. -- Matthew Garrett | mjg59@srcf.ucam.org -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
於 五,2013-08-09 於 06:12 +0100,Matthew Garrett 提到:
On Fri, Aug 09, 2013 at 12:37:37PM +0800, Lee, Chun-Yi wrote:
+ When machine resume from hibernate: - EFI bootloader should copy the public key from boottime variable to S4WakeKey EFI variable. - Bootloader need generated a new key-pair for next round S4 usage. It should put new privat ekey to S4SignKey variable.
So, first, this is brilliant. Thank you for putting the work into this.
Thanks for your quick response!
The only potential problem is the generation of a new key pair on every reboot. Some hardware vendors have expressed concerns about writing variables on every boot, so if we can avoid that somehow then life would probably be better.
Do they concern the life of flash memory? or concern to brick the machine because garbage collection don't trigger?
Options for that would seem to be (1) set a flag on S4 and only regenerate keys if that flag has been set (although I need to think about the security considerations of that), or (2) use a magic GUID space that all kernels (including Windows) refuse to expose to userspace. (2) is obviously conditional upon Microsoft, but let's have a chat with them to see if there's already some special-casing in Windows. It wouldn't surprise me.
I discussed with Gary for the behavior to trigger key-pair regeneration in shim. There have another thinking is: Kernel load the S4 sign key (private key) before kernel call ExitBootServices(), load it from boot-time variable. In this case, shim don't need put the private key to NV-runtime variable, so any other OS could not access the key except the window period before ExitBootServices(). And, we write a tag when hibernate to tell shim regenerate key-pair for next round S4. But, above approach means S4 signature check function limit on EFI stub kernel. User can not enable this function without using EFI stub.
I'll review the rest of these over the next few days. I've been gradually merging in the shim changes to upstream, so do feel free to send me a pull request for the S4 stuff there, too.
-- Matthew Garrett | mjg59@srcf.ucam.org
Thanks for your time to review! Joey Lee -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
On Fri, Aug 09, 2013 at 02:49:44PM +0800, joeyli wrote:
於 五,2013-08-09 於 06:12 +0100,Matthew Garrett 提到:
The only potential problem is the generation of a new key pair on every reboot. Some hardware vendors have expressed concerns about writing variables on every boot, so if we can avoid that somehow then life would probably be better.
Do they concern the life of flash memory? or concern to brick the machine because garbage collection don't trigger?
Yeah, I think the concern is wear cycles.
Kernel load the S4 sign key (private key) before kernel call ExitBootServices(), load it from boot-time variable.
That sounds like a good plan.
But, above approach means S4 signature check function limit on EFI stub kernel. User can not enable this function without using EFI stub.
Personally, I don't have a problem with that - I think using the EFI entry point is the right thing to do, especially since you need to build the kernel with EFI stub support in order to sign it. But in that case, I should really work on getting the grub support for that upstream. -- Matthew Garrett | mjg59@srcf.ucam.org -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
於 五,2013-08-09 於 08:10 +0100,Matthew Garrett 提到:
On Fri, Aug 09, 2013 at 02:49:44PM +0800, joeyli wrote:
於 五,2013-08-09 於 06:12 +0100,Matthew Garrett 提到:
The only potential problem is the generation of a new key pair on every reboot. Some hardware vendors have expressed concerns about writing variables on every boot, so if we can avoid that somehow then life would probably be better.
Do they concern the life of flash memory? or concern to brick the machine because garbage collection don't trigger?
Yeah, I think the concern is wear cycles.
Kernel load the S4 sign key (private key) before kernel call ExitBootServices(), load it from boot-time variable.
That sounds like a good plan.
But, above approach means S4 signature check function limit on EFI stub kernel. User can not enable this function without using EFI stub.
Personally, I don't have a problem with that - I think using the EFI entry point is the right thing to do, especially since you need to build the kernel with EFI stub support in order to sign it. But in that case, I should really work on getting the grub support for that upstream.
Thanks for your suggestion, then I will try the approach to load private key in EFI stub. Joey Lee -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
On Fri, 9 Aug 2013, Lee, Chun-Yi wrote:
Hi experts,
This patchset is the implementation for signature verification of hibernate snapshot image. The origin idea is from Jiri Kosina: Let EFI bootloader generate key-pair in UEFI secure boot environment, then pass it to kernel for sign/verify S4 image.
Due to there have potential threat from the S4 image hacked, it may cause SUSE lost the trust in UEFI secure boot. The hacker attack the S4 snapshot image in swap partition through whatever exploit from another trusted OS, an the exploit may don't need physical access machine.
So, this patchset give the ability to kernel for parsing the RSA key-pair from EFI bootloader, then using the private key to generate the signature of S4 snapshot image. Kernel put the signature to snapshot header, then verify the signature when kernel try to recover snapshot image to memory.
Joey, thanks a lot for all the efforts, good job. I have finally finished going through all this. Please feel free to add Reviewed-by: Jiri Kosina <jkosina@suse.cz> -- Jiri Kosina SUSE Labs -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
於 五,2013-08-09 於 09:53 +0200,Jiri Kosina 提到:
On Fri, 9 Aug 2013, Lee, Chun-Yi wrote:
Hi experts,
This patchset is the implementation for signature verification of hibernate snapshot image. The origin idea is from Jiri Kosina: Let EFI bootloader generate key-pair in UEFI secure boot environment, then pass it to kernel for sign/verify S4 image.
Due to there have potential threat from the S4 image hacked, it may cause SUSE lost the trust in UEFI secure boot. The hacker attack the S4 snapshot image in swap partition through whatever exploit from another trusted OS, an the exploit may don't need physical access machine.
So, this patchset give the ability to kernel for parsing the RSA key-pair from EFI bootloader, then using the private key to generate the signature of S4 snapshot image. Kernel put the signature to snapshot header, then verify the signature when kernel try to recover snapshot image to memory.
Joey,
thanks a lot for all the efforts, good job. I have finally finished going through all this. Please feel free to add
Reviewed-by: Jiri Kosina <jkosina@suse.cz>
-- Jiri Kosina SUSE Labs
Thanks a lot for your review! Joey Lee -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
participants (5)
-
Greg KH
-
Jiri Kosina
-
joeyli
-
Lee, Chun-Yi
-
Matthew Garrett