Merge branch 'linux-linaro-lsk-v4.4' into linux-linaro-lsk-v4.4-android
[firefly-linux-kernel-4.4.55.git] / arch / arm64 / crypto / poly-hash-ce-glue.c
1 /*
2  * Accelerated poly_hash implementation with ARMv8 PMULL instructions.
3  *
4  * Based on ghash-ce-glue.c.
5  *
6  * poly_hash is part of the HEH (Hash-Encrypt-Hash) encryption mode, proposed in
7  * Internet Draft https://tools.ietf.org/html/draft-cope-heh-01.
8  *
9  * poly_hash is very similar to GHASH: both algorithms are keyed hashes which
10  * interpret their input data as coefficients of a polynomial over GF(2^128),
11  * then calculate a hash value by evaluating that polynomial at the point given
12  * by the key, e.g. using Horner's rule.  The difference is that poly_hash uses
13  * the more natural "ble" convention to represent GF(2^128) elements, whereas
14  * GHASH uses the less natural "lle" convention (see include/crypto/gf128mul.h).
15  * The ble convention makes it simpler to implement GF(2^128) multiplication.
16  *
17  * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
18  * Copyright (C) 2017 Google Inc. <ebiggers@google.com>
19  *
20  * This program is free software; you can redistribute it and/or modify it
21  * under the terms of the GNU General Public License version 2 as published
22  * by the Free Software Foundation.
23  */
24
25 #include <asm/neon.h>
26 #include <crypto/b128ops.h>
27 #include <crypto/internal/hash.h>
28 #include <linux/cpufeature.h>
29 #include <linux/crypto.h>
30 #include <linux/module.h>
31
32 /*
33  * Note: in this algorithm we currently use 'le128' to represent GF(2^128)
34  * elements, even though poly_hash-generic uses 'be128'.  Both types are
35  * actually "wrong" because the elements are actually in 'ble' format, and there
36  * should be a ble type to represent this --- as well as lle, bbe, and lbe types
37  * for the other conventions for representing GF(2^128) elements.  But
38  * practically it doesn't matter which type we choose here, so we just use le128
39  * since it's arguably more accurate, while poly_hash-generic still has to use
40  * be128 because the generic GF(2^128) multiplication functions all take be128.
41  */
42
43 struct poly_hash_desc_ctx {
44         le128 digest;
45         unsigned int count;
46 };
47
48 asmlinkage void pmull_poly_hash_update(le128 *digest, const le128 *key,
49                                        const u8 *src, unsigned int blocks,
50                                        unsigned int partial);
51
52 static int poly_hash_setkey(struct crypto_shash *tfm,
53                             const u8 *key, unsigned int keylen)
54 {
55         if (keylen != sizeof(le128)) {
56                 crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
57                 return -EINVAL;
58         }
59
60         memcpy(crypto_shash_ctx(tfm), key, sizeof(le128));
61         return 0;
62 }
63
64 static int poly_hash_init(struct shash_desc *desc)
65 {
66         struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
67
68         ctx->digest = (le128) { 0 };
69         ctx->count = 0;
70         return 0;
71 }
72
73 static int poly_hash_update(struct shash_desc *desc, const u8 *src,
74                             unsigned int len)
75 {
76         struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
77         unsigned int partial = ctx->count % sizeof(le128);
78         u8 *dst = (u8 *)&ctx->digest + partial;
79
80         ctx->count += len;
81
82         /* Finishing at least one block? */
83         if (partial + len >= sizeof(le128)) {
84                 const le128 *key = crypto_shash_ctx(desc->tfm);
85
86                 if (partial) {
87                         /* Finish the pending block. */
88                         unsigned int n = sizeof(le128) - partial;
89
90                         len -= n;
91                         do {
92                                 *dst++ ^= *src++;
93                         } while (--n);
94                 }
95
96                 /*
97                  * Do the real work.  If 'partial' is nonzero, this starts by
98                  * multiplying 'digest' by 'key'.  Then for each additional full
99                  * block it adds the block to 'digest' and multiplies by 'key'.
100                  */
101                 kernel_neon_begin_partial(8);
102                 pmull_poly_hash_update(&ctx->digest, key, src,
103                                        len / sizeof(le128), partial);
104                 kernel_neon_end();
105
106                 src += len - (len % sizeof(le128));
107                 len %= sizeof(le128);
108                 dst = (u8 *)&ctx->digest;
109         }
110
111         /* Continue adding the next block to 'digest'. */
112         while (len--)
113                 *dst++ ^= *src++;
114         return 0;
115 }
116
117 static int poly_hash_final(struct shash_desc *desc, u8 *out)
118 {
119         struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
120         unsigned int partial = ctx->count % sizeof(le128);
121
122         /* Finish the last block if needed. */
123         if (partial) {
124                 const le128 *key = crypto_shash_ctx(desc->tfm);
125
126                 kernel_neon_begin_partial(8);
127                 pmull_poly_hash_update(&ctx->digest, key, NULL, 0, partial);
128                 kernel_neon_end();
129         }
130
131         memcpy(out, &ctx->digest, sizeof(le128));
132         return 0;
133 }
134
135 static struct shash_alg poly_hash_alg = {
136         .digestsize     = sizeof(le128),
137         .init           = poly_hash_init,
138         .update         = poly_hash_update,
139         .final          = poly_hash_final,
140         .setkey         = poly_hash_setkey,
141         .descsize       = sizeof(struct poly_hash_desc_ctx),
142         .base           = {
143                 .cra_name               = "poly_hash",
144                 .cra_driver_name        = "poly_hash-ce",
145                 .cra_priority           = 300,
146                 .cra_ctxsize            = sizeof(le128),
147                 .cra_module             = THIS_MODULE,
148         },
149 };
150
151 static int __init poly_hash_ce_mod_init(void)
152 {
153         return crypto_register_shash(&poly_hash_alg);
154 }
155
156 static void __exit poly_hash_ce_mod_exit(void)
157 {
158         crypto_unregister_shash(&poly_hash_alg);
159 }
160
161 MODULE_DESCRIPTION("Polynomial evaluation hash using ARMv8 Crypto Extensions");
162 MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
163 MODULE_LICENSE("GPL v2");
164
165 module_cpu_feature_match(PMULL, poly_hash_ce_mod_init);
166 module_exit(poly_hash_ce_mod_exit);