Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/crypto/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 26 kB image not shown  

Quelle  ahash.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Asynchronous Cryptographic Hash operations.
 *
 * This is the implementation of the ahash (asynchronous hash) API.  It differs
 * from shash (synchronous hash) in that ahash supports asynchronous operations,
 * and it hashes data from scatterlists instead of virtually addressed buffers.
 *
 * The ahash API provides access to both ahash and shash algorithms.  The shash
 * API only provides access to shash algorithms.
 *
 * Copyright (c) 2008 Loc Ho <lho@amcc.com>
 */


#include <crypto/scatterwalk.h>
#include <linux/cryptouser.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/string_choices.h>
#include <net/netlink.h>

#include "hash.h"

#define CRYPTO_ALG_TYPE_AHASH_MASK 0x0000000e

static int ahash_def_finup(struct ahash_request *req);

static inline bool crypto_ahash_block_only(struct crypto_ahash *tfm)
{
 return crypto_ahash_alg(tfm)->halg.base.cra_flags &
        CRYPTO_AHASH_ALG_BLOCK_ONLY;
}

static inline bool crypto_ahash_final_nonzero(struct crypto_ahash *tfm)
{
 return crypto_ahash_alg(tfm)->halg.base.cra_flags &
        CRYPTO_AHASH_ALG_FINAL_NONZERO;
}

static inline bool crypto_ahash_need_fallback(struct crypto_ahash *tfm)
{
 return crypto_ahash_alg(tfm)->halg.base.cra_flags &
        CRYPTO_ALG_NEED_FALLBACK;
}

static inline void ahash_op_done(void *data, int err,
     int (*finish)(struct ahash_request *, int))
{
 struct ahash_request *areq = data;
 crypto_completion_t compl;

 compl = areq->saved_complete;
 data = areq->saved_data;
 if (err == -EINPROGRESS)
  goto out;

 areq->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;

 err = finish(areq, err);
 if (err == -EINPROGRESS || err == -EBUSY)
  return;

out:
 compl(data, err);
}

static int hash_walk_next(struct crypto_hash_walk *walk)
{
 unsigned int offset = walk->offset;
 unsigned int nbytes = min(walk->entrylen,
      ((unsigned int)(PAGE_SIZE)) - offset);

 walk->data = kmap_local_page(walk->pg);
 walk->data += offset;
 walk->entrylen -= nbytes;
 return nbytes;
}

static int hash_walk_new_entry(struct crypto_hash_walk *walk)
{
 struct scatterlist *sg;

 sg = walk->sg;
 walk->offset = sg->offset;
 walk->pg = nth_page(sg_page(walk->sg), (walk->offset >> PAGE_SHIFT));
 walk->offset = offset_in_page(walk->offset);
 walk->entrylen = sg->length;

 if (walk->entrylen > walk->total)
  walk->entrylen = walk->total;
 walk->total -= walk->entrylen;

 return hash_walk_next(walk);
}

int crypto_hash_walk_first(struct ahash_request *req,
      struct crypto_hash_walk *walk)
{
 walk->total = req->nbytes;
 walk->entrylen = 0;

 if (!walk->total)
  return 0;

 walk->flags = req->base.flags;

 if (ahash_request_isvirt(req)) {
  walk->data = req->svirt;
  walk->total = 0;
  return req->nbytes;
 }

 walk->sg = req->src;

 return hash_walk_new_entry(walk);
}
EXPORT_SYMBOL_GPL(crypto_hash_walk_first);

int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err)
{
 if ((walk->flags & CRYPTO_AHASH_REQ_VIRT))
  return err;

 walk->data -= walk->offset;

 kunmap_local(walk->data);
 crypto_yield(walk->flags);

 if (err)
  return err;

 if (walk->entrylen) {
  walk->offset = 0;
  walk->pg++;
  return hash_walk_next(walk);
 }

 if (!walk->total)
  return 0;

 walk->sg = sg_next(walk->sg);

 return hash_walk_new_entry(walk);
}
EXPORT_SYMBOL_GPL(crypto_hash_walk_done);

/*
 * For an ahash tfm that is using an shash algorithm (instead of an ahash
 * algorithm), this returns the underlying shash tfm.
 */

static inline struct crypto_shash *ahash_to_shash(struct crypto_ahash *tfm)
{
 return *(struct crypto_shash **)crypto_ahash_ctx(tfm);
}

