• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2019 Google LLC
4  */
5 #include <crypto/sha.h>
6 #include <crypto/hash.h>
7 #include <linux/err.h>
8 #include <linux/version.h>
9 #include <crypto/pkcs7.h>
10 
11 #include "integrity.h"
12 
incfs_validate_pkcs7_signature(struct mem_range pkcs7_blob,struct mem_range root_hash,struct mem_range add_data)13 int incfs_validate_pkcs7_signature(struct mem_range pkcs7_blob,
14 	struct mem_range root_hash, struct mem_range add_data)
15 {
16 	struct pkcs7_message *pkcs7 = NULL;
17 	const void *data = NULL;
18 	size_t data_len = 0;
19 	const char *p;
20 	int err;
21 
22 	pkcs7 = pkcs7_parse_message(pkcs7_blob.data, pkcs7_blob.len);
23 	if (IS_ERR(pkcs7)) {
24 		pr_debug("PKCS#7 parsing error. ptr=%p size=%ld err=%ld\n",
25 			pkcs7_blob.data, pkcs7_blob.len, -PTR_ERR(pkcs7));
26 		return PTR_ERR(pkcs7);
27 	}
28 
29 	err = pkcs7_get_content_data(pkcs7, &data, &data_len, NULL);
30 	if (err || data_len == 0 || data == NULL) {
31 		pr_debug("PKCS#7 message does not contain data\n");
32 		err = -EBADMSG;
33 		goto out;
34 	}
35 
36 	if (root_hash.len == 0) {
37 		pr_debug("Root hash is empty.\n");
38 		err = -EBADMSG;
39 		goto out;
40 	}
41 
42 	if (data_len != root_hash.len + add_data.len) {
43 		pr_debug("PKCS#7 data size doesn't match arguments.\n");
44 		err = -EKEYREJECTED;
45 		goto out;
46 	}
47 
48 	p = data;
49 	if (memcmp(p, root_hash.data, root_hash.len) != 0) {
50 		pr_debug("Root hash mismatch.\n");
51 		err = -EKEYREJECTED;
52 		goto out;
53 	}
54 	p += root_hash.len;
55 	if (memcmp(p, add_data.data, add_data.len) != 0) {
56 		pr_debug("Additional data mismatch.\n");
57 		err = -EKEYREJECTED;
58 		goto out;
59 	}
60 
61 	err = pkcs7_verify(pkcs7, VERIFYING_UNSPECIFIED_SIGNATURE);
62 	if (err)
63 		pr_debug("PKCS#7 signature verification error: %d\n", -err);
64 
65 	/*
66 	 * RSA signature verification sometimes returns unexpected error codes
67 	 * when signature doesn't match.
68 	 */
69 	if (err == -ERANGE || err == -EINVAL)
70 		err = -EBADMSG;
71 
72 out:
73 	pkcs7_free_message(pkcs7);
74 	return err;
75 }
76 
incfs_get_hash_alg(enum incfs_hash_tree_algorithm id)77 struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id)
78 {
79 	static struct incfs_hash_alg sha256 = {
80 		.name = "sha256",
81 		.digest_size = SHA256_DIGEST_SIZE,
82 		.id = INCFS_HASH_TREE_SHA256
83 	};
84 	struct incfs_hash_alg *result = NULL;
85 	struct crypto_shash *shash;
86 
87 	if (id == INCFS_HASH_TREE_SHA256) {
88 		BUILD_BUG_ON(INCFS_MAX_HASH_SIZE < SHA256_DIGEST_SIZE);
89 		result = &sha256;
90 	}
91 
92 	if (result == NULL)
93 		return ERR_PTR(-ENOENT);
94 
95 	/* pairs with cmpxchg_release() below */
96 	shash = smp_load_acquire(&result->shash);
97 	if (shash)
98 		return result;
99 
100 	shash = crypto_alloc_shash(result->name, 0, 0);
101 	if (IS_ERR(shash)) {
102 		int err = PTR_ERR(shash);
103 
104 		pr_err("Can't allocate hash alg %s, error code:%d",
105 			result->name, err);
106 		return ERR_PTR(err);
107 	}
108 
109 	/* pairs with smp_load_acquire() above */
110 	if (cmpxchg_release(&result->shash, NULL, shash) != NULL)
111 		crypto_free_shash(shash);
112 
113 	return result;
114 }
115 
116 
incfs_alloc_mtree(enum incfs_hash_tree_algorithm id,int data_block_count,struct mem_range root_hash)117 struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id,
118 				int data_block_count,
119 				struct mem_range root_hash)
120 {
121 	struct mtree *result = NULL;
122 	struct incfs_hash_alg *hash_alg = NULL;
123 	int hash_per_block;
124 	int lvl;
125 	int total_blocks = 0;
126 	int blocks_in_level[INCFS_MAX_MTREE_LEVELS];
127 	int blocks = data_block_count;
128 
129 	if (data_block_count <= 0)
130 		return ERR_PTR(-EINVAL);
131 
132 	hash_alg = incfs_get_hash_alg(id);
133 	if (IS_ERR(hash_alg))
134 		return ERR_PTR(PTR_ERR(hash_alg));
135 
136 	if (root_hash.len < hash_alg->digest_size)
137 		return ERR_PTR(-EINVAL);
138 
139 	result = kzalloc(sizeof(*result), GFP_NOFS);
140 	if (!result)
141 		return ERR_PTR(-ENOMEM);
142 
143 	result->alg = hash_alg;
144 	hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / result->alg->digest_size;
145 
146 	/* Calculating tree geometry. */
147 	/* First pass: calculate how many blocks in each tree level. */
148 	for (lvl = 0; blocks > 1; lvl++) {
149 		if (lvl >= INCFS_MAX_MTREE_LEVELS) {
150 			pr_err("incfs: too much data in mtree");
151 			goto err;
152 		}
153 
154 		blocks = (blocks + hash_per_block - 1) / hash_per_block;
155 		blocks_in_level[lvl] = blocks;
156 		total_blocks += blocks;
157 	}
158 	result->depth = lvl;
159 	result->hash_tree_area_size = total_blocks * INCFS_DATA_FILE_BLOCK_SIZE;
160 	if (result->hash_tree_area_size > INCFS_MAX_HASH_AREA_SIZE)
161 		goto err;
162 
163 	blocks = 0;
164 	/* Second pass: calculate offset of each level. 0th level goes last. */
165 	for (lvl = 0; lvl < result->depth; lvl++) {
166 		u32 suboffset;
167 
168 		blocks += blocks_in_level[lvl];
169 		suboffset = (total_blocks - blocks)
170 					* INCFS_DATA_FILE_BLOCK_SIZE;
171 
172 		result->hash_level_suboffset[lvl] = suboffset;
173 	}
174 
175 	/* Root hash is stored separately from the rest of the tree. */
176 	memcpy(result->root_hash, root_hash.data, hash_alg->digest_size);
177 	return result;
178 
179 err:
180 	kfree(result);
181 	return ERR_PTR(-E2BIG);
182 }
183 
incfs_free_mtree(struct mtree * tree)184 void incfs_free_mtree(struct mtree *tree)
185 {
186 	kfree(tree);
187 }
188 
incfs_calc_digest(struct incfs_hash_alg * alg,struct mem_range data,struct mem_range digest)189 int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data,
190 			struct mem_range digest)
191 {
192 	SHASH_DESC_ON_STACK(desc, alg->shash);
193 
194 	if (!alg || !alg->shash || !data.data || !digest.data)
195 		return -EFAULT;
196 
197 	if (alg->digest_size > digest.len)
198 		return -EINVAL;
199 
200 	desc->tfm = alg->shash;
201 	return crypto_shash_digest(desc, data.data, data.len, digest.data);
202 }
203 
incfs_free_signature_info(struct signature_info * si)204 void incfs_free_signature_info(struct signature_info *si)
205 {
206 	if (!si)
207 		return;
208 	kfree(si->root_hash.data);
209 	kfree(si->additional_data.data);
210 	kfree(si->signature.data);
211 	kfree(si);
212 }
213 
214