Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 : : /* 3 : : * CBC: Cipher Block Chaining mode 4 : : * 5 : : * Copyright (c) 2016 Herbert Xu <herbert@gondor.apana.org.au> 6 : : */ 7 : : 8 : : #ifndef _CRYPTO_CBC_H 9 : : #define _CRYPTO_CBC_H 10 : : 11 : : #include <crypto/internal/skcipher.h> 12 : : #include <linux/string.h> 13 : : #include <linux/types.h> 14 : : 15 : 0 : static inline int crypto_cbc_encrypt_segment( 16 : : struct skcipher_walk *walk, struct crypto_skcipher *tfm, 17 : : void (*fn)(struct crypto_skcipher *, const u8 *, u8 *)) 18 : : { 19 : : unsigned int bsize = crypto_skcipher_blocksize(tfm); 20 : 0 : unsigned int nbytes = walk->nbytes; 21 : 0 : u8 *src = walk->src.virt.addr; 22 : 0 : u8 *dst = walk->dst.virt.addr; 23 : 0 : u8 *iv = walk->iv; 24 : : 25 : : do { 26 : 0 : crypto_xor(iv, src, bsize); 27 : 0 : fn(tfm, iv, dst); 28 : 0 : memcpy(iv, dst, bsize); 29 : : 30 : 0 : src += bsize; 31 : 0 : dst += bsize; 32 : 0 : } while ((nbytes -= bsize) >= bsize); 33 : : 34 : 0 : return nbytes; 35 : : } 36 : : 37 : 0 : static inline int crypto_cbc_encrypt_inplace( 38 : : struct skcipher_walk *walk, struct crypto_skcipher *tfm, 39 : : void (*fn)(struct crypto_skcipher *, const u8 *, u8 *)) 40 : : { 41 : : unsigned int bsize = crypto_skcipher_blocksize(tfm); 42 : 0 : unsigned int nbytes = walk->nbytes; 43 : 0 : u8 *src = walk->src.virt.addr; 44 : 0 : u8 *iv = walk->iv; 45 : : 46 : : do { 47 : 0 : crypto_xor(src, iv, bsize); 48 : 0 : fn(tfm, src, src); 49 : : iv = src; 50 : : 51 : 0 : src += bsize; 52 : 0 : } while ((nbytes -= bsize) >= bsize); 53 : : 54 : 0 : memcpy(walk->iv, iv, bsize); 55 : : 56 : 0 : return nbytes; 57 : : } 58 : : 59 : 0 : static inline int crypto_cbc_encrypt_walk(struct skcipher_request *req, 60 : : void (*fn)(struct crypto_skcipher *, 61 : : const u8 *, u8 *)) 62 : : { 63 : : struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 64 : : struct skcipher_walk walk; 65 : : int err; 66 : : 67 : 0 : err = skcipher_walk_virt(&walk, req, false); 68 : : 69 : 0 : while (walk.nbytes) { 70 : 0 : if (walk.src.virt.addr == walk.dst.virt.addr) 71 : 0 : err = crypto_cbc_encrypt_inplace(&walk, tfm, fn); 72 : : else 73 : 0 : err = crypto_cbc_encrypt_segment(&walk, tfm, fn); 74 : 0 : err = skcipher_walk_done(&walk, err); 75 : : } 76 : : 77 : 0 : return err; 78 : : } 79 : : 80 : 0 : static inline int crypto_cbc_decrypt_segment( 81 : : struct skcipher_walk *walk, struct crypto_skcipher *tfm, 82 : : void (*fn)(struct crypto_skcipher *, const u8 *, u8 *)) 83 : : { 84 : : unsigned int bsize = crypto_skcipher_blocksize(tfm); 85 : 0 : unsigned int nbytes = walk->nbytes; 86 : 0 : u8 *src = walk->src.virt.addr; 87 : 0 : u8 *dst = walk->dst.virt.addr; 88 : 0 : u8 *iv = walk->iv; 89 : : 90 : : do { 91 : 0 : fn(tfm, src, dst); 92 : 0 : crypto_xor(dst, iv, bsize); 93 : : iv = src; 94 : : 95 : 0 : src += bsize; 96 : 0 : dst += bsize; 97 : 0 : } while ((nbytes -= bsize) >= bsize); 98 : : 99 : 0 : memcpy(walk->iv, iv, bsize); 100 : : 101 : 0 : return nbytes; 102 : : } 103 : : 104 : 0 : static inline int crypto_cbc_decrypt_inplace( 105 : : struct skcipher_walk *walk, struct crypto_skcipher *tfm, 106 : : void (*fn)(struct crypto_skcipher *, const u8 *, u8 *)) 107 : : { 108 : : unsigned int bsize = crypto_skcipher_blocksize(tfm); 109 : 0 : unsigned int nbytes = walk->nbytes; 110 : 0 : u8 *src = walk->src.virt.addr; 111 : : u8 last_iv[MAX_CIPHER_BLOCKSIZE]; 112 : : 113 : : /* Start of the last block. */ 114 : 0 : src += nbytes - (nbytes & (bsize - 1)) - bsize; 115 : 0 : memcpy(last_iv, src, bsize); 116 : : 117 : : for (;;) { 118 : 0 : fn(tfm, src, src); 119 : 0 : if ((nbytes -= bsize) < bsize) 120 : : break; 121 : 0 : crypto_xor(src, src - bsize, bsize); 122 : : src -= bsize; 123 : 0 : } 124 : : 125 : 0 : crypto_xor(src, walk->iv, bsize); 126 : 0 : memcpy(walk->iv, last_iv, bsize); 127 : : 128 : 0 : return nbytes; 129 : : } 130 : : 131 : 0 : static inline int crypto_cbc_decrypt_blocks( 132 : : struct skcipher_walk *walk, struct crypto_skcipher *tfm, 133 : : void (*fn)(struct crypto_skcipher *, const u8 *, u8 *)) 134 : : { 135 : 0 : if (walk->src.virt.addr == walk->dst.virt.addr) 136 : 0 : return crypto_cbc_decrypt_inplace(walk, tfm, fn); 137 : : else 138 : 0 : return crypto_cbc_decrypt_segment(walk, tfm, fn); 139 : : } 140 : : 141 : : #endif /* _CRYPTO_CBC_H */