1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright 2019 Google LLC
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun #include <crypto/sha.h>
6*4882a593Smuzhiyun #include <crypto/hash.h>
7*4882a593Smuzhiyun #include <linux/err.h>
8*4882a593Smuzhiyun #include <linux/version.h>
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include "integrity.h"
11*4882a593Smuzhiyun
incfs_get_hash_alg(enum incfs_hash_tree_algorithm id)12*4882a593Smuzhiyun struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id)
13*4882a593Smuzhiyun {
14*4882a593Smuzhiyun static struct incfs_hash_alg sha256 = {
15*4882a593Smuzhiyun .name = "sha256",
16*4882a593Smuzhiyun .digest_size = SHA256_DIGEST_SIZE,
17*4882a593Smuzhiyun .id = INCFS_HASH_TREE_SHA256
18*4882a593Smuzhiyun };
19*4882a593Smuzhiyun struct incfs_hash_alg *result = NULL;
20*4882a593Smuzhiyun struct crypto_shash *shash;
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun if (id == INCFS_HASH_TREE_SHA256) {
23*4882a593Smuzhiyun BUILD_BUG_ON(INCFS_MAX_HASH_SIZE < SHA256_DIGEST_SIZE);
24*4882a593Smuzhiyun result = &sha256;
25*4882a593Smuzhiyun }
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun if (result == NULL)
28*4882a593Smuzhiyun return ERR_PTR(-ENOENT);
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun /* pairs with cmpxchg_release() below */
31*4882a593Smuzhiyun shash = smp_load_acquire(&result->shash);
32*4882a593Smuzhiyun if (shash)
33*4882a593Smuzhiyun return result;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun shash = crypto_alloc_shash(result->name, 0, 0);
36*4882a593Smuzhiyun if (IS_ERR(shash)) {
37*4882a593Smuzhiyun int err = PTR_ERR(shash);
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun pr_err("Can't allocate hash alg %s, error code:%d",
40*4882a593Smuzhiyun result->name, err);
41*4882a593Smuzhiyun return ERR_PTR(err);
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun /* pairs with smp_load_acquire() above */
45*4882a593Smuzhiyun if (cmpxchg_release(&result->shash, NULL, shash) != NULL)
46*4882a593Smuzhiyun crypto_free_shash(shash);
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun return result;
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun struct signature_info {
52*4882a593Smuzhiyun u32 version;
53*4882a593Smuzhiyun enum incfs_hash_tree_algorithm hash_algorithm;
54*4882a593Smuzhiyun u8 log2_blocksize;
55*4882a593Smuzhiyun struct mem_range salt;
56*4882a593Smuzhiyun struct mem_range root_hash;
57*4882a593Smuzhiyun };
58*4882a593Smuzhiyun
read_u32(u8 ** p,u8 * top,u32 * result)59*4882a593Smuzhiyun static bool read_u32(u8 **p, u8 *top, u32 *result)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun if (*p + sizeof(u32) > top)
62*4882a593Smuzhiyun return false;
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun *result = le32_to_cpu(*(__le32 *)*p);
65*4882a593Smuzhiyun *p += sizeof(u32);
66*4882a593Smuzhiyun return true;
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun
read_u8(u8 ** p,u8 * top,u8 * result)69*4882a593Smuzhiyun static bool read_u8(u8 **p, u8 *top, u8 *result)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun if (*p + sizeof(u8) > top)
72*4882a593Smuzhiyun return false;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun *result = *(u8 *)*p;
75*4882a593Smuzhiyun *p += sizeof(u8);
76*4882a593Smuzhiyun return true;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
read_mem_range(u8 ** p,u8 * top,struct mem_range * range)79*4882a593Smuzhiyun static bool read_mem_range(u8 **p, u8 *top, struct mem_range *range)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun u32 len;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun if (!read_u32(p, top, &len) || *p + len > top)
84*4882a593Smuzhiyun return false;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun range->len = len;
87*4882a593Smuzhiyun range->data = *p;
88*4882a593Smuzhiyun *p += len;
89*4882a593Smuzhiyun return true;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun
incfs_parse_signature(struct mem_range signature,struct signature_info * si)92*4882a593Smuzhiyun static int incfs_parse_signature(struct mem_range signature,
93*4882a593Smuzhiyun struct signature_info *si)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun u8 *p = signature.data;
96*4882a593Smuzhiyun u8 *top = signature.data + signature.len;
97*4882a593Smuzhiyun u32 hash_section_size;
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun if (signature.len > INCFS_MAX_SIGNATURE_SIZE)
100*4882a593Smuzhiyun return -EINVAL;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun if (!read_u32(&p, top, &si->version) ||
103*4882a593Smuzhiyun si->version != INCFS_SIGNATURE_VERSION)
104*4882a593Smuzhiyun return -EINVAL;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun if (!read_u32(&p, top, &hash_section_size) ||
107*4882a593Smuzhiyun p + hash_section_size > top)
108*4882a593Smuzhiyun return -EINVAL;
109*4882a593Smuzhiyun top = p + hash_section_size;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun if (!read_u32(&p, top, &si->hash_algorithm) ||
112*4882a593Smuzhiyun si->hash_algorithm != INCFS_HASH_TREE_SHA256)
113*4882a593Smuzhiyun return -EINVAL;
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun if (!read_u8(&p, top, &si->log2_blocksize) || si->log2_blocksize != 12)
116*4882a593Smuzhiyun return -EINVAL;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun if (!read_mem_range(&p, top, &si->salt))
119*4882a593Smuzhiyun return -EINVAL;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun if (!read_mem_range(&p, top, &si->root_hash))
122*4882a593Smuzhiyun return -EINVAL;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun if (p != top)
125*4882a593Smuzhiyun return -EINVAL;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun return 0;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
incfs_alloc_mtree(struct mem_range signature,int data_block_count)130*4882a593Smuzhiyun struct mtree *incfs_alloc_mtree(struct mem_range signature,
131*4882a593Smuzhiyun int data_block_count)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun int error;
134*4882a593Smuzhiyun struct signature_info si;
135*4882a593Smuzhiyun struct mtree *result = NULL;
136*4882a593Smuzhiyun struct incfs_hash_alg *hash_alg = NULL;
137*4882a593Smuzhiyun int hash_per_block;
138*4882a593Smuzhiyun int lvl;
139*4882a593Smuzhiyun int total_blocks = 0;
140*4882a593Smuzhiyun int blocks_in_level[INCFS_MAX_MTREE_LEVELS];
141*4882a593Smuzhiyun int blocks = data_block_count;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun if (data_block_count <= 0)
144*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun error = incfs_parse_signature(signature, &si);
147*4882a593Smuzhiyun if (error)
148*4882a593Smuzhiyun return ERR_PTR(error);
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun hash_alg = incfs_get_hash_alg(si.hash_algorithm);
151*4882a593Smuzhiyun if (IS_ERR(hash_alg))
152*4882a593Smuzhiyun return ERR_PTR(PTR_ERR(hash_alg));
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun if (si.root_hash.len < hash_alg->digest_size)
155*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun result = kzalloc(sizeof(*result), GFP_NOFS);
158*4882a593Smuzhiyun if (!result)
159*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun result->alg = hash_alg;
162*4882a593Smuzhiyun hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / result->alg->digest_size;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun /* Calculating tree geometry. */
165*4882a593Smuzhiyun /* First pass: calculate how many blocks in each tree level. */
166*4882a593Smuzhiyun for (lvl = 0; blocks > 1; lvl++) {
167*4882a593Smuzhiyun if (lvl >= INCFS_MAX_MTREE_LEVELS) {
168*4882a593Smuzhiyun pr_err("incfs: too much data in mtree");
169*4882a593Smuzhiyun goto err;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun blocks = (blocks + hash_per_block - 1) / hash_per_block;
173*4882a593Smuzhiyun blocks_in_level[lvl] = blocks;
174*4882a593Smuzhiyun total_blocks += blocks;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun result->depth = lvl;
177*4882a593Smuzhiyun result->hash_tree_area_size = total_blocks * INCFS_DATA_FILE_BLOCK_SIZE;
178*4882a593Smuzhiyun if (result->hash_tree_area_size > INCFS_MAX_HASH_AREA_SIZE)
179*4882a593Smuzhiyun goto err;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun blocks = 0;
182*4882a593Smuzhiyun /* Second pass: calculate offset of each level. 0th level goes last. */
183*4882a593Smuzhiyun for (lvl = 0; lvl < result->depth; lvl++) {
184*4882a593Smuzhiyun u32 suboffset;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun blocks += blocks_in_level[lvl];
187*4882a593Smuzhiyun suboffset = (total_blocks - blocks)
188*4882a593Smuzhiyun * INCFS_DATA_FILE_BLOCK_SIZE;
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun result->hash_level_suboffset[lvl] = suboffset;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun /* Root hash is stored separately from the rest of the tree. */
194*4882a593Smuzhiyun memcpy(result->root_hash, si.root_hash.data, hash_alg->digest_size);
195*4882a593Smuzhiyun return result;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun err:
198*4882a593Smuzhiyun kfree(result);
199*4882a593Smuzhiyun return ERR_PTR(-E2BIG);
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
incfs_free_mtree(struct mtree * tree)202*4882a593Smuzhiyun void incfs_free_mtree(struct mtree *tree)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun kfree(tree);
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
incfs_calc_digest(struct incfs_hash_alg * alg,struct mem_range data,struct mem_range digest)207*4882a593Smuzhiyun int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data,
208*4882a593Smuzhiyun struct mem_range digest)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun SHASH_DESC_ON_STACK(desc, alg->shash);
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun if (!alg || !alg->shash || !data.data || !digest.data)
213*4882a593Smuzhiyun return -EFAULT;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun if (alg->digest_size > digest.len)
216*4882a593Smuzhiyun return -EINVAL;
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun desc->tfm = alg->shash;
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun if (data.len < INCFS_DATA_FILE_BLOCK_SIZE) {
221*4882a593Smuzhiyun int err;
222*4882a593Smuzhiyun void *buf = kzalloc(INCFS_DATA_FILE_BLOCK_SIZE, GFP_NOFS);
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun if (!buf)
225*4882a593Smuzhiyun return -ENOMEM;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun memcpy(buf, data.data, data.len);
228*4882a593Smuzhiyun err = crypto_shash_digest(desc, buf, INCFS_DATA_FILE_BLOCK_SIZE,
229*4882a593Smuzhiyun digest.data);
230*4882a593Smuzhiyun kfree(buf);
231*4882a593Smuzhiyun return err;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun return crypto_shash_digest(desc, data.data, data.len, digest.data);
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
236