static inline struct shash_desc *prepare_shash_desc(struct ahash_request *req,
          struct crypto_ahash *tfm)
{
 struct shash_desc *desc = ahash_request_ctx(req);

 desc->tfm = ahash_to_shash(tfm);
 return desc;
}

int shash_ahash_update(struct ahash_request *req, struct shash_desc *desc)
{
 struct crypto_hash_walk walk;
 int nbytes;

 for (nbytes = crypto_hash_walk_first(req, &walk); nbytes > 0;
      nbytes = crypto_hash_walk_done(&walk, nbytes))
  nbytes = crypto_shash_update(desc, walk.data, nbytes);

 return nbytes;
}
EXPORT_SYMBOL_GPL(shash_ahash_update);

int shash_ahash_finup(struct ahash_request *req, struct shash_desc *desc)
{
 struct crypto_hash_walk walk;
 int nbytes;

 nbytes = crypto_hash_walk_first(req, &walk);
 if (!nbytes)
  return crypto_shash_final(desc, req->result);

 do {
  nbytes = crypto_hash_walk_last(&walk) ?
    crypto_shash_finup(desc, walk.data, nbytes,
         req->result) :
    crypto_shash_update(desc, walk.data, nbytes);
  nbytes = crypto_hash_walk_done(&walk, nbytes);
 } while (nbytes > 0);

 return nbytes;
}
EXPORT_SYMBOL_GPL(shash_ahash_finup);

int shash_ahash_digest(struct ahash_request *req, struct shash_desc *desc)
{
 unsigned int nbytes = req->nbytes;
 struct scatterlist *sg;
 unsigned int offset;
 struct page *page;
 const u8 *data;
 int err;

 data = req->svirt;
 if (!nbytes || ahash_request_isvirt(req))
  return crypto_shash_digest(desc, data, nbytes, req->result);

 sg = req->src;
 if (nbytes > sg->length)
  return crypto_shash_init(desc) ?:
         shash_ahash_finup(req, desc);

 page = sg_page(sg);
 offset = sg->offset;
 data = lowmem_page_address(page) + offset;
 if (!IS_ENABLED(CONFIG_HIGHMEM))
  return crypto_shash_digest(desc, data, nbytes, req->result);

 page = nth_page(page, offset >> PAGE_SHIFT);
 offset = offset_in_page(offset);

 if (nbytes > (unsigned int)PAGE_SIZE - offset)
  return crypto_shash_init(desc) ?:
         shash_ahash_finup(req, desc);

 data = kmap_local_page(page);
 err = crypto_shash_digest(desc, data + offset, nbytes,
      req->result);
 kunmap_local(data);
 return err;
}
EXPORT_SYMBOL_GPL(shash_ahash_digest);

static void crypto_exit_ahash_using_shash(struct crypto_tfm *tfm)
{
 struct crypto_shash **ctx = crypto_tfm_ctx(tfm);

 crypto_free_shash(*ctx);
}

static int crypto_init_ahash_using_shash(struct crypto_tfm *tfm)
{
 struct crypto_alg *calg = tfm->__crt_alg;
 struct crypto_ahash *crt = __crypto_ahash_cast(tfm);
 struct crypto_shash **ctx = crypto_tfm_ctx(tfm);
 struct crypto_shash *shash;

 if (!crypto_mod_get(calg))
  return -EAGAIN;

 shash = crypto_create_tfm(calg, &crypto_shash_type);
 if (IS_ERR(shash)) {
  crypto_mod_put(calg);
  return PTR_ERR(shash);
 }

 crt->using_shash = true;
 *ctx = shash;
 tfm->exit = crypto_exit_ahash_using_shash;

 crypto_ahash_set_flags(crt, crypto_shash_get_flags(shash) &
        CRYPTO_TFM_NEED_KEY);

 return 0;
}

static int ahash_nosetkey(struct crypto_ahash *tfm, const u8 *key,
     unsigned int keylen)
{
 return -ENOSYS;
}

static void ahash_set_needkey(struct crypto_ahash *tfm, struct ahash_alg *alg)
{
 if (alg->setkey != ahash_nosetkey &&
     !(alg->halg.base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY))
  crypto_ahash_set_flags(tfm, CRYPTO_TFM_NEED_KEY);
}

int crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
   unsigned int keylen)
{
 if (likely(tfm->using_shash)) {
  struct crypto_shash *shash = ahash_to_shash(tfm);
  int err;

  err = crypto_shash_setkey(shash, key, keylen);
  if (unlikely(err)) {
   crypto_ahash_set_flags(tfm,
            crypto_shash_get_flags(shash) &
            CRYPTO_TFM_NEED_KEY);
   return err;
  }
 } else {
  struct ahash_alg *alg = crypto_ahash_alg(tfm);
  int err;

  err = alg->setkey(tfm, key, keylen);
  if (!err && crypto_ahash_need_fallback(tfm))
   err = crypto_ahash_setkey(crypto_ahash_fb(tfm),
        key, keylen);
  if (unlikely(err)) {
   ahash_set_needkey(tfm, alg);
   return err;
  }
 }
 crypto_ahash_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
 return 0;
}
EXPORT_SYMBOL_GPL(crypto_ahash_setkey);

static int ahash_do_req_chain(struct ahash_request *req,
         int (*const *op)(struct ahash_request *req))
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 int err;

 if (crypto_ahash_req_virt(tfm) || !ahash_request_isvirt(req))
  return (*op)(req);

 if (crypto_ahash_statesize(tfm) > HASH_MAX_STATESIZE)
  return -ENOSYS;

 if (!crypto_ahash_need_fallback(tfm))
  return -ENOSYS;

 if (crypto_hash_no_export_core(tfm))
  return -ENOSYS;

 {
  u8 state[HASH_MAX_STATESIZE];

  if (op == &crypto_ahash_alg(tfm)->digest) {
   ahash_request_set_tfm(req, crypto_ahash_fb(tfm));
   err = crypto_ahash_digest(req);
   goto out_no_state;
  }

  err = crypto_ahash_export(req, state);
  ahash_request_set_tfm(req, crypto_ahash_fb(tfm));
  err = err ?: crypto_ahash_import(req, state);

  if (op == &crypto_ahash_alg(tfm)->finup) {
   err = err ?: crypto_ahash_finup(req);
   goto out_no_state;
  }

  err = err ?:
        crypto_ahash_update(req) ?:
        crypto_ahash_export(req, state);

  ahash_request_set_tfm(req, tfm);
  return err ?: crypto_ahash_import(req, state);

out_no_state:
  ahash_request_set_tfm(req, tfm);
  return err;
 }
}

int crypto_ahash_init(struct ahash_request *req)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);

 if (likely(tfm->using_shash))
  return crypto_shash_init(prepare_shash_desc(req, tfm));
 if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
  return -ENOKEY;
 if (ahash_req_on_stack(req) && ahash_is_async(tfm))
  return -EAGAIN;
 if (crypto_ahash_block_only(tfm)) {
  u8 *buf = ahash_request_ctx(req);

  buf += crypto_ahash_reqsize(tfm) - 1;
  *buf = 0;
 }
 return crypto_ahash_alg(tfm)->init(req);
}
EXPORT_SYMBOL_GPL(crypto_ahash_init);

static void ahash_save_req(struct ahash_request *req, crypto_completion_t cplt)
{
 req->saved_complete = req->base.complete;
 req->saved_data = req->base.data;
 req->base.complete = cplt;
 req->base.data = req;
}

static void ahash_restore_req(struct ahash_request *req)
{
 req->base.complete = req->saved_complete;
 req->base.data = req->saved_data;
}

static int ahash_update_finish(struct ahash_request *req, int err)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 bool nonzero = crypto_ahash_final_nonzero(tfm);
 int bs = crypto_ahash_blocksize(tfm);
 u8 *blenp = ahash_request_ctx(req);
 int blen;
 u8 *buf;

 blenp += crypto_ahash_reqsize(tfm) - 1;
 blen = *blenp;
 buf = blenp - bs;

 if (blen) {
  req->src = req->sg_head + 1;
  if (sg_is_chain(req->src))
   req->src = sg_chain_ptr(req->src);
 }

 req->nbytes += nonzero - blen;

 blen = err < 0 ? 0 : err + nonzero;
 if (ahash_request_isvirt(req))
  memcpy(buf, req->svirt + req->nbytes - blen, blen);
 else
  memcpy_from_sglist(buf, req->src, req->nbytes - blen, blen);
 *blenp = blen;

 ahash_restore_req(req);

 return err;
}

