162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011 Nokia Corporation 462306a36Sopenharmony_ci * Copyright (C) 2011 Intel Corporation 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: 762306a36Sopenharmony_ci * Dmitry Kasatkin <dmitry.kasatkin@nokia.com> 862306a36Sopenharmony_ci * <dmitry.kasatkin@intel.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * File: sign.c 1162306a36Sopenharmony_ci * implements signature (RSA) verification 1262306a36Sopenharmony_ci * pkcs decoding is based on LibTomCrypt code 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/key.h> 2162306a36Sopenharmony_ci#include <linux/crypto.h> 2262306a36Sopenharmony_ci#include <crypto/hash.h> 2362306a36Sopenharmony_ci#include <crypto/sha1.h> 2462306a36Sopenharmony_ci#include <keys/user-type.h> 2562306a36Sopenharmony_ci#include <linux/mpi.h> 2662306a36Sopenharmony_ci#include <linux/digsig.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct crypto_shash *shash; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic const char *pkcs_1_v1_5_decode_emsa(const unsigned char *msg, 3162306a36Sopenharmony_ci unsigned long msglen, 3262306a36Sopenharmony_ci unsigned long modulus_bitlen, 3362306a36Sopenharmony_ci unsigned long *outlen) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci unsigned long modulus_len, ps_len, i; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* test message size */ 4062306a36Sopenharmony_ci if ((msglen > modulus_len) || (modulus_len < 11)) 4162306a36Sopenharmony_ci return NULL; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* separate encoded message */ 4462306a36Sopenharmony_ci if (msg[0] != 0x00 || msg[1] != 0x01) 4562306a36Sopenharmony_ci return NULL; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci for (i = 2; i < modulus_len - 1; i++) 4862306a36Sopenharmony_ci if (msg[i] != 0xFF) 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* separator check */ 5262306a36Sopenharmony_ci if (msg[i] != 0) 5362306a36Sopenharmony_ci /* There was no octet with hexadecimal value 0x00 5462306a36Sopenharmony_ci to separate ps from m. */ 5562306a36Sopenharmony_ci return NULL; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci ps_len = i - 2; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci *outlen = (msglen - (2 + ps_len + 1)); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return msg + 2 + ps_len + 1; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* 6562306a36Sopenharmony_ci * RSA Signature verification with public key 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_cistatic int digsig_verify_rsa(struct key *key, 6862306a36Sopenharmony_ci const char *sig, int siglen, 6962306a36Sopenharmony_ci const char *h, int hlen) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci int err = -EINVAL; 7262306a36Sopenharmony_ci unsigned long len; 7362306a36Sopenharmony_ci unsigned long mlen, mblen; 7462306a36Sopenharmony_ci unsigned nret, l; 7562306a36Sopenharmony_ci int head, i; 7662306a36Sopenharmony_ci unsigned char *out1 = NULL; 7762306a36Sopenharmony_ci const char *m; 7862306a36Sopenharmony_ci MPI in = NULL, res = NULL, pkey[2]; 7962306a36Sopenharmony_ci uint8_t *p, *datap; 8062306a36Sopenharmony_ci const uint8_t *endp; 8162306a36Sopenharmony_ci const struct user_key_payload *ukp; 8262306a36Sopenharmony_ci struct pubkey_hdr *pkh; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci down_read(&key->sem); 8562306a36Sopenharmony_ci ukp = user_key_payload_locked(key); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!ukp) { 8862306a36Sopenharmony_ci /* key was revoked before we acquired its semaphore */ 8962306a36Sopenharmony_ci err = -EKEYREVOKED; 9062306a36Sopenharmony_ci goto err1; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (ukp->datalen < sizeof(*pkh)) 9462306a36Sopenharmony_ci goto err1; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci pkh = (struct pubkey_hdr *)ukp->data; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (pkh->version != 1) 9962306a36Sopenharmony_ci goto err1; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (pkh->algo != PUBKEY_ALGO_RSA) 10262306a36Sopenharmony_ci goto err1; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (pkh->nmpi != 2) 10562306a36Sopenharmony_ci goto err1; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci datap = pkh->mpi; 10862306a36Sopenharmony_ci endp = ukp->data + ukp->datalen; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for (i = 0; i < pkh->nmpi; i++) { 11162306a36Sopenharmony_ci unsigned int remaining = endp - datap; 11262306a36Sopenharmony_ci pkey[i] = mpi_read_from_buffer(datap, &remaining); 11362306a36Sopenharmony_ci if (IS_ERR(pkey[i])) { 11462306a36Sopenharmony_ci err = PTR_ERR(pkey[i]); 11562306a36Sopenharmony_ci goto err; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci datap += remaining; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci mblen = mpi_get_nbits(pkey[0]); 12162306a36Sopenharmony_ci mlen = DIV_ROUND_UP(mblen, 8); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (mlen == 0) { 12462306a36Sopenharmony_ci err = -EINVAL; 12562306a36Sopenharmony_ci goto err; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci err = -ENOMEM; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci out1 = kzalloc(mlen, GFP_KERNEL); 13162306a36Sopenharmony_ci if (!out1) 13262306a36Sopenharmony_ci goto err; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci nret = siglen; 13562306a36Sopenharmony_ci in = mpi_read_from_buffer(sig, &nret); 13662306a36Sopenharmony_ci if (IS_ERR(in)) { 13762306a36Sopenharmony_ci err = PTR_ERR(in); 13862306a36Sopenharmony_ci goto err; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci res = mpi_alloc(mpi_get_nlimbs(in) * 2); 14262306a36Sopenharmony_ci if (!res) 14362306a36Sopenharmony_ci goto err; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci err = mpi_powm(res, in, pkey[1], pkey[0]); 14662306a36Sopenharmony_ci if (err) 14762306a36Sopenharmony_ci goto err; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (mpi_get_nlimbs(res) * BYTES_PER_MPI_LIMB > mlen) { 15062306a36Sopenharmony_ci err = -EINVAL; 15162306a36Sopenharmony_ci goto err; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci p = mpi_get_buffer(res, &l, NULL); 15562306a36Sopenharmony_ci if (!p) { 15662306a36Sopenharmony_ci err = -EINVAL; 15762306a36Sopenharmony_ci goto err; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci len = mlen; 16162306a36Sopenharmony_ci head = len - l; 16262306a36Sopenharmony_ci memset(out1, 0, head); 16362306a36Sopenharmony_ci memcpy(out1 + head, p, l); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci kfree(p); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci m = pkcs_1_v1_5_decode_emsa(out1, len, mblen, &len); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (!m || len != hlen || memcmp(m, h, hlen)) 17062306a36Sopenharmony_ci err = -EINVAL; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cierr: 17362306a36Sopenharmony_ci mpi_free(in); 17462306a36Sopenharmony_ci mpi_free(res); 17562306a36Sopenharmony_ci kfree(out1); 17662306a36Sopenharmony_ci while (--i >= 0) 17762306a36Sopenharmony_ci mpi_free(pkey[i]); 17862306a36Sopenharmony_cierr1: 17962306a36Sopenharmony_ci up_read(&key->sem); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return err; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/** 18562306a36Sopenharmony_ci * digsig_verify() - digital signature verification with public key 18662306a36Sopenharmony_ci * @keyring: keyring to search key in 18762306a36Sopenharmony_ci * @sig: digital signature 18862306a36Sopenharmony_ci * @siglen: length of the signature 18962306a36Sopenharmony_ci * @data: data 19062306a36Sopenharmony_ci * @datalen: length of the data 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * Returns 0 on success, -EINVAL otherwise 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Verifies data integrity against digital signature. 19562306a36Sopenharmony_ci * Currently only RSA is supported. 19662306a36Sopenharmony_ci * Normally hash of the content is used as a data for this function. 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ciint digsig_verify(struct key *keyring, const char *sig, int siglen, 20062306a36Sopenharmony_ci const char *data, int datalen) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci int err = -ENOMEM; 20362306a36Sopenharmony_ci struct signature_hdr *sh = (struct signature_hdr *)sig; 20462306a36Sopenharmony_ci struct shash_desc *desc = NULL; 20562306a36Sopenharmony_ci unsigned char hash[SHA1_DIGEST_SIZE]; 20662306a36Sopenharmony_ci struct key *key; 20762306a36Sopenharmony_ci char name[20]; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (siglen < sizeof(*sh) + 2) 21062306a36Sopenharmony_ci return -EINVAL; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (sh->algo != PUBKEY_ALGO_RSA) 21362306a36Sopenharmony_ci return -ENOTSUPP; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci sprintf(name, "%llX", __be64_to_cpup((uint64_t *)sh->keyid)); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (keyring) { 21862306a36Sopenharmony_ci /* search in specific keyring */ 21962306a36Sopenharmony_ci key_ref_t kref; 22062306a36Sopenharmony_ci kref = keyring_search(make_key_ref(keyring, 1UL), 22162306a36Sopenharmony_ci &key_type_user, name, true); 22262306a36Sopenharmony_ci if (IS_ERR(kref)) 22362306a36Sopenharmony_ci key = ERR_CAST(kref); 22462306a36Sopenharmony_ci else 22562306a36Sopenharmony_ci key = key_ref_to_ptr(kref); 22662306a36Sopenharmony_ci } else { 22762306a36Sopenharmony_ci key = request_key(&key_type_user, name, NULL); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci if (IS_ERR(key)) { 23062306a36Sopenharmony_ci pr_err("key not found, id: %s\n", name); 23162306a36Sopenharmony_ci return PTR_ERR(key); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(shash), 23562306a36Sopenharmony_ci GFP_KERNEL); 23662306a36Sopenharmony_ci if (!desc) 23762306a36Sopenharmony_ci goto err; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci desc->tfm = shash; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci crypto_shash_init(desc); 24262306a36Sopenharmony_ci crypto_shash_update(desc, data, datalen); 24362306a36Sopenharmony_ci crypto_shash_update(desc, sig, sizeof(*sh)); 24462306a36Sopenharmony_ci crypto_shash_final(desc, hash); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci kfree(desc); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* pass signature mpis address */ 24962306a36Sopenharmony_ci err = digsig_verify_rsa(key, sig + sizeof(*sh), siglen - sizeof(*sh), 25062306a36Sopenharmony_ci hash, sizeof(hash)); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cierr: 25362306a36Sopenharmony_ci key_put(key); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return err ? -EINVAL : 0; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(digsig_verify); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int __init digsig_init(void) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci shash = crypto_alloc_shash("sha1", 0, 0); 26262306a36Sopenharmony_ci if (IS_ERR(shash)) { 26362306a36Sopenharmony_ci pr_err("shash allocation failed\n"); 26462306a36Sopenharmony_ci return PTR_ERR(shash); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void __exit digsig_cleanup(void) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci crypto_free_shash(shash); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cimodule_init(digsig_init); 27762306a36Sopenharmony_cimodule_exit(digsig_cleanup); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 280