Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
c3afafa
Documentation
arch
block
certs
crypto
asymmetric_keys
async_tx
.gitignore
842.c
Kconfig
Makefile
ablk_helper.c
ablkcipher.c
aead.c
aes_generic.c
af_alg.c
ahash.c
akcipher.c
algapi.c
algboss.c
algif_aead.c
algif_hash.c
algif_rng.c
algif_skcipher.c
ansi_cprng.c
anubis.c
api.c
arc4.c
authenc.c
authencesn.c
blkcipher.c
blowfish_common.c
blowfish_generic.c
camellia_generic.c
cast5_generic.c
cast6_generic.c
cast_common.c
cbc.c
ccm.c
chacha20_generic.c
chacha20poly1305.c
cipher.c
cmac.c
compress.c
crc32_generic.c
crc32c_generic.c
crct10dif_common.c
crct10dif_generic.c
cryptd.c
crypto_engine.c
crypto_null.c
crypto_user.c
crypto_wq.c
ctr.c
cts.c
deflate.c
des_generic.c
dh.c
dh_helper.c
drbg.c
ecb.c
ecc.c
ecc.h
ecc_curve_defs.h
ecdh.c
ecdh_helper.c
echainiv.c
fcrypt.c
fips.c
gcm.c
gf128mul.c
ghash-generic.c
hash_info.c
hmac.c
internal.h
jitterentropy-kcapi.c
jitterentropy.c
keywrap.c
khazad.c
kpp.c
lrw.c
lz4.c
lz4hc.c
lzo.c
mcryptd.c
md4.c
md5.c
memneq.c
michael_mic.c
pcbc.c
pcrypt.c
poly1305_generic.c
proc.c
ripemd.h
rmd128.c
rmd160.c
rmd256.c
rmd320.c
rng.c
rsa-pkcs1pad.c
rsa.c
rsa_helper.c
rsaprivkey.asn1
rsapubkey.asn1
salsa20_generic.c
scatterwalk.c
seed.c
seqiv.c
serpent_generic.c
sha1_generic.c
sha256_generic.c
sha3_generic.c
sha512_generic.c
shash.c
skcipher.c
tcrypt.c
tcrypt.h
tea.c
testmgr.c
testmgr.h
tgr192.c
twofish_common.c
twofish_generic.c
vmac.c
wp512.c
xcbc.c
xor.c
xts.c
drivers
firmware
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.cocciconfig
.get_maintainer.ignore
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
crypto
/
blkcipher.c
Blame
Blame
Latest commit
History
History
560 lines (462 loc) · 15 KB
Breadcrumbs
linux
/
crypto
/
blkcipher.c
Top
File metadata and controls
Code
Blame
560 lines (462 loc) · 15 KB
Raw
/* * Block chaining cipher operations. * * Generic encrypt/decrypt wrapper for ciphers, handles operations across * multiple page boundaries by using temporary blocks. In user context, * the kernel is given a chance to schedule us once per page. * * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * */ #include <crypto/aead.h> #include <crypto/internal/skcipher.h> #include <crypto/scatterwalk.h> #include <linux/errno.h> #include <linux/hardirq.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/cryptouser.h> #include <net/netlink.h> #include "internal.h" enum { BLKCIPHER_WALK_PHYS = 1 << 0, BLKCIPHER_WALK_SLOW = 1 << 1, BLKCIPHER_WALK_COPY = 1 << 2, BLKCIPHER_WALK_DIFF = 1 << 3, }; static int blkcipher_walk_next(struct blkcipher_desc *desc, struct blkcipher_walk *walk); static int blkcipher_walk_first(struct blkcipher_desc *desc, struct blkcipher_walk *walk); static inline void blkcipher_map_src(struct blkcipher_walk *walk) { walk->src.virt.addr = scatterwalk_map(&walk->in); } static inline void blkcipher_map_dst(struct blkcipher_walk *walk) { walk->dst.virt.addr = scatterwalk_map(&walk->out); } static inline void blkcipher_unmap_src(struct blkcipher_walk *walk) { scatterwalk_unmap(walk->src.virt.addr); } static inline void blkcipher_unmap_dst(struct blkcipher_walk *walk) { scatterwalk_unmap(walk->dst.virt.addr); } /* Get a spot of the specified length that does not straddle a page. * The caller needs to ensure that there is enough space for this operation. */ static inline u8 *blkcipher_get_spot(u8 *start, unsigned int len) { u8 *end_page = (u8 *)(((unsigned long)(start + len - 1)) & PAGE_MASK); return max(start, end_page); } static inline unsigned int blkcipher_done_slow(struct blkcipher_walk *walk, unsigned int bsize) { u8 *addr; addr = (u8 *)ALIGN((unsigned long)walk->buffer, walk->alignmask + 1); addr = blkcipher_get_spot(addr, bsize); scatterwalk_copychunks(addr, &walk->out, bsize, 1); return bsize; } static inline unsigned int blkcipher_done_fast(struct blkcipher_walk *walk, unsigned int n) { if (walk->flags & BLKCIPHER_WALK_COPY) { blkcipher_map_dst(walk); memcpy(walk->dst.virt.addr, walk->page, n); blkcipher_unmap_dst(walk); } else if (!(walk->flags & BLKCIPHER_WALK_PHYS)) { if (walk->flags & BLKCIPHER_WALK_DIFF) blkcipher_unmap_dst(walk); blkcipher_unmap_src(walk); } scatterwalk_advance(&walk->in, n); scatterwalk_advance(&walk->out, n); return n; } int blkcipher_walk_done(struct blkcipher_desc *desc, struct blkcipher_walk *walk, int err) { unsigned int nbytes = 0; if (likely(err >= 0)) { unsigned int n = walk->nbytes - err; if (likely(!(walk->flags & BLKCIPHER_WALK_SLOW))) n = blkcipher_done_fast(walk, n); else if (WARN_ON(err)) { err = -EINVAL; goto err; } else n = blkcipher_done_slow(walk, n); nbytes = walk->total - n; err = 0; } scatterwalk_done(&walk->in, 0, nbytes); scatterwalk_done(&walk->out, 1, nbytes); err: walk->total = nbytes; walk->nbytes = nbytes; if (nbytes) { crypto_yield(desc->flags); return blkcipher_walk_next(desc, walk); } if (walk->iv != desc->info) memcpy(desc->info, walk->iv, walk->ivsize); if (walk->buffer != walk->page) kfree(walk->buffer); if (walk->page) free_page((unsigned long)walk->page); return err; } EXPORT_SYMBOL_GPL(blkcipher_walk_done); static inline int blkcipher_next_slow(struct blkcipher_desc *desc, struct blkcipher_walk *walk, unsigned int bsize, unsigned int alignmask) { unsigned int n; unsigned aligned_bsize = ALIGN(bsize, alignmask + 1); if (walk->buffer) goto ok; walk->buffer = walk->page; if (walk->buffer) goto ok; n = aligned_bsize * 3 - (alignmask + 1) + (alignmask & ~(crypto_tfm_ctx_alignment() - 1)); walk->buffer = kmalloc(n, GFP_ATOMIC); if (!walk->buffer) return blkcipher_walk_done(desc, walk, -ENOMEM); ok: walk->dst.virt.addr = (u8 *)ALIGN((unsigned long)walk->buffer, alignmask + 1); walk->dst.virt.addr = blkcipher_get_spot(walk->dst.virt.addr, bsize); walk->src.virt.addr = blkcipher_get_spot(walk->dst.virt.addr + aligned_bsize, bsize); scatterwalk_copychunks(walk->src.virt.addr, &walk->in, bsize, 0); walk->nbytes = bsize; walk->flags |= BLKCIPHER_WALK_SLOW; return 0; } static inline int blkcipher_next_copy(struct blkcipher_walk *walk) { u8 *tmp = walk->page; blkcipher_map_src(walk); memcpy(tmp, walk->src.virt.addr, walk->nbytes); blkcipher_unmap_src(walk); walk->src.virt.addr = tmp; walk->dst.virt.addr = tmp; return 0; } static inline int blkcipher_next_fast(struct blkcipher_desc *desc, struct blkcipher_walk *walk) { unsigned long diff; walk->src.phys.page = scatterwalk_page(&walk->in); walk->src.phys.offset = offset_in_page(walk->in.offset); walk->dst.phys.page = scatterwalk_page(&walk->out); walk->dst.phys.offset = offset_in_page(walk->out.offset); if (walk->flags & BLKCIPHER_WALK_PHYS) return 0; diff = walk->src.phys.offset - walk->dst.phys.offset; diff |= walk->src.virt.page - walk->dst.virt.page; blkcipher_map_src(walk); walk->dst.virt.addr = walk->src.virt.addr; if (diff) { walk->flags |= BLKCIPHER_WALK_DIFF; blkcipher_map_dst(walk); } return 0; } static int blkcipher_walk_next(struct blkcipher_desc *desc, struct blkcipher_walk *walk) { unsigned int bsize; unsigned int n; int err; n = walk->total; if (unlikely(n < walk->cipher_blocksize)) { desc->flags |= CRYPTO_TFM_RES_BAD_BLOCK_LEN; return blkcipher_walk_done(desc, walk, -EINVAL); } bsize = min(walk->walk_blocksize, n); walk->flags &= ~(BLKCIPHER_WALK_SLOW | BLKCIPHER_WALK_COPY | BLKCIPHER_WALK_DIFF); if (!scatterwalk_aligned(&walk->in, walk->alignmask) || !scatterwalk_aligned(&walk->out, walk->alignmask)) { walk->flags |= BLKCIPHER_WALK_COPY; if (!walk->page) { walk->page = (void *)__get_free_page(GFP_ATOMIC); if (!walk->page) n = 0; } } n = scatterwalk_clamp(&walk->in, n); n = scatterwalk_clamp(&walk->out, n); if (unlikely(n < bsize)) { err = blkcipher_next_slow(desc, walk, bsize, walk->alignmask); goto set_phys_lowmem; } walk->nbytes = n; if (walk->flags & BLKCIPHER_WALK_COPY) { err = blkcipher_next_copy(walk); goto set_phys_lowmem; } return blkcipher_next_fast(desc, walk); set_phys_lowmem: if (walk->flags & BLKCIPHER_WALK_PHYS) { walk->src.phys.page = virt_to_page(walk->src.virt.addr); walk->dst.phys.page = virt_to_page(walk->dst.virt.addr); walk->src.phys.offset &= PAGE_SIZE - 1; walk->dst.phys.offset &= PAGE_SIZE - 1; } return err; } static inline int blkcipher_copy_iv(struct blkcipher_walk *walk) { unsigned bs = walk->walk_blocksize; unsigned aligned_bs = ALIGN(bs, walk->alignmask + 1); unsigned int size = aligned_bs * 2 + walk->ivsize + max(aligned_bs, walk->ivsize) - (walk->alignmask + 1); u8 *iv; size += walk->alignmask & ~(crypto_tfm_ctx_alignment() - 1); walk->buffer = kmalloc(size, GFP_ATOMIC); if (!walk->buffer) return -ENOMEM; iv = (u8 *)ALIGN((unsigned long)walk->buffer, walk->alignmask + 1); iv = blkcipher_get_spot(iv, bs) + aligned_bs; iv = blkcipher_get_spot(iv, bs) + aligned_bs; iv = blkcipher_get_spot(iv, walk->ivsize); walk->iv = memcpy(iv, walk->iv, walk->ivsize); return 0; } int blkcipher_walk_virt(struct blkcipher_desc *desc, struct blkcipher_walk *walk) { walk->flags &= ~BLKCIPHER_WALK_PHYS; walk->walk_blocksize = crypto_blkcipher_blocksize(desc->tfm); walk->cipher_blocksize = walk->walk_blocksize; walk->ivsize = crypto_blkcipher_ivsize(desc->tfm); walk->alignmask = crypto_blkcipher_alignmask(desc->tfm); return blkcipher_walk_first(desc, walk); } EXPORT_SYMBOL_GPL(blkcipher_walk_virt); int blkcipher_walk_phys(struct blkcipher_desc *desc, struct blkcipher_walk *walk) { walk->flags |= BLKCIPHER_WALK_PHYS; walk->walk_blocksize = crypto_blkcipher_blocksize(desc->tfm); walk->cipher_blocksize = walk->walk_blocksize; walk->ivsize = crypto_blkcipher_ivsize(desc->tfm); walk->alignmask = crypto_blkcipher_alignmask(desc->tfm); return blkcipher_walk_first(desc, walk); } EXPORT_SYMBOL_GPL(blkcipher_walk_phys); static int blkcipher_walk_first(struct blkcipher_desc *desc, struct blkcipher_walk *walk) { if (WARN_ON_ONCE(in_irq())) return -EDEADLK; walk->iv = desc->info; walk->nbytes = walk->total; if (unlikely(!walk->total)) return 0; walk->buffer = NULL; if (unlikely(((unsigned long)walk->iv & walk->alignmask))) { int err = blkcipher_copy_iv(walk); if (err) return err; } scatterwalk_start(&walk->in, walk->in.sg); scatterwalk_start(&walk->out, walk->out.sg); walk->page = NULL; return blkcipher_walk_next(desc, walk); } int blkcipher_walk_virt_block(struct blkcipher_desc *desc, struct blkcipher_walk *walk, unsigned int blocksize) { walk->flags &= ~BLKCIPHER_WALK_PHYS; walk->walk_blocksize = blocksize; walk->cipher_blocksize = crypto_blkcipher_blocksize(desc->tfm); walk->ivsize = crypto_blkcipher_ivsize(desc->tfm); walk->alignmask = crypto_blkcipher_alignmask(desc->tfm); return blkcipher_walk_first(desc, walk); } EXPORT_SYMBOL_GPL(blkcipher_walk_virt_block); int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc, struct blkcipher_walk *walk, struct crypto_aead *tfm, unsigned int blocksize) { walk->flags &= ~BLKCIPHER_WALK_PHYS; walk->walk_blocksize = blocksize; walk->cipher_blocksize = crypto_aead_blocksize(tfm); walk->ivsize = crypto_aead_ivsize(tfm); walk->alignmask = crypto_aead_alignmask(tfm); return blkcipher_walk_first(desc, walk); } EXPORT_SYMBOL_GPL(blkcipher_aead_walk_virt_block); static int setkey_unaligned(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) { struct blkcipher_alg *cipher = &tfm->__crt_alg->cra_blkcipher; unsigned long alignmask = crypto_tfm_alg_alignmask(tfm); int ret; u8 *buffer, *alignbuffer; unsigned long absize; absize = keylen + alignmask; buffer = kmalloc(absize, GFP_ATOMIC); if (!buffer) return -ENOMEM; alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); memcpy(alignbuffer, key, keylen); ret = cipher->setkey(tfm, alignbuffer, keylen); memset(alignbuffer, 0, keylen); kfree(buffer); return ret; } static int setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) { struct blkcipher_alg *cipher = &tfm->__crt_alg->cra_blkcipher; unsigned long alignmask = crypto_tfm_alg_alignmask(tfm); if (keylen < cipher->min_keysize || keylen > cipher->max_keysize) { tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; return -EINVAL; } if ((unsigned long)key & alignmask) return setkey_unaligned(tfm, key, keylen); return cipher->setkey(tfm, key, keylen); } static int async_setkey(struct crypto_ablkcipher *tfm, const u8 *key, unsigned int keylen) { return setkey(crypto_ablkcipher_tfm(tfm), key, keylen); } static int async_encrypt(struct ablkcipher_request *req) { struct crypto_tfm *tfm = req->base.tfm; struct blkcipher_alg *alg = &tfm->__crt_alg->cra_blkcipher; struct blkcipher_desc desc = { .tfm = __crypto_blkcipher_cast(tfm), .info = req->info, .flags = req->base.flags, }; return alg->encrypt(&desc, req->dst, req->src, req->nbytes); } static int async_decrypt(struct ablkcipher_request *req) { struct crypto_tfm *tfm = req->base.tfm; struct blkcipher_alg *alg = &tfm->__crt_alg->cra_blkcipher; struct blkcipher_desc desc = { .tfm = __crypto_blkcipher_cast(tfm), .info = req->info, .flags = req->base.flags, }; return alg->decrypt(&desc, req->dst, req->src, req->nbytes); } static unsigned int crypto_blkcipher_ctxsize(struct crypto_alg *alg, u32 type, u32 mask) { struct blkcipher_alg *cipher = &alg->cra_blkcipher; unsigned int len = alg->cra_ctxsize; if ((mask & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_MASK && cipher->ivsize) { len = ALIGN(len, (unsigned long)alg->cra_alignmask + 1); len += cipher->ivsize; } return len; } static int crypto_init_blkcipher_ops_async(struct crypto_tfm *tfm) { struct ablkcipher_tfm *crt = &tfm->crt_ablkcipher; struct blkcipher_alg *alg = &tfm->__crt_alg->cra_blkcipher; crt->setkey = async_setkey; crt->encrypt = async_encrypt; crt->decrypt = async_decrypt; crt->base = __crypto_ablkcipher_cast(tfm); crt->ivsize = alg->ivsize; return 0; } static int crypto_init_blkcipher_ops_sync(struct crypto_tfm *tfm) { struct blkcipher_tfm *crt = &tfm->crt_blkcipher; struct blkcipher_alg *alg = &tfm->__crt_alg->cra_blkcipher; unsigned long align = crypto_tfm_alg_alignmask(tfm) + 1; unsigned long addr; crt->setkey = setkey; crt->encrypt = alg->encrypt; crt->decrypt = alg->decrypt; addr = (unsigned long)crypto_tfm_ctx(tfm); addr = ALIGN(addr, align); addr += ALIGN(tfm->__crt_alg->cra_ctxsize, align); crt->iv = (void *)addr; return 0; } static int crypto_init_blkcipher_ops(struct crypto_tfm *tfm, u32 type, u32 mask) { struct blkcipher_alg *alg = &tfm->__crt_alg->cra_blkcipher; if (alg->ivsize > PAGE_SIZE / 8) return -EINVAL; if ((mask & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_MASK) return crypto_init_blkcipher_ops_sync(tfm); else return crypto_init_blkcipher_ops_async(tfm); } #ifdef CONFIG_NET static int crypto_blkcipher_report(struct sk_buff *skb, struct crypto_alg *alg) { struct crypto_report_blkcipher rblkcipher; strncpy(rblkcipher.type, "blkcipher", sizeof(rblkcipher.type)); strncpy(rblkcipher.geniv, alg->cra_blkcipher.geniv ?: "<default>", sizeof(rblkcipher.geniv)); rblkcipher.blocksize = alg->cra_blocksize; rblkcipher.min_keysize = alg->cra_blkcipher.min_keysize; rblkcipher.max_keysize = alg->cra_blkcipher.max_keysize; rblkcipher.ivsize = alg->cra_blkcipher.ivsize; if (nla_put(skb, CRYPTOCFGA_REPORT_BLKCIPHER, sizeof(struct crypto_report_blkcipher), &rblkcipher)) goto nla_put_failure; return 0; nla_put_failure: return -EMSGSIZE; } #else static int crypto_blkcipher_report(struct sk_buff *skb, struct crypto_alg *alg) { return -ENOSYS; } #endif static void crypto_blkcipher_show(struct seq_file *m, struct crypto_alg *alg) __attribute__ ((unused)); static void crypto_blkcipher_show(struct seq_file *m, struct crypto_alg *alg) { seq_printf(m, "type : blkcipher\n"); seq_printf(m, "blocksize : %u\n", alg->cra_blocksize); seq_printf(m, "min keysize : %u\n", alg->cra_blkcipher.min_keysize); seq_printf(m, "max keysize : %u\n", alg->cra_blkcipher.max_keysize); seq_printf(m, "ivsize : %u\n", alg->cra_blkcipher.ivsize); seq_printf(m, "geniv : %s\n", alg->cra_blkcipher.geniv ?: "<default>"); } const struct crypto_type crypto_blkcipher_type = { .ctxsize = crypto_blkcipher_ctxsize, .init = crypto_init_blkcipher_ops, #ifdef CONFIG_PROC_FS .show = crypto_blkcipher_show, #endif .report = crypto_blkcipher_report, }; EXPORT_SYMBOL_GPL(crypto_blkcipher_type); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic block chaining cipher type");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
You can’t perform that action at this time.