linux/lib/digsig.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011 Nokia Corporation
   3 * Copyright (C) 2011 Intel Corporation
   4 *
   5 * Author:
   6 * Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
   7 *                 <dmitry.kasatkin@intel.com>
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation, version 2 of the License.
  12 *
  13 * File: sign.c
  14 *      implements signature (RSA) verification
  15 *      pkcs decoding is based on LibTomCrypt code
  16 */
  17
  18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  19
  20#include <linux/err.h>
  21#include <linux/module.h>
  22#include <linux/slab.h>
  23#include <linux/key.h>
  24#include <linux/crypto.h>
  25#include <crypto/hash.h>
  26#include <crypto/sha.h>
  27#include <keys/user-type.h>
  28#include <linux/mpi.h>
  29#include <linux/digsig.h>
  30
  31static struct crypto_shash *shash;
  32
  33static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg,
  34                        unsigned long  msglen,
  35                        unsigned long  modulus_bitlen,
  36                        unsigned char *out,
  37                        unsigned long *outlen)
  38{
  39        unsigned long modulus_len, ps_len, i;
  40
  41        modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
  42
  43        /* test message size */
  44        if ((msglen > modulus_len) || (modulus_len < 11))
  45                return -EINVAL;
  46
  47        /* separate encoded message */
  48        if ((msg[0] != 0x00) || (msg[1] != (unsigned char)1))
  49                return -EINVAL;
  50
  51        for (i = 2; i < modulus_len - 1; i++)
  52                if (msg[i] != 0xFF)
  53                        break;
  54
  55        /* separator check */
  56        if (msg[i] != 0)
  57                /* There was no octet with hexadecimal value 0x00
  58                to separate ps from m. */
  59                return -EINVAL;
  60
  61        ps_len = i - 2;
  62
  63        if (*outlen < (msglen - (2 + ps_len + 1))) {
  64                *outlen = msglen - (2 + ps_len + 1);
  65                return -EOVERFLOW;
  66        }
  67
  68        *outlen = (msglen - (2 + ps_len + 1));
  69        memcpy(out, &msg[2 + ps_len + 1], *outlen);
  70
  71        return 0;
  72}
  73
  74/*
  75 * RSA Signature verification with public key
  76 */
  77static int digsig_verify_rsa(struct key *key,
  78                    const char *sig, int siglen,
  79                       const char *h, int hlen)
  80{
  81        int err = -EINVAL;
  82        unsigned long len;
  83        unsigned long mlen, mblen;
  84        unsigned nret, l;
  85        int head, i;
  86        unsigned char *out1 = NULL, *out2 = NULL;
  87        MPI in = NULL, res = NULL, pkey[2];
  88        uint8_t *p, *datap, *endp;
  89        struct user_key_payload *ukp;
  90        struct pubkey_hdr *pkh;
  91
  92        down_read(&key->sem);
  93        ukp = key->payload.data;
  94
  95        if (ukp->datalen < sizeof(*pkh))
  96                goto err1;
  97
  98        pkh = (struct pubkey_hdr *)ukp->data;
  99
 100        if (pkh->version != 1)
 101                goto err1;
 102
 103        if (pkh->algo != PUBKEY_ALGO_RSA)
 104                goto err1;
 105
 106        if (pkh->nmpi != 2)
 107                goto err1;
 108
 109        datap = pkh->mpi;
 110        endp = ukp->data + ukp->datalen;
 111
 112        err = -ENOMEM;
 113
 114        for (i = 0; i < pkh->nmpi; i++) {
 115                unsigned int remaining = endp - datap;
 116                pkey[i] = mpi_read_from_buffer(datap, &remaining);
 117                if (!pkey[i])
 118                        goto err;
 119                datap += remaining;
 120        }
 121
 122        mblen = mpi_get_nbits(pkey[0]);
 123        mlen = (mblen + 7)/8;
 124
 125        if (mlen == 0)
 126                goto err;
 127
 128        out1 = kzalloc(mlen, GFP_KERNEL);
 129        if (!out1)
 130                goto err;
 131
 132        out2 = kzalloc(mlen, GFP_KERNEL);
 133        if (!out2)
 134                goto err;
 135
 136        nret = siglen;
 137        in = mpi_read_from_buffer(sig, &nret);
 138        if (!in)
 139                goto err;
 140
 141        res = mpi_alloc(mpi_get_nlimbs(in) * 2);
 142        if (!res)
 143                goto err;
 144
 145        err = mpi_powm(res, in, pkey[1], pkey[0]);
 146        if (err)
 147                goto err;
 148
 149        if (mpi_get_nlimbs(res) * BYTES_PER_MPI_LIMB > mlen) {
 150                err = -EINVAL;
 151                goto err;
 152        }
 153
 154        p = mpi_get_buffer(res, &l, NULL);
 155        if (!p) {
 156                err = -EINVAL;
 157                goto err;
 158        }
 159
 160        len = mlen;
 161        head = len - l;
 162        memset(out1, 0, head);
 163        memcpy(out1 + head, p, l);
 164
 165        kfree(p);
 166
 167        err = pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len);
 168        if (err)
 169                goto err;
 170
 171        if (len != hlen || memcmp(out2, h, hlen))
 172                err = -EINVAL;
 173
 174err:
 175        mpi_free(in);
 176        mpi_free(res);
 177        kfree(out1);
 178        kfree(out2);
 179        while (--i >= 0)
 180                mpi_free(pkey[i]);
 181err1:
 182        up_read(&key->sem);
 183
 184        return err;
 185}
 186
 187/**
 188 * digsig_verify() - digital signature verification with public key
 189 * @keyring:    keyring to search key in
 190 * @sig:        digital signature
 191 * @sigen:      length of the signature
 192 * @data:       data
 193 * @datalen:    length of the data
 194 * @return:     0 on success, -EINVAL otherwise
 195 *
 196 * Verifies data integrity against digital signature.
 197 * Currently only RSA is supported.
 198 * Normally hash of the content is used as a data for this function.
 199 *
 200 */
 201int digsig_verify(struct key *keyring, const char *sig, int siglen,
 202                                                const char *data, int datalen)
 203{
 204        int err = -ENOMEM;
 205        struct signature_hdr *sh = (struct signature_hdr *)sig;
 206        struct shash_desc *desc = NULL;
 207        unsigned char hash[SHA1_DIGEST_SIZE];
 208        struct key *key;
 209        char name[20];
 210
 211        if (siglen < sizeof(*sh) + 2)
 212                return -EINVAL;
 213
 214        if (sh->algo != PUBKEY_ALGO_RSA)
 215                return -ENOTSUPP;
 216
 217        sprintf(name, "%llX", __be64_to_cpup((uint64_t *)sh->keyid));
 218
 219        if (keyring) {
 220                /* search in specific keyring */
 221                key_ref_t kref;
 222                kref = keyring_search(make_key_ref(keyring, 1UL),
 223                                                &key_type_user, name);
 224                if (IS_ERR(kref))
 225                        key = ERR_PTR(PTR_ERR(kref));
 226                else
 227                        key = key_ref_to_ptr(kref);
 228        } else {
 229                key = request_key(&key_type_user, name, NULL);
 230        }
 231        if (IS_ERR(key)) {
 232                pr_err("key not found, id: %s\n", name);
 233                return PTR_ERR(key);
 234        }
 235
 236        desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(shash),
 237                       GFP_KERNEL);
 238        if (!desc)
 239                goto err;
 240
 241        desc->tfm = shash;
 242        desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
 243
 244        crypto_shash_init(desc);
 245        crypto_shash_update(desc, data, datalen);
 246        crypto_shash_update(desc, sig, sizeof(*sh));
 247        crypto_shash_final(desc, hash);
 248
 249        kfree(desc);
 250
 251        /* pass signature mpis address */
 252        err = digsig_verify_rsa(key, sig + sizeof(*sh), siglen - sizeof(*sh),
 253                             hash, sizeof(hash));
 254
 255err:
 256        key_put(key);
 257
 258        return err ? -EINVAL : 0;
 259}
 260EXPORT_SYMBOL_GPL(digsig_verify);
 261
 262static int __init digsig_init(void)
 263{
 264        shash = crypto_alloc_shash("sha1", 0, 0);
 265        if (IS_ERR(shash)) {
 266                pr_err("shash allocation failed\n");
 267                return  PTR_ERR(shash);
 268        }
 269
 270        return 0;
 271
 272}
 273
 274static void __exit digsig_cleanup(void)
 275{
 276        crypto_free_shash(shash);
 277}
 278
 279module_init(digsig_init);
 280module_exit(digsig_cleanup);
 281
 282MODULE_LICENSE("GPL");
 283
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.