18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2011 Nokia Corporation 48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Intel Corporation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: 78c2ecf20Sopenharmony_ci * Dmitry Kasatkin <dmitry.kasatkin@nokia.com> 88c2ecf20Sopenharmony_ci * <dmitry.kasatkin@intel.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * File: sign.c 118c2ecf20Sopenharmony_ci * implements signature (RSA) verification 128c2ecf20Sopenharmony_ci * pkcs decoding is based on LibTomCrypt code 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/key.h> 218c2ecf20Sopenharmony_ci#include <linux/crypto.h> 228c2ecf20Sopenharmony_ci#include <crypto/hash.h> 238c2ecf20Sopenharmony_ci#include <crypto/sha.h> 248c2ecf20Sopenharmony_ci#include <keys/user-type.h> 258c2ecf20Sopenharmony_ci#include <linux/mpi.h> 268c2ecf20Sopenharmony_ci#include <linux/digsig.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic struct crypto_shash *shash; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic const char *pkcs_1_v1_5_decode_emsa(const unsigned char *msg, 318c2ecf20Sopenharmony_ci unsigned long msglen, 328c2ecf20Sopenharmony_ci unsigned long modulus_bitlen, 338c2ecf20Sopenharmony_ci unsigned long *outlen) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci unsigned long modulus_len, ps_len, i; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci /* test message size */ 408c2ecf20Sopenharmony_ci if ((msglen > modulus_len) || (modulus_len < 11)) 418c2ecf20Sopenharmony_ci return NULL; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci /* separate encoded message */ 448c2ecf20Sopenharmony_ci if (msg[0] != 0x00 || msg[1] != 0x01) 458c2ecf20Sopenharmony_ci return NULL; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci for (i = 2; i < modulus_len - 1; i++) 488c2ecf20Sopenharmony_ci if (msg[i] != 0xFF) 498c2ecf20Sopenharmony_ci break; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* separator check */ 528c2ecf20Sopenharmony_ci if (msg[i] != 0) 538c2ecf20Sopenharmony_ci /* There was no octet with hexadecimal value 0x00 548c2ecf20Sopenharmony_ci to separate ps from m. */ 558c2ecf20Sopenharmony_ci return NULL; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci ps_len = i - 2; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci *outlen = (msglen - (2 + ps_len + 1)); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return msg + 2 + ps_len + 1; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * RSA Signature verification with public key 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistatic int digsig_verify_rsa(struct key *key, 688c2ecf20Sopenharmony_ci const char *sig, int siglen, 698c2ecf20Sopenharmony_ci const char *h, int hlen) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci int err = -EINVAL; 728c2ecf20Sopenharmony_ci unsigned long len; 738c2ecf20Sopenharmony_ci unsigned long mlen, mblen; 748c2ecf20Sopenharmony_ci unsigned nret, l; 758c2ecf20Sopenharmony_ci int head, i; 768c2ecf20Sopenharmony_ci unsigned char *out1 = NULL; 778c2ecf20Sopenharmony_ci const char *m; 788c2ecf20Sopenharmony_ci MPI in = NULL, res = NULL, pkey[2]; 798c2ecf20Sopenharmony_ci uint8_t *p, *datap; 808c2ecf20Sopenharmony_ci const uint8_t *endp; 818c2ecf20Sopenharmony_ci const struct user_key_payload *ukp; 828c2ecf20Sopenharmony_ci struct pubkey_hdr *pkh; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci down_read(&key->sem); 858c2ecf20Sopenharmony_ci ukp = user_key_payload_locked(key); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (!ukp) { 888c2ecf20Sopenharmony_ci /* key was revoked before we acquired its semaphore */ 898c2ecf20Sopenharmony_ci err = -EKEYREVOKED; 908c2ecf20Sopenharmony_ci goto err1; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (ukp->datalen < sizeof(*pkh)) 948c2ecf20Sopenharmony_ci goto err1; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci pkh = (struct pubkey_hdr *)ukp->data; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (pkh->version != 1) 998c2ecf20Sopenharmony_ci goto err1; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (pkh->algo != PUBKEY_ALGO_RSA) 1028c2ecf20Sopenharmony_ci goto err1; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (pkh->nmpi != 2) 1058c2ecf20Sopenharmony_ci goto err1; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci datap = pkh->mpi; 1088c2ecf20Sopenharmony_ci endp = ukp->data + ukp->datalen; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci for (i = 0; i < pkh->nmpi; i++) { 1118c2ecf20Sopenharmony_ci unsigned int remaining = endp - datap; 1128c2ecf20Sopenharmony_ci pkey[i] = mpi_read_from_buffer(datap, &remaining); 1138c2ecf20Sopenharmony_ci if (IS_ERR(pkey[i])) { 1148c2ecf20Sopenharmony_ci err = PTR_ERR(pkey[i]); 1158c2ecf20Sopenharmony_ci goto err; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci datap += remaining; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci mblen = mpi_get_nbits(pkey[0]); 1218c2ecf20Sopenharmony_ci mlen = DIV_ROUND_UP(mblen, 8); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (mlen == 0) { 1248c2ecf20Sopenharmony_ci err = -EINVAL; 1258c2ecf20Sopenharmony_ci goto err; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci err = -ENOMEM; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci out1 = kzalloc(mlen, GFP_KERNEL); 1318c2ecf20Sopenharmony_ci if (!out1) 1328c2ecf20Sopenharmony_ci goto err; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci nret = siglen; 1358c2ecf20Sopenharmony_ci in = mpi_read_from_buffer(sig, &nret); 1368c2ecf20Sopenharmony_ci if (IS_ERR(in)) { 1378c2ecf20Sopenharmony_ci err = PTR_ERR(in); 1388c2ecf20Sopenharmony_ci goto err; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci res = mpi_alloc(mpi_get_nlimbs(in) * 2); 1428c2ecf20Sopenharmony_ci if (!res) 1438c2ecf20Sopenharmony_ci goto err; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci err = mpi_powm(res, in, pkey[1], pkey[0]); 1468c2ecf20Sopenharmony_ci if (err) 1478c2ecf20Sopenharmony_ci goto err; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (mpi_get_nlimbs(res) * BYTES_PER_MPI_LIMB > mlen) { 1508c2ecf20Sopenharmony_ci err = -EINVAL; 1518c2ecf20Sopenharmony_ci goto err; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci p = mpi_get_buffer(res, &l, NULL); 1558c2ecf20Sopenharmony_ci if (!p) { 1568c2ecf20Sopenharmony_ci err = -EINVAL; 1578c2ecf20Sopenharmony_ci goto err; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci len = mlen; 1618c2ecf20Sopenharmony_ci head = len - l; 1628c2ecf20Sopenharmony_ci memset(out1, 0, head); 1638c2ecf20Sopenharmony_ci memcpy(out1 + head, p, l); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci kfree(p); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci m = pkcs_1_v1_5_decode_emsa(out1, len, mblen, &len); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!m || len != hlen || memcmp(m, h, hlen)) 1708c2ecf20Sopenharmony_ci err = -EINVAL; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cierr: 1738c2ecf20Sopenharmony_ci mpi_free(in); 1748c2ecf20Sopenharmony_ci mpi_free(res); 1758c2ecf20Sopenharmony_ci kfree(out1); 1768c2ecf20Sopenharmony_ci while (--i >= 0) 1778c2ecf20Sopenharmony_ci mpi_free(pkey[i]); 1788c2ecf20Sopenharmony_cierr1: 1798c2ecf20Sopenharmony_ci up_read(&key->sem); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return err; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/** 1858c2ecf20Sopenharmony_ci * digsig_verify() - digital signature verification with public key 1868c2ecf20Sopenharmony_ci * @keyring: keyring to search key in 1878c2ecf20Sopenharmony_ci * @sig: digital signature 1888c2ecf20Sopenharmony_ci * @siglen: length of the signature 1898c2ecf20Sopenharmony_ci * @data: data 1908c2ecf20Sopenharmony_ci * @datalen: length of the data 1918c2ecf20Sopenharmony_ci * 1928c2ecf20Sopenharmony_ci * Returns 0 on success, -EINVAL otherwise 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * Verifies data integrity against digital signature. 1958c2ecf20Sopenharmony_ci * Currently only RSA is supported. 1968c2ecf20Sopenharmony_ci * Normally hash of the content is used as a data for this function. 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ciint digsig_verify(struct key *keyring, const char *sig, int siglen, 2008c2ecf20Sopenharmony_ci const char *data, int datalen) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int err = -ENOMEM; 2038c2ecf20Sopenharmony_ci struct signature_hdr *sh = (struct signature_hdr *)sig; 2048c2ecf20Sopenharmony_ci struct shash_desc *desc = NULL; 2058c2ecf20Sopenharmony_ci unsigned char hash[SHA1_DIGEST_SIZE]; 2068c2ecf20Sopenharmony_ci struct key *key; 2078c2ecf20Sopenharmony_ci char name[20]; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (siglen < sizeof(*sh) + 2) 2108c2ecf20Sopenharmony_ci return -EINVAL; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (sh->algo != PUBKEY_ALGO_RSA) 2138c2ecf20Sopenharmony_ci return -ENOTSUPP; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci sprintf(name, "%llX", __be64_to_cpup((uint64_t *)sh->keyid)); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (keyring) { 2188c2ecf20Sopenharmony_ci /* search in specific keyring */ 2198c2ecf20Sopenharmony_ci key_ref_t kref; 2208c2ecf20Sopenharmony_ci kref = keyring_search(make_key_ref(keyring, 1UL), 2218c2ecf20Sopenharmony_ci &key_type_user, name, true); 2228c2ecf20Sopenharmony_ci if (IS_ERR(kref)) 2238c2ecf20Sopenharmony_ci key = ERR_CAST(kref); 2248c2ecf20Sopenharmony_ci else 2258c2ecf20Sopenharmony_ci key = key_ref_to_ptr(kref); 2268c2ecf20Sopenharmony_ci } else { 2278c2ecf20Sopenharmony_ci key = request_key(&key_type_user, name, NULL); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci if (IS_ERR(key)) { 2308c2ecf20Sopenharmony_ci pr_err("key not found, id: %s\n", name); 2318c2ecf20Sopenharmony_ci return PTR_ERR(key); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(shash), 2358c2ecf20Sopenharmony_ci GFP_KERNEL); 2368c2ecf20Sopenharmony_ci if (!desc) 2378c2ecf20Sopenharmony_ci goto err; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci desc->tfm = shash; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci crypto_shash_init(desc); 2428c2ecf20Sopenharmony_ci crypto_shash_update(desc, data, datalen); 2438c2ecf20Sopenharmony_ci crypto_shash_update(desc, sig, sizeof(*sh)); 2448c2ecf20Sopenharmony_ci crypto_shash_final(desc, hash); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci kfree(desc); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* pass signature mpis address */ 2498c2ecf20Sopenharmony_ci err = digsig_verify_rsa(key, sig + sizeof(*sh), siglen - sizeof(*sh), 2508c2ecf20Sopenharmony_ci hash, sizeof(hash)); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cierr: 2538c2ecf20Sopenharmony_ci key_put(key); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return err ? -EINVAL : 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(digsig_verify); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int __init digsig_init(void) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci shash = crypto_alloc_shash("sha1", 0, 0); 2628c2ecf20Sopenharmony_ci if (IS_ERR(shash)) { 2638c2ecf20Sopenharmony_ci pr_err("shash allocation failed\n"); 2648c2ecf20Sopenharmony_ci return PTR_ERR(shash); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void __exit digsig_cleanup(void) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci crypto_free_shash(shash); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cimodule_init(digsig_init); 2778c2ecf20Sopenharmony_cimodule_exit(digsig_cleanup); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 280