1
0
Fork 0
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:
Pablo Curiel 2022-06-25 21:28:31 +02:00
parent 4b2cd46785
commit 1bc1a05f96
5 changed files with 371 additions and 3 deletions

View file

@ -65,6 +65,9 @@
/* USB Mass Storage support. */ /* USB Mass Storage support. */
#include "ums.h" #include "ums.h"
/* SHA3 checksum calculator. */
#include "sha3.h"
/// Used to store version numbers expressed in dot notation: /// Used to store version numbers expressed in dot notation:
/// * System version: "{major}.{minor}.{micro}-{major_relstep}.{minor_relstep}". /// * System version: "{major}.{minor}.{micro}-{major_relstep}.{minor_relstep}".
/// * Application version: "{release}.{private}". /// * Application version: "{release}.{private}".

84
include/core/sha3.h Normal file
View 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__ */

View file

@ -30,6 +30,9 @@
#define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0]))) #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 BIT_LONG(n) (1UL << (n))
#define ALIGN_UP(x, y) (((x) + ((y) - 1)) & ~((y) - 1)) #define ALIGN_UP(x, y) (((x) + ((y) - 1)) & ~((y) - 1))

View file

@ -60,6 +60,7 @@ NX_INLINE bool ncaCheckRightsIdAvailability(NcaContext *ctx);
static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset); 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 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 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); 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; 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. */ /* 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) 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; u8 *parent_layer_block = NULL, *cur_layer_block = NULL;
u64 last_layer_size = 0; 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 && \ 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 || \ 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); 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. */ /* Process each layer. */
for(u32 i = layer_count; i > 0; i--) 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++) 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); 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 { } else {
/* Recalculate master hash from the HashData area. */ /* 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); 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) if (!ctx->skip_hash_layer_crypto || i == layer_count)

264
source/sha3.c Normal file
View 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);
}