From 709aefba5d984f3fae1b7c80f15c1a7315408099 Mon Sep 17 00:00:00 2001 From: TuxSH Date: Wed, 3 Aug 2016 14:13:26 +0200 Subject: [PATCH] Implement a PIN-checking system. Idea and original code by @reworks --- source/buttons.h | 5 +- source/config.c | 15 +--- source/crypto.c | 44 +++++++----- source/crypto.h | 4 +- source/firm.c | 25 ++++++- source/pin.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++ source/pin.h | 46 ++++++++++++ source/utils.c | 17 +++-- source/utils.h | 2 + 9 files changed, 300 insertions(+), 39 deletions(-) create mode 100644 source/pin.c create mode 100644 source/pin.h diff --git a/source/buttons.h b/source/buttons.h index 05ddc7a..83f9a04 100644 --- a/source/buttons.h +++ b/source/buttons.h @@ -28,7 +28,7 @@ #define BUTTON_R1 (1 << 8) #define BUTTON_L1 (1 << 9) -#define BUTTON_A 1 +#define BUTTON_A (1 << 0) #define BUTTON_B (1 << 1) #define BUTTON_X (1 << 10) #define BUTTON_Y (1 << 11) @@ -42,4 +42,5 @@ #define SAFE_MODE (BUTTON_R1 | BUTTON_L1 | BUTTON_A | BUTTON_UP) #define SINGLE_PAYLOAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START | BUTTON_X | BUTTON_Y) #define L_PAYLOAD_BUTTONS (BUTTON_R1 | BUTTON_A | BUTTON_SELECT) -#define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START) \ No newline at end of file +#define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START) +#define PIN_BUTTONS (BUTTON_A | BUTTON_B | BUTTON_X | BUTTON_Y | BUTTON_START) \ No newline at end of file diff --git a/source/config.c b/source/config.c index 183a098..b1a5563 100644 --- a/source/config.c +++ b/source/config.c @@ -25,13 +25,11 @@ #include "screen.h" #include "draw.h" #include "fs.h" -#include "i2c.h" #include "buttons.h" void configureCFW(const char *configPath) { - bool needToDeinit = initScreens(); - + clearScreens(); drawString(CONFIG_TITLE, 10, 10, COLOR_TITLE); drawString("Press A to select, START to save", 10, 30, COLOR_WHITE); @@ -44,7 +42,8 @@ void configureCFW(const char *configPath) "( ) Enable region/language emu. and ext. .code", "( ) Show current NAND in System Settings", "( ) Show GBA boot screen in patched AGB_FIRM", - "( ) Enable splash screen with no screen-init" }; + "( ) Enable splash screen with no screen-init", + "( ) Use a PIN" }; struct multiOption { int posXs[4]; @@ -202,12 +201,4 @@ void configureCFW(const char *configPath) //Wait for the pressed buttons to change while(HID_PAD == BUTTON_START); - - if(needToDeinit) - { - //Turn off backlight - i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x16); - deinitScreens(); - PDN_GPU_CNT = 1; - } } \ No newline at end of file diff --git a/source/crypto.c b/source/crypto.c index b82ae90..cb3806e 100755 --- a/source/crypto.c +++ b/source/crypto.c @@ -294,16 +294,16 @@ static void sha(void *res, const void *src, u32 size, u32 mode) * NAND/FIRM crypto ****************************************************************/ -static u8 nandCTR[0x10], - nandSlot; +static u8 __attribute__((aligned(4))) nandCTR[0x10]; +static u8 nandSlot; static u32 fatStart; //Initialize the CTRNAND crypto void ctrNandInit(void) { - u8 cid[0x10]; - u8 shaSum[0x20]; + u8 __attribute__((aligned(4))) cid[0x10]; + u8 __attribute__((aligned(4))) shaSum[0x20]; sdmmc_get_cid(1, (u32 *)cid); sha(shaSum, cid, 0x10, SHA_256_MODE); @@ -311,7 +311,7 @@ void ctrNandInit(void) if(isN3DS) { - u8 keyY0x5[0x10] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE}; + u8 __attribute__((aligned(4))) keyY0x5[0x10] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE}; aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); nandSlot = 0x05; fatStart = 0x5CAD7; @@ -326,7 +326,7 @@ void ctrNandInit(void) //Read and decrypt from the selected CTRNAND u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf) { - u8 tmpCTR[0x10]; + u8 __attribute__((aligned(4))) tmpCTR[0x10]; memcpy(tmpCTR, nandCTR, 0x10); aes_advctr(tmpCTR, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL); @@ -350,8 +350,8 @@ u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf) //Sets the 7.x NCCH KeyX and the 6.x gamecard save data KeyY void setRSAMod0DerivedKeys(void) { - const u8 keyX0x25[0x10] = {0xCE, 0xE7, 0xD8, 0xAB, 0x30, 0xC0, 0x0D, 0xAE, 0x85, 0x0E, 0xF5, 0xE3, 0x82, 0xAC, 0x5A, 0xF3}, - keyY0x2F[0x10] = {0xC3, 0x69, 0xBA, 0xA2, 0x1E, 0x18, 0x8A, 0x88, 0xA9, 0xAA, 0x94, 0xE5, 0x50, 0x6A, 0x9F, 0x16}; + const u8 __attribute__((aligned(4))) keyX0x25[0x10] = {0xCE, 0xE7, 0xD8, 0xAB, 0x30, 0xC0, 0x0D, 0xAE, 0x85, 0x0E, 0xF5, 0xE3, 0x82, 0xAC, 0x5A, 0xF3}; + const u8 __attribute__((aligned(4))) keyY0x2F[0x10] = {0xC3, 0x69, 0xBA, 0xA2, 0x1E, 0x18, 0x8A, 0x88, 0xA9, 0xAA, 0x94, 0xE5, 0x50, 0x6A, 0x9F, 0x16}; aes_setkey(0x25, keyX0x25, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); aes_setkey(0x2F, keyY0x2F, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); @@ -394,9 +394,9 @@ void arm9Loader(u8 *arm9Section) } //Firm keys - u8 keyY[0x10], - arm9BinCTR[0x10], - arm9BinSlot = a9lVersion ? 0x16 : 0x15; + u8 __attribute__((aligned(4))) keyY[0x10]; + u8 __attribute__((aligned(4))) arm9BinCTR[0x10]; + u8 arm9BinSlot = a9lVersion ? 0x16 : 0x15; //Setup keys needed for arm9bin decryption memcpy(keyY, arm9Section + 0x10, 0x10); @@ -410,9 +410,9 @@ void arm9Loader(u8 *arm9Section) if(a9lVersion) { - const u8 key1[0x10] = {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8}, - key2[0x10] = {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0}; - u8 keyX[0x10]; + const u8 __attribute__((aligned(4))) key1[0x10] = {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8}; + const u8 __attribute__((aligned(4))) key2[0x10] = {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0}; + u8 __attribute__((aligned(4))) keyX[0x10]; aes_setkey(0x11, a9lVersion == 2 ? key2 : key1, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); aes_use_keyslot(0x11); @@ -430,8 +430,8 @@ void arm9Loader(u8 *arm9Section) //Set >=9.6 KeyXs if(a9lVersion == 2) { - u8 keyData[0x10] = {0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, 0x9D, 0x2A, 0x21, 0x98}, - decKey[0x10]; + u8 __attribute__((aligned(4))) keyData[0x10] = {0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, 0x9D, 0x2A, 0x21, 0x98}; + u8 __attribute__((aligned(4))) decKey[0x10]; //Set keys 0x19..0x1F keyXs aes_use_keyslot(0x11); @@ -442,4 +442,16 @@ void arm9Loader(u8 *arm9Section) keyData[0xF] += 1; } } +} + +void computePINHash(u8 out[32], u8 *in, u32 blockCount) +{ + u8 __attribute__((aligned(4))) cid[0x10]; + u8 __attribute__((aligned(4))) cipherText[0x10]; + sdmmc_get_cid(1, (u32 *)cid); + + aes_use_keyslot(4); // console-unique keyslot which keys are set by the Arm9 bootROM + aes(cipherText, in, blockCount, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + sha(out, cipherText, 0x10, SHA_256_MODE); } \ No newline at end of file diff --git a/source/crypto.h b/source/crypto.h index 8872b0b..f8db96c 100755 --- a/source/crypto.h +++ b/source/crypto.h @@ -107,4 +107,6 @@ void ctrNandInit(void); u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf); void setRSAMod0DerivedKeys(void); void decryptExeFs(u8 *inbuf); -void arm9Loader(u8 *arm9Section); \ No newline at end of file +void arm9Loader(u8 *arm9Section); + +void computePINHash(u8 out[32], u8 *in, u32 blockCount); \ No newline at end of file diff --git a/source/firm.c b/source/firm.c index d1a1f0a..4912aeb 100755 --- a/source/firm.c +++ b/source/firm.c @@ -32,6 +32,8 @@ #include "draw.h" #include "screen.h" #include "buttons.h" +#include "pin.h" +#include "i2c.h" #include "../build/injector.h" static firmHeader *const firm = (firmHeader *)0x24000000; @@ -44,6 +46,8 @@ bool isN3DS; FirmwareSource firmSource; +PINData pin; + void main(void) { bool isFirmlaunch, @@ -67,6 +71,7 @@ void main(void) //Attempt to read the configuration file needConfig = fileRead(&config, configPath) ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION; + bool pinExists = CONFIG(7) && readPin(&pin); //Determine if this is a firmlaunch boot if(*(vu8 *)0x23F00005) @@ -123,10 +128,19 @@ void main(void) } //If no configuration file exists or SELECT is held, load configuration menu - if(needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1))) + bool loadConfigurationMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1)); + bool needToDeinit = false; + if(CFG_BOOTENV == 0 || loadConfigurationMenu) + { + if(loadConfigurationMenu || pinExists) needToDeinit = initScreens(); + if(pinExists) verifyPin(&pin, true); + } + if(loadConfigurationMenu) { configureCFW(configPath); + if(!pinExists && CONFIG(7)) pin = newPin(); + //Zero the last booted FIRM flag CFG_BOOTENV = 0; @@ -137,6 +151,13 @@ void main(void) //Update pressed buttons pressed = HID_PAD; } + if(needToDeinit) + { + //Turn off backlight + i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x16); + deinitScreens(); + PDN_GPU_CNT = 1; + } if(isA9lh && !CFG_BOOTENV && pressed == SAFE_MODE) { @@ -294,7 +315,7 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 //Apply anti-anti-DG patches patchTitleInstallMinVersionCheck(process9Offset, process9Size); - //Restore SVCBackdoor + //Restore svcBackdoor reimplementSvcBackdoor((u8 *)firm + section[1].offset, section[1].size); } } diff --git a/source/pin.c b/source/pin.c new file mode 100644 index 0000000..c7f3d50 --- /dev/null +++ b/source/pin.c @@ -0,0 +1,181 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program 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. +* +* This program 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 . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +/* +* pin.c +* Code to manage pin locking for 3ds. By reworks. +*/ + +#include "draw.h" +#include "screen.h" +#include "utils.h" +#include "memory.h" +#include "buttons.h" +#include "fs.h" +#include "i2c.h" +#include "pin.h" +#include "crypto.h" + +bool readPin(PINData *out) +{ + u8 __attribute__((aligned(4))) zeroes[16] = {0}; + u8 __attribute__((aligned(4))) tmp[32] = {0}; + if(fileRead(out, "/luma/pin.bin") != sizeof(PINData)) return false; + else if(memcmp(out->magic, "PINF", 4) != 0) return false; + + computePINHash(tmp, zeroes, 1); + fileWrite(tmp, "/luma/testhash.bin", 32); + return memcmp(out->testHash, tmp, 32) == 0; //test vector verification (SD card has (or hasn't) been used on another console) +} + +static inline char PINKeyToLetter(u32 pressed) +{ + const char keys[] = "AB--------XY"; + + u32 i; + __asm__ volatile("clz %[i], %[pressed]" : [i] "=r" (i) : [pressed] "r" (pressed)); + + return keys[31 - i]; +} + +PINData newPin(void) +{ + clearScreens(); + + drawString("Enter your NEW PIN (4 keys): ", 10, 10, COLOR_WHITE); + + u32 pressed = 0; + + // Set the default value as 0x00 so we can check if there are any unentered characters. + u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; // pad to AES block length + + int charDrawPos = 29 * SPACING_X; + int cnt = 0; + + while(true) + { + do + { + pressed = waitInput(); + } + while(!(pressed & PIN_BUTTONS & ~BUTTON_START)); + + pressed &= PIN_BUTTONS & ~BUTTON_START; + + if(!pressed) continue; + char key = PINKeyToLetter(pressed); + enteredPassword[cnt++] = (u8)key; // add character to password. + + // visualize character on screen. + drawCharacter(key, 10 + charDrawPos, 10, COLOR_WHITE); + charDrawPos += 2 * SPACING_X; + + // we leave the rest of the array zeroed out. + if (cnt >= PIN_LENGTH) + { + + PINData pin = {0}; + u8 __attribute__((aligned(4))) tmp[32] = {0}; + u8 __attribute__((aligned(4))) zeroes[16] = {0}; + + memcpy(pin.magic, "PINF", 4); + pin.formatVersionMajor = 1; + pin.formatVersionMinor = 0; + + computePINHash(tmp, zeroes, 1); + memcpy(pin.testHash, tmp, 32); + + computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16); + memcpy(pin.hash, tmp, 32); + + fileWrite(&pin, "/luma/pin.bin", sizeof(PINData)); + return pin; + } + } +} + +void verifyPin(PINData *in, bool allowQuit) +{ + clearScreens(); + + drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE); + drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE); + + u32 pressed = 0; + + // Set the default characters as 0x00 so we can check if there are any unentered characters. + u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; + + bool unlock; + int charDrawPos = 5 * SPACING_X, cnt = 0; + + while(true) + { + do + { + pressed = waitInput(); + } + while(!(pressed & PIN_BUTTONS)); + + pressed &= PIN_BUTTONS;// & ~BUTTON_START; + if(!allowQuit) pressed &= ~BUTTON_START; + if(!pressed) continue; + + if(pressed & BUTTON_START) + { + clearScreens(); + mcuPowerOff(); + } + + char key = PINKeyToLetter(pressed); + enteredPassword[cnt++] = (u8)key; // add character to password. + + // visualize character on screen. + drawCharacter(key, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE); + charDrawPos += 2 * SPACING_X; + + if(cnt >= PIN_LENGTH) + { + u8 __attribute__((aligned(4))) tmp[32] = {0}; + + computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16); + unlock = memcmp(in->hash, tmp, 32) == 0; + + if (!unlock) + { + // re zero out all 16 just in case. + memset32(enteredPassword, 0, 16); + + pressed = 0; + charDrawPos = 5 * SPACING_X; + cnt = 0; + + clearScreens(); + + drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE); + drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE); + drawString("Wrong pin! Try again!", 10, 10 + 3 * SPACING_Y, COLOR_RED); + } + else return; + } + } +} \ No newline at end of file diff --git a/source/pin.h b/source/pin.h new file mode 100644 index 0000000..cfa4684 --- /dev/null +++ b/source/pin.h @@ -0,0 +1,46 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program 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. +* +* This program 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 . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +/* +* pin.h +* +* Code to manage pin locking for 3ds. By reworks. +*/ + +#pragma once + +#include "types.h" + +#define PIN_LENGTH 4 +typedef struct __attribute__((packed)) +{ + char magic[4]; + u16 formatVersionMajor, formatVersionMinor; + + u8 testHash[32]; + u8 hash[32]; +} PINData; + +bool readPin(PINData* out); + +PINData newPin(void); +void verifyPin(PINData *in, bool allowQuit); \ No newline at end of file diff --git a/source/utils.c b/source/utils.c index 76cbdeb..f180ecd 100644 --- a/source/utils.c +++ b/source/utils.c @@ -59,7 +59,15 @@ void mcuReboot(void) flushEntireDCache(); //Ensure that all memory transfers have completed and that the data cache has been flushed i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); - while(1); + while(true); +} + +void mcuPowerOff(void) +{ + flushEntireDCache(); //Ensure that all memory transfers have completed and that the data cache has been flushed + + i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0); + while(true); } //TODO: add support for TIMER IRQ @@ -102,15 +110,12 @@ void stopChrono(void) void error(const char *message) { initScreens(); + clearScreens(); drawString("An error has occurred:", 10, 10, COLOR_RED); int posY = drawString(message, 10, 30, COLOR_WHITE); drawString("Press any button to shutdown", 10, posY + 2 * SPACING_Y, COLOR_WHITE); waitInput(); - - flushEntireDCache(); //Ensure that all memory transfers have completed and that the data cache has been flushed - - i2cWriteRegister(I2C_DEV_MCU, 0x20, 1); - while(1); + mcuPowerOff(); } \ No newline at end of file diff --git a/source/utils.h b/source/utils.h index 224eb62..80deb90 100644 --- a/source/utils.h +++ b/source/utils.h @@ -30,6 +30,8 @@ u32 waitInput(void); void mcuReboot(void); +void mcuPowerOff(void); + void chrono(u32 seconds); void stopChrono(void); void error(const char *message); \ No newline at end of file