mirror of
https://github.com/SciresM/hactool
synced 2024-11-23 04:23:13 +00:00
90 lines
3 KiB
C
90 lines
3 KiB
C
|
#include <stdlib.h>
|
||
|
#include "rsa.h"
|
||
|
#include "sha.h"
|
||
|
#include "utils.h"
|
||
|
#include "types.h"
|
||
|
|
||
|
#define RSA_2048_BYTES 0x100
|
||
|
#define RSA_2048_BITS (RSA_2048_BYTES*8)
|
||
|
|
||
|
void init_mpi_from_buffer(gcry_mpi_t *mpi, const void *data, size_t len) {
|
||
|
/* Create new mpi, len * 8 bits. */
|
||
|
*mpi = gcry_mpi_new(len * 8);
|
||
|
if (gcry_mpi_scan(mpi, GCRYMPI_FMT_USG, data, len, NULL) != 0) {
|
||
|
FATAL_ERROR("Unable to load mpi!");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Perform an RSA-PSS verify operation on data, with signature and N. */
|
||
|
int rsa2048_pss_verify(const void *data, size_t len, const unsigned char *signature, const unsigned char *modulus) {
|
||
|
/* Do gcrypt exponentiation. */
|
||
|
gcry_mpi_t signature_mpi;
|
||
|
gcry_mpi_t modulus_mpi;
|
||
|
gcry_mpi_t e_mpi;
|
||
|
gcry_mpi_t message_mpi = gcry_mpi_new(RSA_2048_BITS);
|
||
|
unsigned char m_buf[RSA_2048_BYTES];
|
||
|
unsigned char h_buf[0x24];
|
||
|
|
||
|
const unsigned char E[3] = {1, 0, 1};
|
||
|
init_mpi_from_buffer(&e_mpi, E, 3);
|
||
|
init_mpi_from_buffer(&signature_mpi, signature, RSA_2048_BYTES);
|
||
|
init_mpi_from_buffer(&modulus_mpi, modulus, RSA_2048_BYTES);
|
||
|
gcry_mpi_powm(message_mpi, signature_mpi, e_mpi, modulus_mpi);
|
||
|
|
||
|
size_t sz;
|
||
|
if (gcry_mpi_print(GCRYMPI_FMT_USG, m_buf, RSA_2048_BYTES, &sz, message_mpi) != 0) {
|
||
|
FATAL_ERROR("Failed to export exponentiated RSA message!");
|
||
|
}
|
||
|
|
||
|
if (sz != RSA_2048_BYTES) { /* Ensure message is correct length. */
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
gcry_mpi_release(signature_mpi);
|
||
|
gcry_mpi_release(modulus_mpi);
|
||
|
gcry_mpi_release(e_mpi);
|
||
|
gcry_mpi_release(message_mpi);
|
||
|
|
||
|
/* libgcrypt requires knowledge of the salt to do automated PSS verification as far as I can tell. */
|
||
|
/* This is not an option in our case... */
|
||
|
if (m_buf[RSA_2048_BYTES-1] != 0xBC) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
memset(h_buf, 0, 0x24);
|
||
|
memcpy(h_buf, m_buf + RSA_2048_BYTES - 0x20 - 0x1, 0x20);
|
||
|
|
||
|
/* Decrypt maskedDB. Should MGF1 be its own function? */
|
||
|
unsigned char seed = 0;
|
||
|
unsigned char mgf1_buf[0x20];
|
||
|
for (unsigned int ofs = 0; ofs < RSA_2048_BYTES - 0x20 - 1; ofs += 0x20) {
|
||
|
h_buf[0x23] = seed++;
|
||
|
sha_hash_buffer(mgf1_buf, h_buf, 0x24);
|
||
|
for (unsigned int i = ofs; i < ofs + 0x20 && i < RSA_2048_BYTES - 0x20 - 1; i++) {
|
||
|
m_buf[i] ^= mgf1_buf[i - ofs];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_buf[0] &= 0x7F; /* Constant lmask for rsa-2048-pss. */
|
||
|
|
||
|
/* Validate DB. */
|
||
|
for (unsigned int i = 0; i < RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1; i++) {
|
||
|
if (m_buf[i] != 0) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
if (m_buf[RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1] != 1) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Check hash correctness. */
|
||
|
unsigned char validate_buf[8 + 0x20 + 0x20];
|
||
|
unsigned char validate_hash[0x20];
|
||
|
memset(validate_buf, 0, 0x48);
|
||
|
sha_hash_buffer(&validate_buf[8], data, len);
|
||
|
memcpy(&validate_buf[0x28], &m_buf[RSA_2048_BYTES - 0x20 - 0x20 - 1], 0x20);
|
||
|
sha_hash_buffer(validate_hash, validate_buf, 0x48);
|
||
|
return memcmp(h_buf, validate_hash, 0x20) == 0;
|
||
|
|
||
|
}
|