static void ahash_update_done(void *data, int err)
{
 ahash_op_done(data, err, ahash_update_finish);
}

int crypto_ahash_update(struct ahash_request *req)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 bool nonzero = crypto_ahash_final_nonzero(tfm);
 int bs = crypto_ahash_blocksize(tfm);
 u8 *blenp = ahash_request_ctx(req);
 int blen, err;
 u8 *buf;

 if (likely(tfm->using_shash))
  return shash_ahash_update(req, ahash_request_ctx(req));
 if (ahash_req_on_stack(req) && ahash_is_async(tfm))
  return -EAGAIN;
 if (!crypto_ahash_block_only(tfm))
  return ahash_do_req_chain(req, &crypto_ahash_alg(tfm)->update);

 blenp += crypto_ahash_reqsize(tfm) - 1;
 blen = *blenp;
 buf = blenp - bs;

 if (blen + req->nbytes < bs + nonzero) {
  if (ahash_request_isvirt(req))
   memcpy(buf + blen, req->svirt, req->nbytes);
  else
   memcpy_from_sglist(buf + blen, req->src, 0,
        req->nbytes);

  *blenp += req->nbytes;
  return 0;
 }

 if (blen) {
  memset(req->sg_head, 0, sizeof(req->sg_head[0]));
  sg_set_buf(req->sg_head, buf, blen);
  if (req->src != req->sg_head + 1)
   sg_chain(req->sg_head, 2, req->src);
  req->src = req->sg_head;
  req->nbytes += blen;
 }
 req->nbytes -= nonzero;

 ahash_save_req(req, ahash_update_done);

 err = ahash_do_req_chain(req, &crypto_ahash_alg(tfm)->update);
 if (err == -EINPROGRESS || err == -EBUSY)
  return err;

 return ahash_update_finish(req, err);
}
EXPORT_SYMBOL_GPL(crypto_ahash_update);

static int ahash_finup_finish(struct ahash_request *req, int err)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 u8 *blenp = ahash_request_ctx(req);
 int blen;

 blenp += crypto_ahash_reqsize(tfm) - 1;
 blen = *blenp;

 if (blen) {
  if (sg_is_last(req->src))
   req->src = NULL;
  else {
   req->src = req->sg_head + 1;
   if (sg_is_chain(req->src))
    req->src = sg_chain_ptr(req->src);
  }
  req->nbytes -= blen;
 }

 ahash_restore_req(req);

 return err;
}

static void ahash_finup_done(void *data, int err)
{
 ahash_op_done(data, err, ahash_finup_finish);
}

int crypto_ahash_finup(struct ahash_request *req)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 int bs = crypto_ahash_blocksize(tfm);
 u8 *blenp = ahash_request_ctx(req);
 int blen, err;
 u8 *buf;

 if (likely(tfm->using_shash))
  return shash_ahash_finup(req, ahash_request_ctx(req));
 if (ahash_req_on_stack(req) && ahash_is_async(tfm))
  return -EAGAIN;
 if (!crypto_ahash_alg(tfm)->finup)
  return ahash_def_finup(req);
 if (!crypto_ahash_block_only(tfm))
  return ahash_do_req_chain(req, &crypto_ahash_alg(tfm)->finup);

 blenp += crypto_ahash_reqsize(tfm) - 1;
 blen = *blenp;
 buf = blenp - bs;

 if (blen) {
  memset(req->sg_head, 0, sizeof(req->sg_head[0]));
  sg_set_buf(req->sg_head, buf, blen);
  if (!req->src)
   sg_mark_end(req->sg_head);
  else if (req->src != req->sg_head + 1)
   sg_chain(req->sg_head, 2, req->src);
  req->src = req->sg_head;
  req->nbytes += blen;
 }

 ahash_save_req(req, ahash_finup_done);

 err = ahash_do_req_chain(req, &crypto_ahash_alg(tfm)->finup);
 if (err == -EINPROGRESS || err == -EBUSY)
  return err;

 return ahash_finup_finish(req, err);
}
EXPORT_SYMBOL_GPL(crypto_ahash_finup);

