| /* |
| * Cryptographic API. |
| * |
| * HMAC: Keyed-Hashing for Message Authentication (RFC2104). |
| * SHA-256 is used as an underlying hash function. |
| * |
| * Author : Igor Shcheglakov (i.shcheglako@samsung.com) |
| * Date : 15 Dec 2017 |
| * |
| * Copyright (C) 2017 Samsung Electronics Co., Ltd. |
| * |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/string.h> |
| |
| #include "hmac-sha256.h" |
| #include "sha256.h" |
| |
| int hmac_sha256_init(struct hmac_sha256_ctx *ctx, const u8 *key, unsigned int key_len) |
| { |
| int ret = -1; |
| unsigned int i; |
| u8 key_buf[SHA256_BLOCK_SIZE]; |
| unsigned int key_buf_len; |
| u8 inner_pad[SHA256_BLOCK_SIZE]; |
| u8 outer_pad[SHA256_BLOCK_SIZE]; |
| unsigned int block_size = SHA256_BLOCK_SIZE; |
| unsigned int digest_size = SHA256_DIGEST_SIZE; |
| |
| if (!ctx || !key) |
| goto err; |
| |
| // long keys are hashed |
| if (key_len > block_size) { |
| |
| if (sha256(key, key_len, key_buf)) |
| goto err; |
| |
| key_buf_len = digest_size; |
| } else { |
| memcpy(key_buf, key, key_len); |
| key_buf_len = key_len; |
| } |
| |
| // short keys are padded with zeroes to the right |
| if (key_buf_len != block_size) |
| memset(&key_buf[key_buf_len], 0, sizeof(key_buf) - key_buf_len); |
| |
| // inner and outer padding calculation |
| for (i = 0; i < block_size; i++) { |
| inner_pad[i] = 0x36 ^ key_buf[i]; |
| outer_pad[i] = 0x5c ^ key_buf[i]; |
| } |
| |
| // inner hash context preparation |
| if (sha256_init(&ctx->inner_ctx)) |
| goto err; |
| |
| if (sha256_update(&ctx->inner_ctx, inner_pad, sizeof(inner_pad))) |
| goto err; |
| |
| // outer hash context preparation |
| if (sha256_init(&ctx->outer_ctx)) |
| goto err; |
| |
| if (sha256_update(&ctx->outer_ctx, outer_pad, sizeof(outer_pad))) |
| goto err; |
| |
| ret = 0; |
| |
| err: |
| memset(key_buf, 0, sizeof(key_buf)); |
| memset(inner_pad, 0, sizeof(inner_pad)); |
| memset(outer_pad, 0, sizeof(outer_pad)); |
| |
| return ret; |
| } |
| |
| int hmac_sha256_update(struct hmac_sha256_ctx *ctx, const u8 *data, unsigned int data_len) |
| { |
| if (!ctx || !data) |
| return 0; |
| |
| return sha256_update(&ctx->inner_ctx, data, data_len); |
| } |
| |
| int hmac_sha256_final(struct hmac_sha256_ctx *ctx, u8 *out) |
| { |
| int ret = -1; |
| u8 result[SHA256_DIGEST_SIZE]; |
| |
| if (!ctx || !out) |
| goto err; |
| |
| if (sha256_final(&ctx->inner_ctx, result)) |
| goto err; |
| |
| if (sha256_update(&ctx->outer_ctx, result, sizeof(result))) |
| goto err; |
| |
| if (sha256_final(&ctx->outer_ctx, result)) |
| goto err; |
| |
| memcpy(out, result, sizeof(result)); |
| |
| ret = 0; |
| |
| err: |
| return ret; |
| } |
| |
| int hmac_sha256(const u8 *key, unsigned int key_len, const u8 *data, unsigned int data_len, u8 *out) |
| { |
| int ret = -1; |
| struct hmac_sha256_ctx ctx; |
| |
| if (hmac_sha256_init(&ctx, key, key_len)) |
| goto err; |
| |
| if (hmac_sha256_update(&ctx, data, data_len)) |
| goto err; |
| |
| if (hmac_sha256_final(&ctx, out)) |
| goto err; |
| |
| ret = 0; |
| |
| err: |
| return ret; |
| } |
| |
| void hmac_sha256_ctx_cleanup(struct hmac_sha256_ctx *ctx) |
| { |
| memset(ctx, 0, sizeof(struct hmac_sha256_ctx)); |
| } |