xref: /kernel/linux/linux-6.6/fs/ubifs/auth.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This file is part of UBIFS.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2018 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * This file implements various helper functions for UBIFS authentication support
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/crypto.h>
1362306a36Sopenharmony_ci#include <linux/verification.h>
1462306a36Sopenharmony_ci#include <crypto/hash.h>
1562306a36Sopenharmony_ci#include <crypto/algapi.h>
1662306a36Sopenharmony_ci#include <keys/user-type.h>
1762306a36Sopenharmony_ci#include <keys/asymmetric-type.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "ubifs.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/**
2262306a36Sopenharmony_ci * ubifs_node_calc_hash - calculate the hash of a UBIFS node
2362306a36Sopenharmony_ci * @c: UBIFS file-system description object
2462306a36Sopenharmony_ci * @node: the node to calculate a hash for
2562306a36Sopenharmony_ci * @hash: the returned hash
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Returns 0 for success or a negative error code otherwise.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ciint __ubifs_node_calc_hash(const struct ubifs_info *c, const void *node,
3062306a36Sopenharmony_ci			    u8 *hash)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	const struct ubifs_ch *ch = node;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	return crypto_shash_tfm_digest(c->hash_tfm, node, le32_to_cpu(ch->len),
3562306a36Sopenharmony_ci				       hash);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/**
3962306a36Sopenharmony_ci * ubifs_hash_calc_hmac - calculate a HMAC from a hash
4062306a36Sopenharmony_ci * @c: UBIFS file-system description object
4162306a36Sopenharmony_ci * @hash: the node to calculate a HMAC for
4262306a36Sopenharmony_ci * @hmac: the returned HMAC
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * Returns 0 for success or a negative error code otherwise.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_cistatic int ubifs_hash_calc_hmac(const struct ubifs_info *c, const u8 *hash,
4762306a36Sopenharmony_ci				 u8 *hmac)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	return crypto_shash_tfm_digest(c->hmac_tfm, hash, c->hash_len, hmac);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/**
5362306a36Sopenharmony_ci * ubifs_prepare_auth_node - Prepare an authentication node
5462306a36Sopenharmony_ci * @c: UBIFS file-system description object
5562306a36Sopenharmony_ci * @node: the node to calculate a hash for
5662306a36Sopenharmony_ci * @inhash: input hash of previous nodes
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * This function prepares an authentication node for writing onto flash.
5962306a36Sopenharmony_ci * It creates a HMAC from the given input hash and writes it to the node.
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * Returns 0 for success or a negative error code otherwise.
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_ciint ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
6462306a36Sopenharmony_ci			     struct shash_desc *inhash)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct ubifs_auth_node *auth = node;
6762306a36Sopenharmony_ci	u8 hash[UBIFS_HASH_ARR_SZ];
6862306a36Sopenharmony_ci	int err;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	{
7162306a36Sopenharmony_ci		SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		hash_desc->tfm = c->hash_tfm;
7462306a36Sopenharmony_ci		ubifs_shash_copy_state(c, inhash, hash_desc);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci		err = crypto_shash_final(hash_desc, hash);
7762306a36Sopenharmony_ci		if (err)
7862306a36Sopenharmony_ci			return err;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	err = ubifs_hash_calc_hmac(c, hash, auth->hmac);
8262306a36Sopenharmony_ci	if (err)
8362306a36Sopenharmony_ci		return err;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	auth->ch.node_type = UBIFS_AUTH_NODE;
8662306a36Sopenharmony_ci	ubifs_prepare_node(c, auth, ubifs_auth_node_sz(c), 0);
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct shash_desc *ubifs_get_desc(const struct ubifs_info *c,
9162306a36Sopenharmony_ci					 struct crypto_shash *tfm)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct shash_desc *desc;
9462306a36Sopenharmony_ci	int err;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (!ubifs_authenticated(c))
9762306a36Sopenharmony_ci		return NULL;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
10062306a36Sopenharmony_ci	if (!desc)
10162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	desc->tfm = tfm;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	err = crypto_shash_init(desc);
10662306a36Sopenharmony_ci	if (err) {
10762306a36Sopenharmony_ci		kfree(desc);
10862306a36Sopenharmony_ci		return ERR_PTR(err);
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return desc;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/**
11562306a36Sopenharmony_ci * __ubifs_hash_get_desc - get a descriptor suitable for hashing a node
11662306a36Sopenharmony_ci * @c: UBIFS file-system description object
11762306a36Sopenharmony_ci *
11862306a36Sopenharmony_ci * This function returns a descriptor suitable for hashing a node. Free after use
11962306a36Sopenharmony_ci * with kfree.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_cistruct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	return ubifs_get_desc(c, c->hash_tfm);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/**
12762306a36Sopenharmony_ci * ubifs_bad_hash - Report hash mismatches
12862306a36Sopenharmony_ci * @c: UBIFS file-system description object
12962306a36Sopenharmony_ci * @node: the node
13062306a36Sopenharmony_ci * @hash: the expected hash
13162306a36Sopenharmony_ci * @lnum: the LEB @node was read from
13262306a36Sopenharmony_ci * @offs: offset in LEB @node was read from
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci * This function reports a hash mismatch when a node has a different hash than
13562306a36Sopenharmony_ci * expected.
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_civoid ubifs_bad_hash(const struct ubifs_info *c, const void *node, const u8 *hash,
13862306a36Sopenharmony_ci		    int lnum, int offs)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	int len = min(c->hash_len, 20);
14162306a36Sopenharmony_ci	int cropped = len != c->hash_len;
14262306a36Sopenharmony_ci	const char *cont = cropped ? "..." : "";
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	u8 calc[UBIFS_HASH_ARR_SZ];
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	__ubifs_node_calc_hash(c, node, calc);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	ubifs_err(c, "hash mismatch on node at LEB %d:%d", lnum, offs);
14962306a36Sopenharmony_ci	ubifs_err(c, "hash expected:   %*ph%s", len, hash, cont);
15062306a36Sopenharmony_ci	ubifs_err(c, "hash calculated: %*ph%s", len, calc, cont);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/**
15462306a36Sopenharmony_ci * __ubifs_node_check_hash - check the hash of a node against given hash
15562306a36Sopenharmony_ci * @c: UBIFS file-system description object
15662306a36Sopenharmony_ci * @node: the node
15762306a36Sopenharmony_ci * @expected: the expected hash
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * This function calculates a hash over a node and compares it to the given hash.
16062306a36Sopenharmony_ci * Returns 0 if both hashes are equal or authentication is disabled, otherwise a
16162306a36Sopenharmony_ci * negative error code is returned.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_ciint __ubifs_node_check_hash(const struct ubifs_info *c, const void *node,
16462306a36Sopenharmony_ci			    const u8 *expected)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	u8 calc[UBIFS_HASH_ARR_SZ];
16762306a36Sopenharmony_ci	int err;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	err = __ubifs_node_calc_hash(c, node, calc);
17062306a36Sopenharmony_ci	if (err)
17162306a36Sopenharmony_ci		return err;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (ubifs_check_hash(c, expected, calc))
17462306a36Sopenharmony_ci		return -EPERM;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/**
18062306a36Sopenharmony_ci * ubifs_sb_verify_signature - verify the signature of a superblock
18162306a36Sopenharmony_ci * @c: UBIFS file-system description object
18262306a36Sopenharmony_ci * @sup: The superblock node
18362306a36Sopenharmony_ci *
18462306a36Sopenharmony_ci * To support offline signed images the superblock can be signed with a
18562306a36Sopenharmony_ci * PKCS#7 signature. The signature is placed directly behind the superblock
18662306a36Sopenharmony_ci * node in an ubifs_sig_node.
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * Returns 0 when the signature can be successfully verified or a negative
18962306a36Sopenharmony_ci * error code if not.
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_ciint ubifs_sb_verify_signature(struct ubifs_info *c,
19262306a36Sopenharmony_ci			      const struct ubifs_sb_node *sup)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	int err;
19562306a36Sopenharmony_ci	struct ubifs_scan_leb *sleb;
19662306a36Sopenharmony_ci	struct ubifs_scan_node *snod;
19762306a36Sopenharmony_ci	const struct ubifs_sig_node *signode;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	sleb = ubifs_scan(c, UBIFS_SB_LNUM, UBIFS_SB_NODE_SZ, c->sbuf, 0);
20062306a36Sopenharmony_ci	if (IS_ERR(sleb)) {
20162306a36Sopenharmony_ci		err = PTR_ERR(sleb);
20262306a36Sopenharmony_ci		return err;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (sleb->nodes_cnt == 0) {
20662306a36Sopenharmony_ci		ubifs_err(c, "Unable to find signature node");
20762306a36Sopenharmony_ci		err = -EINVAL;
20862306a36Sopenharmony_ci		goto out_destroy;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	snod = list_first_entry(&sleb->nodes, struct ubifs_scan_node, list);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (snod->type != UBIFS_SIG_NODE) {
21462306a36Sopenharmony_ci		ubifs_err(c, "Signature node is of wrong type");
21562306a36Sopenharmony_ci		err = -EINVAL;
21662306a36Sopenharmony_ci		goto out_destroy;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	signode = snod->node;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (le32_to_cpu(signode->len) > snod->len + sizeof(struct ubifs_sig_node)) {
22262306a36Sopenharmony_ci		ubifs_err(c, "invalid signature len %d", le32_to_cpu(signode->len));
22362306a36Sopenharmony_ci		err = -EINVAL;
22462306a36Sopenharmony_ci		goto out_destroy;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (le32_to_cpu(signode->type) != UBIFS_SIGNATURE_TYPE_PKCS7) {
22862306a36Sopenharmony_ci		ubifs_err(c, "Signature type %d is not supported\n",
22962306a36Sopenharmony_ci			  le32_to_cpu(signode->type));
23062306a36Sopenharmony_ci		err = -EINVAL;
23162306a36Sopenharmony_ci		goto out_destroy;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	err = verify_pkcs7_signature(sup, sizeof(struct ubifs_sb_node),
23562306a36Sopenharmony_ci				     signode->sig, le32_to_cpu(signode->len),
23662306a36Sopenharmony_ci				     NULL, VERIFYING_UNSPECIFIED_SIGNATURE,
23762306a36Sopenharmony_ci				     NULL, NULL);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (err)
24062306a36Sopenharmony_ci		ubifs_err(c, "Failed to verify signature");
24162306a36Sopenharmony_ci	else
24262306a36Sopenharmony_ci		ubifs_msg(c, "Successfully verified super block signature");
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ciout_destroy:
24562306a36Sopenharmony_ci	ubifs_scan_destroy(sleb);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return err;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/**
25162306a36Sopenharmony_ci * ubifs_init_authentication - initialize UBIFS authentication support
25262306a36Sopenharmony_ci * @c: UBIFS file-system description object
25362306a36Sopenharmony_ci *
25462306a36Sopenharmony_ci * This function returns 0 for success or a negative error code otherwise.
25562306a36Sopenharmony_ci */
25662306a36Sopenharmony_ciint ubifs_init_authentication(struct ubifs_info *c)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct key *keyring_key;
25962306a36Sopenharmony_ci	const struct user_key_payload *ukp;
26062306a36Sopenharmony_ci	int err;
26162306a36Sopenharmony_ci	char hmac_name[CRYPTO_MAX_ALG_NAME];
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (!c->auth_hash_name) {
26462306a36Sopenharmony_ci		ubifs_err(c, "authentication hash name needed with authentication");
26562306a36Sopenharmony_ci		return -EINVAL;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	c->auth_hash_algo = match_string(hash_algo_name, HASH_ALGO__LAST,
26962306a36Sopenharmony_ci					 c->auth_hash_name);
27062306a36Sopenharmony_ci	if ((int)c->auth_hash_algo < 0) {
27162306a36Sopenharmony_ci		ubifs_err(c, "Unknown hash algo %s specified",
27262306a36Sopenharmony_ci			  c->auth_hash_name);
27362306a36Sopenharmony_ci		return -EINVAL;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	snprintf(hmac_name, CRYPTO_MAX_ALG_NAME, "hmac(%s)",
27762306a36Sopenharmony_ci		 c->auth_hash_name);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	keyring_key = request_key(&key_type_logon, c->auth_key_name, NULL);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (IS_ERR(keyring_key)) {
28262306a36Sopenharmony_ci		ubifs_err(c, "Failed to request key: %ld",
28362306a36Sopenharmony_ci			  PTR_ERR(keyring_key));
28462306a36Sopenharmony_ci		return PTR_ERR(keyring_key);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	down_read(&keyring_key->sem);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (keyring_key->type != &key_type_logon) {
29062306a36Sopenharmony_ci		ubifs_err(c, "key type must be logon");
29162306a36Sopenharmony_ci		err = -ENOKEY;
29262306a36Sopenharmony_ci		goto out;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	ukp = user_key_payload_locked(keyring_key);
29662306a36Sopenharmony_ci	if (!ukp) {
29762306a36Sopenharmony_ci		/* key was revoked before we acquired its semaphore */
29862306a36Sopenharmony_ci		err = -EKEYREVOKED;
29962306a36Sopenharmony_ci		goto out;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	c->hash_tfm = crypto_alloc_shash(c->auth_hash_name, 0, 0);
30362306a36Sopenharmony_ci	if (IS_ERR(c->hash_tfm)) {
30462306a36Sopenharmony_ci		err = PTR_ERR(c->hash_tfm);
30562306a36Sopenharmony_ci		ubifs_err(c, "Can not allocate %s: %d",
30662306a36Sopenharmony_ci			  c->auth_hash_name, err);
30762306a36Sopenharmony_ci		goto out;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	c->hash_len = crypto_shash_digestsize(c->hash_tfm);
31162306a36Sopenharmony_ci	if (c->hash_len > UBIFS_HASH_ARR_SZ) {
31262306a36Sopenharmony_ci		ubifs_err(c, "hash %s is bigger than maximum allowed hash size (%d > %d)",
31362306a36Sopenharmony_ci			  c->auth_hash_name, c->hash_len, UBIFS_HASH_ARR_SZ);
31462306a36Sopenharmony_ci		err = -EINVAL;
31562306a36Sopenharmony_ci		goto out_free_hash;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	c->hmac_tfm = crypto_alloc_shash(hmac_name, 0, 0);
31962306a36Sopenharmony_ci	if (IS_ERR(c->hmac_tfm)) {
32062306a36Sopenharmony_ci		err = PTR_ERR(c->hmac_tfm);
32162306a36Sopenharmony_ci		ubifs_err(c, "Can not allocate %s: %d", hmac_name, err);
32262306a36Sopenharmony_ci		goto out_free_hash;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	c->hmac_desc_len = crypto_shash_digestsize(c->hmac_tfm);
32662306a36Sopenharmony_ci	if (c->hmac_desc_len > UBIFS_HMAC_ARR_SZ) {
32762306a36Sopenharmony_ci		ubifs_err(c, "hmac %s is bigger than maximum allowed hmac size (%d > %d)",
32862306a36Sopenharmony_ci			  hmac_name, c->hmac_desc_len, UBIFS_HMAC_ARR_SZ);
32962306a36Sopenharmony_ci		err = -EINVAL;
33062306a36Sopenharmony_ci		goto out_free_hmac;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	err = crypto_shash_setkey(c->hmac_tfm, ukp->data, ukp->datalen);
33462306a36Sopenharmony_ci	if (err)
33562306a36Sopenharmony_ci		goto out_free_hmac;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	c->authenticated = true;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	c->log_hash = ubifs_hash_get_desc(c);
34062306a36Sopenharmony_ci	if (IS_ERR(c->log_hash)) {
34162306a36Sopenharmony_ci		err = PTR_ERR(c->log_hash);
34262306a36Sopenharmony_ci		goto out_free_hmac;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	err = 0;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ciout_free_hmac:
34862306a36Sopenharmony_ci	if (err)
34962306a36Sopenharmony_ci		crypto_free_shash(c->hmac_tfm);
35062306a36Sopenharmony_ciout_free_hash:
35162306a36Sopenharmony_ci	if (err)
35262306a36Sopenharmony_ci		crypto_free_shash(c->hash_tfm);
35362306a36Sopenharmony_ciout:
35462306a36Sopenharmony_ci	up_read(&keyring_key->sem);
35562306a36Sopenharmony_ci	key_put(keyring_key);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return err;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/**
36162306a36Sopenharmony_ci * __ubifs_exit_authentication - release resource
36262306a36Sopenharmony_ci * @c: UBIFS file-system description object
36362306a36Sopenharmony_ci *
36462306a36Sopenharmony_ci * This function releases the authentication related resources.
36562306a36Sopenharmony_ci */
36662306a36Sopenharmony_civoid __ubifs_exit_authentication(struct ubifs_info *c)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	if (!ubifs_authenticated(c))
36962306a36Sopenharmony_ci		return;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	crypto_free_shash(c->hmac_tfm);
37262306a36Sopenharmony_ci	crypto_free_shash(c->hash_tfm);
37362306a36Sopenharmony_ci	kfree(c->log_hash);
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci/**
37762306a36Sopenharmony_ci * ubifs_node_calc_hmac - calculate the HMAC of a UBIFS node
37862306a36Sopenharmony_ci * @c: UBIFS file-system description object
37962306a36Sopenharmony_ci * @node: the node to insert a HMAC into.
38062306a36Sopenharmony_ci * @len: the length of the node
38162306a36Sopenharmony_ci * @ofs_hmac: the offset in the node where the HMAC is inserted
38262306a36Sopenharmony_ci * @hmac: returned HMAC
38362306a36Sopenharmony_ci *
38462306a36Sopenharmony_ci * This function calculates a HMAC of a UBIFS node. The HMAC is expected to be
38562306a36Sopenharmony_ci * embedded into the node, so this area is not covered by the HMAC. Also not
38662306a36Sopenharmony_ci * covered is the UBIFS_NODE_MAGIC and the CRC of the node.
38762306a36Sopenharmony_ci */
38862306a36Sopenharmony_cistatic int ubifs_node_calc_hmac(const struct ubifs_info *c, const void *node,
38962306a36Sopenharmony_ci				int len, int ofs_hmac, void *hmac)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
39262306a36Sopenharmony_ci	int hmac_len = c->hmac_desc_len;
39362306a36Sopenharmony_ci	int err;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	ubifs_assert(c, ofs_hmac > 8);
39662306a36Sopenharmony_ci	ubifs_assert(c, ofs_hmac + hmac_len < len);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	shash->tfm = c->hmac_tfm;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	err = crypto_shash_init(shash);
40162306a36Sopenharmony_ci	if (err)
40262306a36Sopenharmony_ci		return err;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* behind common node header CRC up to HMAC begin */
40562306a36Sopenharmony_ci	err = crypto_shash_update(shash, node + 8, ofs_hmac - 8);
40662306a36Sopenharmony_ci	if (err < 0)
40762306a36Sopenharmony_ci		return err;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* behind HMAC, if any */
41062306a36Sopenharmony_ci	if (len - ofs_hmac - hmac_len > 0) {
41162306a36Sopenharmony_ci		err = crypto_shash_update(shash, node + ofs_hmac + hmac_len,
41262306a36Sopenharmony_ci			    len - ofs_hmac - hmac_len);
41362306a36Sopenharmony_ci		if (err < 0)
41462306a36Sopenharmony_ci			return err;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return crypto_shash_final(shash, hmac);
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci/**
42162306a36Sopenharmony_ci * __ubifs_node_insert_hmac - insert a HMAC into a UBIFS node
42262306a36Sopenharmony_ci * @c: UBIFS file-system description object
42362306a36Sopenharmony_ci * @node: the node to insert a HMAC into.
42462306a36Sopenharmony_ci * @len: the length of the node
42562306a36Sopenharmony_ci * @ofs_hmac: the offset in the node where the HMAC is inserted
42662306a36Sopenharmony_ci *
42762306a36Sopenharmony_ci * This function inserts a HMAC at offset @ofs_hmac into the node given in
42862306a36Sopenharmony_ci * @node.
42962306a36Sopenharmony_ci *
43062306a36Sopenharmony_ci * This function returns 0 for success or a negative error code otherwise.
43162306a36Sopenharmony_ci */
43262306a36Sopenharmony_ciint __ubifs_node_insert_hmac(const struct ubifs_info *c, void *node, int len,
43362306a36Sopenharmony_ci			    int ofs_hmac)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	return ubifs_node_calc_hmac(c, node, len, ofs_hmac, node + ofs_hmac);
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci/**
43962306a36Sopenharmony_ci * __ubifs_node_verify_hmac - verify the HMAC of UBIFS node
44062306a36Sopenharmony_ci * @c: UBIFS file-system description object
44162306a36Sopenharmony_ci * @node: the node to insert a HMAC into.
44262306a36Sopenharmony_ci * @len: the length of the node
44362306a36Sopenharmony_ci * @ofs_hmac: the offset in the node where the HMAC is inserted
44462306a36Sopenharmony_ci *
44562306a36Sopenharmony_ci * This function verifies the HMAC at offset @ofs_hmac of the node given in
44662306a36Sopenharmony_ci * @node. Returns 0 if successful or a negative error code otherwise.
44762306a36Sopenharmony_ci */
44862306a36Sopenharmony_ciint __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *node,
44962306a36Sopenharmony_ci			     int len, int ofs_hmac)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	int hmac_len = c->hmac_desc_len;
45262306a36Sopenharmony_ci	u8 *hmac;
45362306a36Sopenharmony_ci	int err;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	hmac = kmalloc(hmac_len, GFP_NOFS);
45662306a36Sopenharmony_ci	if (!hmac)
45762306a36Sopenharmony_ci		return -ENOMEM;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	err = ubifs_node_calc_hmac(c, node, len, ofs_hmac, hmac);
46062306a36Sopenharmony_ci	if (err) {
46162306a36Sopenharmony_ci		kfree(hmac);
46262306a36Sopenharmony_ci		return err;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	err = crypto_memneq(hmac, node + ofs_hmac, hmac_len);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	kfree(hmac);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (!err)
47062306a36Sopenharmony_ci		return 0;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return -EPERM;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ciint __ubifs_shash_copy_state(const struct ubifs_info *c, struct shash_desc *src,
47662306a36Sopenharmony_ci			     struct shash_desc *target)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	u8 *state;
47962306a36Sopenharmony_ci	int err;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	state = kmalloc(crypto_shash_descsize(src->tfm), GFP_NOFS);
48262306a36Sopenharmony_ci	if (!state)
48362306a36Sopenharmony_ci		return -ENOMEM;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	err = crypto_shash_export(src, state);
48662306a36Sopenharmony_ci	if (err)
48762306a36Sopenharmony_ci		goto out;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	err = crypto_shash_import(target, state);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ciout:
49262306a36Sopenharmony_ci	kfree(state);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return err;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci/**
49862306a36Sopenharmony_ci * ubifs_hmac_wkm - Create a HMAC of the well known message
49962306a36Sopenharmony_ci * @c: UBIFS file-system description object
50062306a36Sopenharmony_ci * @hmac: The HMAC of the well known message
50162306a36Sopenharmony_ci *
50262306a36Sopenharmony_ci * This function creates a HMAC of a well known message. This is used
50362306a36Sopenharmony_ci * to check if the provided key is suitable to authenticate a UBIFS
50462306a36Sopenharmony_ci * image. This is only a convenience to the user to provide a better
50562306a36Sopenharmony_ci * error message when the wrong key is provided.
50662306a36Sopenharmony_ci *
50762306a36Sopenharmony_ci * This function returns 0 for success or a negative error code otherwise.
50862306a36Sopenharmony_ci */
50962306a36Sopenharmony_ciint ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
51262306a36Sopenharmony_ci	int err;
51362306a36Sopenharmony_ci	const char well_known_message[] = "UBIFS";
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (!ubifs_authenticated(c))
51662306a36Sopenharmony_ci		return 0;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	shash->tfm = c->hmac_tfm;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	err = crypto_shash_init(shash);
52162306a36Sopenharmony_ci	if (err)
52262306a36Sopenharmony_ci		return err;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	err = crypto_shash_update(shash, well_known_message,
52562306a36Sopenharmony_ci				  sizeof(well_known_message) - 1);
52662306a36Sopenharmony_ci	if (err < 0)
52762306a36Sopenharmony_ci		return err;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	err = crypto_shash_final(shash, hmac);
53062306a36Sopenharmony_ci	if (err)
53162306a36Sopenharmony_ci		return err;
53262306a36Sopenharmony_ci	return 0;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci/*
53662306a36Sopenharmony_ci * ubifs_hmac_zero - test if a HMAC is zero
53762306a36Sopenharmony_ci * @c: UBIFS file-system description object
53862306a36Sopenharmony_ci * @hmac: the HMAC to test
53962306a36Sopenharmony_ci *
54062306a36Sopenharmony_ci * This function tests if a HMAC is zero and returns true if it is
54162306a36Sopenharmony_ci * and false otherwise.
54262306a36Sopenharmony_ci */
54362306a36Sopenharmony_cibool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	return !memchr_inv(hmac, 0, c->hmac_desc_len);
54662306a36Sopenharmony_ci}
547