int crypto_ahash_digest(struct ahash_request *req)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);

 if (likely(tfm->using_shash))
  return shash_ahash_digest(req, prepare_shash_desc(req, tfm));
 if (ahash_req_on_stack(req) && ahash_is_async(tfm))
  return -EAGAIN;
 if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
  return -ENOKEY;
 return ahash_do_req_chain(req, &crypto_ahash_alg(tfm)->digest);
}
EXPORT_SYMBOL_GPL(crypto_ahash_digest);

static void ahash_def_finup_done2(void *data, int err)
{
 struct ahash_request *areq = data;

 if (err == -EINPROGRESS)
  return;

 ahash_restore_req(areq);
 ahash_request_complete(areq, err);
}

static int ahash_def_finup_finish1(struct ahash_request *req, int err)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);

 if (err)
  goto out;

 req->base.complete = ahash_def_finup_done2;

 err = crypto_ahash_alg(tfm)->final(req);
 if (err == -EINPROGRESS || err == -EBUSY)
  return err;

out:
 ahash_restore_req(req);
 return err;
}

static void ahash_def_finup_done1(void *data, int err)
{
 ahash_op_done(data, err, ahash_def_finup_finish1);
}

static int ahash_def_finup(struct ahash_request *req)
{
 int err;

 ahash_save_req(req, ahash_def_finup_done1);

 err = crypto_ahash_update(req);
 if (err == -EINPROGRESS || err == -EBUSY)
  return err;

 return ahash_def_finup_finish1(req, err);
}

int crypto_ahash_export_core(struct ahash_request *req, void *out)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);

 if (likely(tfm->using_shash))
  return crypto_shash_export_core(ahash_request_ctx(req), out);
 return crypto_ahash_alg(tfm)->export_core(req, out);
}
EXPORT_SYMBOL_GPL(crypto_ahash_export_core);

int crypto_ahash_export(struct ahash_request *req, void *out)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);

 if (likely(tfm->using_shash))
  return crypto_shash_export(ahash_request_ctx(req), out);
 if (crypto_ahash_block_only(tfm)) {
  unsigned int plen = crypto_ahash_blocksize(tfm) + 1;
  unsigned int reqsize = crypto_ahash_reqsize(tfm);
  unsigned int ss = crypto_ahash_statesize(tfm);
  u8 *buf = ahash_request_ctx(req);

  memcpy(out + ss - plen, buf + reqsize - plen, plen);
 }
 return crypto_ahash_alg(tfm)->export(req, out);
}
EXPORT_SYMBOL_GPL(crypto_ahash_export);

int crypto_ahash_import_core(struct ahash_request *req, const void *in)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);

 if (likely(tfm->using_shash))
  return crypto_shash_import_core(prepare_shash_desc(req, tfm),
      in);
 if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
  return -ENOKEY;
 return crypto_ahash_alg(tfm)->import_core(req, in);
}
EXPORT_SYMBOL_GPL(crypto_ahash_import_core);

int crypto_ahash_import(struct ahash_request *req, const void *in)
{
 struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);

 if (likely(tfm->using_shash))
  return crypto_shash_import(prepare_shash_desc(req, tfm), in);
 if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
  return -ENOKEY;
 if (crypto_ahash_block_only(tfm)) {
  unsigned int reqsize = crypto_ahash_reqsize(tfm);
  u8 *buf = ahash_request_ctx(req);

  buf[reqsize - 1] = 0;
 }
 return crypto_ahash_alg(tfm)->import(req, in);
}
EXPORT_SYMBOL_GPL(crypto_ahash_import);

static void crypto_ahash_exit_tfm(struct crypto_tfm *tfm)
{
 struct crypto_ahash *hash = __crypto_ahash_cast(tfm);
 struct ahash_alg *alg = crypto_ahash_alg(hash);

 if (alg->exit_tfm)
  alg->exit_tfm(hash);
 else if (tfm->__crt_alg->cra_exit)
  tfm->__crt_alg->cra_exit(tfm);

 if (crypto_ahash_need_fallback(hash))
  crypto_free_ahash(crypto_ahash_fb(hash));
}

