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