From 1bc1a05f9658a17c5d37ea4ebf58ae2e1a64ba1c Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Sat, 25 Jun 2022 21:28:31 +0200 Subject: [PATCH] Add SHA3 support. --- include/core/nxdt_includes.h | 3 + include/core/sha3.h | 84 +++++++++++ include/defines.h | 3 + source/core/nca.c | 20 ++- source/sha3.c | 264 +++++++++++++++++++++++++++++++++++ 5 files changed, 371 insertions(+), 3 deletions(-) create mode 100644 include/core/sha3.h create mode 100644 source/sha3.c diff --git a/include/core/nxdt_includes.h b/include/core/nxdt_includes.h index f71b990..5165679 100644 --- a/include/core/nxdt_includes.h +++ b/include/core/nxdt_includes.h @@ -65,6 +65,9 @@ /* USB Mass Storage support. */ #include "ums.h" +/* SHA3 checksum calculator. */ +#include "sha3.h" + /// Used to store version numbers expressed in dot notation: /// * System version: "{major}.{minor}.{micro}-{major_relstep}.{minor_relstep}". /// * Application version: "{release}.{private}". diff --git a/include/core/sha3.h b/include/core/sha3.h new file mode 100644 index 0000000..2fbc551 --- /dev/null +++ b/include/core/sha3.h @@ -0,0 +1,84 @@ +/* + * sha3.h + * + * Copyright (c) Atmosphère-NX + * Copyright (c) 2022, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * Loosely based on crypto_sha3_impl.hpp from Atmosphere-libs. + * + * nxdumptool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nxdumptool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifndef __SHA3_H__ +#define __SHA3_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SHA3_INTERNAL_STATE_SIZE +#define SHA3_INTERNAL_STATE_SIZE 200 +#endif + +#ifndef SHA3_HASH_SIZE_BYTES +#define SHA3_HASH_SIZE_BYTES(bits) ((bits) / sizeof(u8)) +#endif + +#ifndef SHA3_BLOCK_SIZE +#define SHA3_BLOCK_SIZE(bits) (SHA3_INTERNAL_STATE_SIZE - (2 * SHA3_HASH_SIZE_BYTES(bits))) +#endif + +#define _SHA3_CTX_OPS(bits) \ +void sha3##bits##ContextCreate(Sha3Context *out); \ +void sha3##bits##CalculateHash(void *dst, const void *src, size_t size); + +/// Context for SHA3 operations. +typedef struct { + size_t hash_size; + size_t block_size; + size_t buffered_bytes; + u64 internal_state[SHA3_INTERNAL_STATE_SIZE / sizeof(u64)]; + bool finalized; +} Sha3Context; + +/// SHA3-224 context creation and simple all-in-one calculation functions. +_SHA3_CTX_OPS(224); + +/// SHA3-256 context creation and simple all-in-one calculation functions. +_SHA3_CTX_OPS(256); + +/// SHA3-384 context creation and simple all-in-one calculation functions. +_SHA3_CTX_OPS(384); + +/// SHA3-512 context creation and simple all-in-one calculation functions. +_SHA3_CTX_OPS(512); + +/// Updates SHA3 context with data to hash. +void sha3ContextUpdate(Sha3Context *ctx, const void *src, size_t size); + +/// Gets the context's output hash, finalizes the context. +void sha3ContextGetHash(Sha3Context *ctx, void *dst); + +#undef _SHA3_CTX_OPS + +#ifdef __cplusplus +} +#endif + +#endif /* __SHA3_H__ */ diff --git a/include/defines.h b/include/defines.h index 0e57015..c00d512 100644 --- a/include/defines.h +++ b/include/defines.h @@ -30,6 +30,9 @@ #define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0]))) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + #define BIT_LONG(n) (1UL << (n)) #define ALIGN_UP(x, y) (((x) + ((y) - 1)) & ~((y) - 1)) diff --git a/source/core/nca.c b/source/core/nca.c index 7540802..757211f 100644 --- a/source/core/nca.c +++ b/source/core/nca.c @@ -60,6 +60,7 @@ NX_INLINE bool ncaCheckRightsIdAvailability(NcaContext *ctx); static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset); static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val); +static void ncaCalculateLayerHash(void *dst, const void *src, size_t size, bool use_sha3); static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, void *out, bool is_integrity_patch); static bool ncaWritePatchToMemoryBuffer(NcaContext *ctx, const void *patch, u64 patch_size, u64 patch_offset, void *buf, u64 buf_size, u64 buf_offset); @@ -1149,6 +1150,16 @@ end: return ret; } +static void ncaCalculateLayerHash(void *dst, const void *src, size_t size, bool use_sha3) +{ + if (use_sha3) + { + sha256CalculateHash(dst, src, size); + } else { + sha3256CalculateHash(dst, src, size); + } +} + /* In this function, the term "layer" is used as a generic way to refer to both HierarchicalSha256 hash regions and HierarchicalIntegrity verification levels. */ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, void *out, bool is_integrity_patch) { @@ -1164,7 +1175,7 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, u8 *parent_layer_block = NULL, *cur_layer_block = NULL; u64 last_layer_size = 0; - bool success = false; + bool use_sha3 = false, success = false; if (!ctx || !ctx->enabled || ctx->has_sparse_layer || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || (!is_integrity_patch && ((ctx->hash_type != NcaHashType_HierarchicalSha256 && \ ctx->hash_type != NcaHashType_HierarchicalSha3256) || !ctx->header.hash_data.hierarchical_sha256_data.hash_block_size || \ @@ -1188,6 +1199,9 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, ncaFreeHierarchicalIntegrityPatch(hierarchical_integrity_patch); } + /* Check if we should use SHA3-256 instead of SHA-256 for layer hash calculation. */ + use_sha3 = (ctx->hash_type == NcaHashType_HierarchicalSha3256 || ctx->hash_type == NcaHashType_HierarchicalIntegritySha3); + /* Process each layer. */ for(u32 i = layer_count; i > 0; i--) { @@ -1302,12 +1316,12 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, for(u64 j = 0, k = 0; j < cur_layer_read_size; j += hash_block_size, k++) { if (!is_integrity_patch && hash_block_size > (cur_layer_read_size - j)) hash_block_size = (cur_layer_read_size - j); - sha256CalculateHash(parent_layer_block + (k * SHA256_HASH_SIZE), cur_layer_block + j, hash_block_size); + ncaCalculateLayerHash(parent_layer_block + (k * SHA256_HASH_SIZE), cur_layer_block + j, hash_block_size, use_sha3); } } else { /* Recalculate master hash from the HashData area. */ u8 *master_hash = (!is_integrity_patch ? ctx->header.hash_data.hierarchical_sha256_data.master_hash : ctx->header.hash_data.integrity_meta_info.master_hash); - sha256CalculateHash(master_hash, cur_layer_block, cur_layer_read_size); + ncaCalculateLayerHash(master_hash, cur_layer_block, cur_layer_read_size, use_sha3); } if (!ctx->skip_hash_layer_crypto || i == layer_count) diff --git a/source/sha3.c b/source/sha3.c new file mode 100644 index 0000000..24ff324 --- /dev/null +++ b/source/sha3.c @@ -0,0 +1,264 @@ +/* + * sha3.c + * + * Copyright (c) Atmosphère-NX + * Copyright (c) 2022, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * Loosely based on crypto_sha3_impl.cpp from Atmosphere-libs. + * + * nxdumptool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nxdumptool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "nxdt_utils.h" +#include "sha3.h" + +#define SHA3_NUM_ROUNDS 24 + +#define _SHA3_CTX_OPS(bits) \ +void sha3##bits##ContextCreate(Sha3Context *out) { \ + sha3ContextCreate(out, bits); \ +} \ +void sha3##bits##CalculateHash(void *dst, const void *src, size_t size) { \ + Sha3Context ctx; \ + sha3##bits##ContextCreate(&ctx); \ + sha3ContextUpdate(&ctx, src, size); \ + sha3ContextGetHash(&ctx, dst); \ +} + +/* Global constants. */ + +static const u64 g_iotaRoundConstant[SHA3_NUM_ROUNDS] = { + 0x0000000000000001, 0x0000000000008082, + 0x800000000000808A, 0x8000000080008000, + 0x000000000000808B, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, + 0x000000000000008A, 0x0000000000000088, + 0x0000000080008009, 0x000000008000000A, + 0x000000008000808B, 0x800000000000008B, + 0x8000000000008089, 0x8000000000008003, + 0x8000000000008002, 0x8000000000000080, + 0x000000000000800A, 0x800000008000000A, + 0x8000000080008081, 0x8000000000008080, + 0x0000000080000001, 0x8000000080008008 +}; + +static const int g_rhoShiftBit[SHA3_NUM_ROUNDS] = { + 1, 3, 6, 10, 15, 21, 28, 36, + 45, 55, 2, 14, 27, 41, 56, 8, + 25, 43, 62, 18, 39, 61, 20, 44 +}; + +static const int g_rhoNextIndex[SHA3_NUM_ROUNDS] = { + 10, 7, 11, 17, 18, 3, 5, 16, + 8, 21, 24, 4, 15, 23, 19, 13, + 12, 2, 20, 14, 22, 9, 6, 1 +}; + +static const u64 g_finalMask = 0x8000000000000000; + +/* Function prototypes. */ + +static u64 rotl_u64(u64 x, int s); +static u64 rotr_u64(u64 x, int s); + +static void sha3ContextCreate(Sha3Context *out, u32 hash_size); + +static void sha3ProcessBlock(Sha3Context *ctx); +static void sha3ProcessLastBlock(Sha3Context *ctx); + +/* Functions for SHA3 context creation and simple all-in-one calculation. */ + +_SHA3_CTX_OPS(224); +_SHA3_CTX_OPS(256); +_SHA3_CTX_OPS(384); +_SHA3_CTX_OPS(512); + +#undef _SHA3_CTX_OPS + +void sha3ContextUpdate(Sha3Context *ctx, const void *src, size_t size) +{ + if (!ctx || !src || !size || ctx->finalized) + { + LOG_MSG("Invalid parameters!"); + return; + } + + const u8 *src_u8 = (u8*)src; + size_t remaining = size; + + /* Process we have anything buffered. */ + if (ctx->buffered_bytes > 0) + { + /* Determine how much we can copy. */ + const size_t copy_size = MIN(ctx->block_size - ctx->buffered_bytes, remaining); + + /* Mix the bytes into our state. */ + u8 *dst = (((u8*)ctx->internal_state) + ctx->buffered_bytes); + for(size_t i = 0; i < copy_size; ++i) dst[i] ^= src_u8[i]; + + /* Advance. */ + src_u8 += copy_size; + remaining -= copy_size; + ctx->buffered_bytes += copy_size; + + /* Process a block, if we filled one. */ + if (ctx->buffered_bytes == ctx->block_size) + { + sha3ProcessBlock(ctx); + ctx->buffered_bytes = 0; + } + } + + /* Process blocks, if we have any. */ + while(remaining >= ctx->block_size) + { + /* Mix the bytes into our state. */ + u8 *dst = (u8*)ctx->internal_state; + for(size_t i = 0; i < ctx->block_size; ++i) dst[i] ^= src_u8[i]; + + sha3ProcessBlock(ctx); + + src_u8 += ctx->block_size; + remaining -= ctx->block_size; + } + + /* Copy any leftover data to our buffer. */ + if (remaining > 0) + { + u8 *dst = (u8*)ctx->internal_state; + for(size_t i = 0; i < remaining; ++i) dst[i] ^= src_u8[i]; + ctx->buffered_bytes = remaining; + } +} + +void sha3ContextGetHash(Sha3Context *ctx, void *dst) +{ + if (!ctx || !dst) + { + LOG_MSG("Invalid parameters!"); + return; + } + + /* If we need to, process the last block. */ + if (!ctx->finalized) + { + sha3ProcessLastBlock(ctx); + ctx->finalized = true; + } + + /* Copy the output hash. */ + memcpy(dst, ctx->internal_state, ctx->hash_size); +} + +static u64 rotl_u64(u64 x, int s) +{ + int N = (sizeof(u64) * 8); + int r = (s % N); + + if (r == 0) + { + return x; + } else + if (r > 0) + { + return ((x << r) | (x >> (N - r))); + } + + return rotr_u64(x, -r); +} + +static u64 rotr_u64(u64 x, int s) +{ + int N = (sizeof(u64) * 8); + int r = (s % N); + + if (r == 0) + { + return x; + } else + if (r > 0) + { + return ((x >> r) | (x << (N - r))); + } + + return rotl_u64(x, -r); +} + +static void sha3ContextCreate(Sha3Context *out, u32 hash_size) +{ + if (!out) + { + LOG_MSG("Invalid parameters!"); + return; + } + + memset(out, 0, sizeof(Sha3Context)); + + out->hash_size = SHA3_HASH_SIZE_BYTES(hash_size); + out->block_size = SHA3_BLOCK_SIZE(hash_size); +} + +static void sha3ProcessBlock(Sha3Context *ctx) +{ + u64 tmp = 0, C[5] = {0}; + + /* Perform all rounds. */ + for(u8 round = 0; round < SHA3_NUM_ROUNDS; ++round) + { + /* Handle theta. */ + for(size_t i = 0; i < 5; ++i) + { + C[i] = (ctx->internal_state[i] ^ ctx->internal_state[i + 5] ^ ctx->internal_state[i + 10] ^ ctx->internal_state[i + 15] ^ ctx->internal_state[i + 20]); + } + + for(size_t i = 0; i < 5; ++i) + { + tmp = (C[(i + 4) % 5] ^ rotl_u64(C[(i + 1) % 5], 1)); + for(size_t j = 0; j < 5; ++j) ctx->internal_state[(5 * j) + i] ^= tmp; + } + + /* Handle rho/pi. */ + tmp = ctx->internal_state[1]; + for(size_t i = 0; i < SHA3_NUM_ROUNDS; ++i) + { + const int rho_next_idx = g_rhoNextIndex[i]; + C[0] = ctx->internal_state[rho_next_idx]; + ctx->internal_state[rho_next_idx] = rotl_u64(tmp, g_rhoShiftBit[i]); + tmp = C[0]; + } + + /* Handle chi. */ + for(size_t i = 0; i < 5; ++i) + { + for(size_t j = 0; j < 5; ++j) C[j] = ctx->internal_state[(5 * i) + j]; + for(size_t j = 0; j < 5; ++j) ctx->internal_state[(5 * i) + j] ^= ((~C[(j + 1) % 5]) & C[(j + 2) % 5]); + } + + /* Handle iota. */ + ctx->internal_state[0] ^= g_iotaRoundConstant[round]; + } +} + +static void sha3ProcessLastBlock(Sha3Context *ctx) +{ + /* Mix final bits (011) into our state. */ + ((u8*)ctx->internal_state)[ctx->buffered_bytes] ^= 0b110; + + /* Mix in the high bit of the last word in our block. */ + ctx->internal_state[(ctx->block_size / sizeof(u64)) - 1] ^= g_finalMask; + + /* Process the last block. */ + sha3ProcessBlock(ctx); +}