static int crypto_ahash_init_tfm(struct crypto_tfm *tfm)
{
 struct crypto_ahash *hash = __crypto_ahash_cast(tfm);
 struct ahash_alg *alg = crypto_ahash_alg(hash);
 struct crypto_ahash *fb = NULL;
 int err;

 crypto_ahash_set_statesize(hash, alg->halg.statesize);
 crypto_ahash_set_reqsize(hash, crypto_tfm_alg_reqsize(tfm));

 if (tfm->__crt_alg->cra_type == &crypto_shash_type)
  return crypto_init_ahash_using_shash(tfm);

 if (crypto_ahash_need_fallback(hash)) {
  fb = crypto_alloc_ahash(crypto_ahash_alg_name(hash),
     CRYPTO_ALG_REQ_VIRT,
     CRYPTO_ALG_ASYNC |
     CRYPTO_ALG_REQ_VIRT |
     CRYPTO_AHASH_ALG_NO_EXPORT_CORE);
  if (IS_ERR(fb))
   return PTR_ERR(fb);

  tfm->fb = crypto_ahash_tfm(fb);
 }

 ahash_set_needkey(hash, alg);

 tfm->exit = crypto_ahash_exit_tfm;

 if (alg->init_tfm)
  err = alg->init_tfm(hash);
 else if (tfm->__crt_alg->cra_init)
  err = tfm->__crt_alg->cra_init(tfm);
 else
  return 0;

 if (err)
  goto out_free_sync_hash;

 if (!ahash_is_async(hash) && crypto_ahash_reqsize(hash) >
         MAX_SYNC_HASH_REQSIZE)
  goto out_exit_tfm;

 BUILD_BUG_ON(HASH_MAX_DESCSIZE > MAX_SYNC_HASH_REQSIZE);
 if (crypto_ahash_reqsize(hash) < HASH_MAX_DESCSIZE)
  crypto_ahash_set_reqsize(hash, HASH_MAX_DESCSIZE);

 return 0;

out_exit_tfm:
 if (alg->exit_tfm)
  alg->exit_tfm(hash);
 else if (tfm->__crt_alg->cra_exit)
  tfm->__crt_alg->cra_exit(tfm);
 err = -EINVAL;
out_free_sync_hash:
 crypto_free_ahash(fb);
 return err;
}

static unsigned int crypto_ahash_extsize(struct crypto_alg *alg)
{
 if (alg->cra_type == &crypto_shash_type)
  return sizeof(struct crypto_shash *);

 return crypto_alg_extsize(alg);
}

static void crypto_ahash_free_instance(struct crypto_instance *inst)
{
 struct ahash_instance *ahash = ahash_instance(inst);

 ahash->free(ahash);
}

static int __maybe_unused crypto_ahash_report(
 struct sk_buff *skb, struct crypto_alg *alg)
{
 struct crypto_report_hash rhash;

 memset(&rhash, 0, sizeof(rhash));

 strscpy(rhash.type, "ahash"sizeof(rhash.type));

 rhash.blocksize = alg->cra_blocksize;
 rhash.digestsize = __crypto_hash_alg_common(alg)->digestsize;

 return nla_put(skb, CRYPTOCFGA_REPORT_HASH, sizeof(rhash), &rhash);
}

static void crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg)
 __maybe_unused;
static void crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg)
{
 seq_printf(m, "type : ahash\n");
 seq_printf(m, "async : %s\n",
     str_yes_no(alg->cra_flags & CRYPTO_ALG_ASYNC));
 seq_printf(m, "blocksize : %u\n", alg->cra_blocksize);
 seq_printf(m, "digestsize : %u\n",
     __crypto_hash_alg_common(alg)->digestsize);
}

static const struct crypto_type crypto_ahash_type = {
 .extsize = crypto_ahash_extsize,
 .init_tfm = crypto_ahash_init_tfm,
 .free = crypto_ahash_free_instance,
#ifdef CONFIG_PROC_FS
 .show = crypto_ahash_show,
#endif
#if IS_ENABLED(CONFIG_CRYPTO_USER)
 .report = crypto_ahash_report,
#endif
 .maskclear = ~CRYPTO_ALG_TYPE_MASK,
 .maskset = CRYPTO_ALG_TYPE_AHASH_MASK,
 .type = CRYPTO_ALG_TYPE_AHASH,
 .tfmsize = offsetof(struct crypto_ahash, base),
 .algsize = offsetof(struct ahash_alg, halg.base),
};

