mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-08 11:51:48 +00:00
Add SHA3 support.
This commit is contained in:
parent
4b2cd46785
commit
1bc1a05f96
5 changed files with 371 additions and 3 deletions
|
@ -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}".
|
||||
|
|
84
include/core/sha3.h
Normal file
84
include/core/sha3.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* sha3.h
|
||||
*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
* Copyright (c) 2022, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __SHA3_H__
|
||||
#define __SHA3_H__
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#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__ */
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
264
source/sha3.c
Normal file
264
source/sha3.c
Normal file
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* sha3.c
|
||||
*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
* Copyright (c) 2022, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
Loading…
Reference in a new issue