int crypto_grab_ahash(struct crypto_ahash_spawn *spawn,
        struct crypto_instance *inst,
        const char *name, u32 type, u32 mask)
{
 spawn->base.frontend = &crypto_ahash_type;
 return crypto_grab_spawn(&spawn->base, inst, name, type, mask);
}
EXPORT_SYMBOL_GPL(crypto_grab_ahash);

struct crypto_ahash *crypto_alloc_ahash(const char *alg_name, u32 type,
     u32 mask)
{
 return crypto_alloc_tfm(alg_name, &crypto_ahash_type, type, mask);
}
EXPORT_SYMBOL_GPL(crypto_alloc_ahash);

int crypto_has_ahash(const char *alg_name, u32 type, u32 mask)
{
 return crypto_type_has_alg(alg_name, &crypto_ahash_type, type, mask);
}
EXPORT_SYMBOL_GPL(crypto_has_ahash);

bool crypto_hash_alg_has_setkey(struct hash_alg_common *halg)
{
 struct crypto_alg *alg = &halg->base;

 if (alg->cra_type == &crypto_shash_type)
  return crypto_shash_alg_has_setkey(__crypto_shash_alg(alg));

 return __crypto_ahash_alg(alg)->setkey != ahash_nosetkey;
}
EXPORT_SYMBOL_GPL(crypto_hash_alg_has_setkey);

struct crypto_ahash *crypto_clone_ahash(struct crypto_ahash *hash)
{
 struct hash_alg_common *halg = crypto_hash_alg_common(hash);
 struct crypto_tfm *tfm = crypto_ahash_tfm(hash);
 struct crypto_ahash *fb = NULL;
 struct crypto_ahash *nhash;
 struct ahash_alg *alg;
 int err;

 if (!crypto_hash_alg_has_setkey(halg)) {
  tfm = crypto_tfm_get(tfm);
  if (IS_ERR(tfm))
   return ERR_CAST(tfm);

  return hash;
 }

 nhash = crypto_clone_tfm(&crypto_ahash_type, tfm);

 if (IS_ERR(nhash))
  return nhash;

 nhash->reqsize = hash->reqsize;
 nhash->statesize = hash->statesize;

 if (likely(hash->using_shash)) {
  struct crypto_shash **nctx = crypto_ahash_ctx(nhash);
  struct crypto_shash *shash;

  shash = crypto_clone_shash(ahash_to_shash(hash));
  if (IS_ERR(shash)) {
   err = PTR_ERR(shash);
   goto out_free_nhash;
  }
  crypto_ahash_tfm(nhash)->exit = crypto_exit_ahash_using_shash;
  nhash->using_shash = true;
  *nctx = shash;
  return nhash;
 }

 if (crypto_ahash_need_fallback(hash)) {
  fb = crypto_clone_ahash(crypto_ahash_fb(hash));
  err = PTR_ERR(fb);
  if (IS_ERR(fb))
   goto out_free_nhash;

  crypto_ahash_tfm(nhash)->fb = crypto_ahash_tfm(fb);
 }

 err = -ENOSYS;
 alg = crypto_ahash_alg(hash);
 if (!alg->clone_tfm)
  goto out_free_fb;

 err = alg->clone_tfm(nhash, hash);
 if (err)
  goto out_free_fb;

 crypto_ahash_tfm(nhash)->exit = crypto_ahash_exit_tfm;

 return nhash;

out_free_fb:
 crypto_free_ahash(fb);
out_free_nhash:
 crypto_free_ahash(nhash);
 return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(crypto_clone_ahash);

static int ahash_default_export_core(struct ahash_request *req, void *out)
{
 return -ENOSYS;
}

static int ahash_default_import_core(struct ahash_request *req, const void *in)
{
 return -ENOSYS;
}

static int ahash_prepare_alg(struct ahash_alg *alg)
{
 struct crypto_alg *base = &alg->halg.base;
 int err;

 if (alg->halg.statesize == 0)
  return -EINVAL;

 if (base->cra_reqsize && base->cra_reqsize < alg->halg.statesize)
  return -EINVAL;

 if (!(base->cra_flags & CRYPTO_ALG_ASYNC) &&
     base->cra_reqsize > MAX_SYNC_HASH_REQSIZE)
  return -EINVAL;

 if (base->cra_flags & CRYPTO_ALG_NEED_FALLBACK &&
     base->cra_flags & CRYPTO_ALG_NO_FALLBACK)
  return -EINVAL;

 err = hash_prepare_alg(&alg->halg);
 if (err)
  return err;

 base->cra_type = &crypto_ahash_type;
 base->cra_flags |= CRYPTO_ALG_TYPE_AHASH;

 if ((base->cra_flags ^ CRYPTO_ALG_REQ_VIRT) &
     (CRYPTO_ALG_ASYNC | CRYPTO_ALG_REQ_VIRT) &&
     !(base->cra_flags & CRYPTO_ALG_NO_FALLBACK))
  base->cra_flags |= CRYPTO_ALG_NEED_FALLBACK;

 if (!alg->setkey)
  alg->setkey = ahash_nosetkey;

 if (base->cra_flags & CRYPTO_AHASH_ALG_BLOCK_ONLY) {
  BUILD_BUG_ON(MAX_ALGAPI_BLOCKSIZE >= 256);
  if (!alg->finup)
   return -EINVAL;

  base->cra_reqsize += base->cra_blocksize + 1;
  alg->halg.statesize += base->cra_blocksize + 1;
  alg->export_core = alg->export;
  alg->import_core = alg->import;
 } else if (!alg->export_core || !alg->import_core) {
  alg->export_core = ahash_default_export_core;
  alg->import_core = ahash_default_import_core;
  base->cra_flags |= CRYPTO_AHASH_ALG_NO_EXPORT_CORE;
 }

 return 0;
}

int crypto_register_ahash(struct ahash_alg *alg)
{
 struct crypto_alg *base = &alg->halg.base;
 int err;

 err = ahash_prepare_alg(alg);
 if (err)
  return err;

 return crypto_register_alg(base);
}
EXPORT_SYMBOL_GPL(crypto_register_ahash);

void crypto_unregister_ahash(struct ahash_alg *alg)
{
 crypto_unregister_alg(&alg->halg.base);
}
EXPORT_SYMBOL_GPL(crypto_unregister_ahash);

int crypto_register_ahashes(struct ahash_alg *algs, int count)
{
 int i, ret;

 for (i = 0; i < count; i++) {
  ret = crypto_register_ahash(&algs[i]);
  if (ret)
   goto err;
 }

 return 0;

err:
 for (--i; i >= 0; --i)
  crypto_unregister_ahash(&algs[i]);

 return ret;
}
EXPORT_SYMBOL_GPL(crypto_register_ahashes);

void crypto_unregister_ahashes(struct ahash_alg *algs, int count)
{
 int i;

 for (i = count - 1; i >= 0; --i)
  crypto_unregister_ahash(&algs[i]);
}
EXPORT_SYMBOL_GPL(crypto_unregister_ahashes);

int ahash_register_instance(struct crypto_template *tmpl,
       struct ahash_instance *inst)
{
 int err;

 if (WARN_ON(!inst->free))
  return -EINVAL;

 err = ahash_prepare_alg(&inst->alg);
 if (err)
  return err;

 return crypto_register_instance(tmpl, ahash_crypto_instance(inst));
}
EXPORT_SYMBOL_GPL(ahash_register_instance);

void ahash_request_free(struct ahash_request *req)
{
 if (unlikely(!req))
  return;

 if (!ahash_req_on_stack(req)) {
  kfree(req);
  return;
 }

 ahash_request_zero(req);
}
EXPORT_SYMBOL_GPL(ahash_request_free);

int crypto_hash_digest(struct crypto_ahash *tfm, const u8 *data,
         unsigned int len, u8 *out)
{
 HASH_REQUEST_ON_STACK(req, crypto_ahash_fb(tfm));
 int err;

 ahash_request_set_callback(req, 0, NULL, NULL);
 ahash_request_set_virt(req, data, out, len);
 err = crypto_ahash_digest(req);

 ahash_request_zero(req);

 return err;
}
EXPORT_SYMBOL_GPL(crypto_hash_digest);

void ahash_free_singlespawn_instance(struct ahash_instance *inst)
{
 crypto_drop_spawn(ahash_instance_ctx(inst));
 kfree(inst);
}
EXPORT_SYMBOL_GPL(ahash_free_singlespawn_instance);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Asynchronous cryptographic hash type");

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.33 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.