로우레벨 개발자 (FPGA, 어셈블리 + C 레벨), 하이레벨 개발자 (OOP), 네트워크 운영을 거쳐
정보보안으로 전향한 지금
장난감 가지고 만지작거리는데에 흥미를 완전히 잃어버렸다.
간단한 디스플레잉 / 센싱 / 엑츄에이팅은 할줄 아니까
굳이 손도 안대고 대충 만들어 쓰고 잊어버리는 것이
마이크로 컨트롤러 유닛이 되어버렸다.
젊을때는 많이 가지고 놀았지만..
아두이노 프로 마이크로 (레오나르도 동일?)를 가지고
프로젝트를 하나 만들게 되었다.
키보드 에뮬레이션 + 키 제너레이터.
요구사항은 다음과 같다:
0. 키 관리 (생성, 저장, 삭제, 변경)
회사를 가면 개인적인 비밀번호 (특히 애플/구글처럼 삶과 밀접하거나 금융과 관련된 것들)와
회사에서 쓰는 비밀번호를 따로 분리해서 써야한다.
전에 네트워크 운영할 땐 h8thisjob!, sma1-compens(ation)과 같이
아 시발 때려치고싶다, 월급 존나 작네 (내 시간에 대한 컴펜세이션) 등
기억하기 쉬운 문장으로 구성해서 사용했다.
플래시 메모리에 키를 저장하여 전원이 차단되더라도 패스워드 배열을 기억하고 있어야 하고,
랜덤생성 (azAZ09~+) 기능도 있어야 하며 (8자리 이상, 대소 특 숫 조합)
디스플레이를 통해 조회하거나 삭제를 할 수 있어야 한다.
다만... 사이즈가 작아야하기 때문에
그냥 PC 시리얼 포트로 관리할지,
전면에 인터페이스를 구성할지,
아님 IR 리모컨이나 RF443Mhz로 별도 관리 유닛을 둘지는
아직 모르겠다.
어찌됐든, 안전한 키 관리 역시 필요하다.
키 입력 전 사용자를 확인하여야 하고 (분실하더라도 타인 사용 차단)
백업 스테이션에 도킹할 경우 패스워드를 백업/동기화 해야하고 (원문 그대로)
기기 외부에서 접속할 때 해쉬한 비밀번호나 아날로그 조합 등 허가하지 않은 접근을 제한하여야 하고 (안전한 보관)
접근 시도 실패의 경우 특정 횟수 초과시 timeout을 두거나 자기파괴를 수행하여아 하며 (브루트포스 방지)
...
플래쉬 덤프를 떠서 소스코드와 비밀번호 내역을 떠내더라도
내용을 알아보지 못하게 해야하는데...
암호화 (대칭키든 비대칭키든)까지 할 여력이 있을지 모르겠다.
SD카드를 붙이면 되긴 하는데, 그건 상황 보고.
1. 키 입력 (키보드 에뮬레이션)
간단하다.
키보드 사용조건을 충족하게되면
키보드 입력을 후루루루룩 하면 된다.
엔터키까지 넣을지는 그때 상황 봐서 하면 된다.
메뉴 기능을 넣어도 되고.
개발환경
macOS (Apple Silicone) + Arduino IDE
사용자재
- Arduino Micro
- Adafruit LCD (I2C)
...
일단 하드웨어부터 다 작동하도록 하자.
https://www.codrey.com/learn/arduino-pro-micro-primer/
결선을 했는데 반응이 없다.
(샘플 업로드함)
I2C 스캐너부터 돌려야겠다.
#include <Wire.h>
void setup()
{
Wire.begin();
Serial.begin(9600);
while (!Serial); // Leonardo: wait for serial monitor
Serial.println("\nI2C Scanner");
}
void loop()
{
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0)
{
Serial.print("I2C device found at address 0x");
if (address<16)
Serial.print("0");
Serial.print(address,HEX);
Serial.println(" !");
nDevices++;
}
else if (error==4)
{
Serial.print("Unknown error at address 0x");
if (address<16)
Serial.print("0");
Serial.println(address,HEX);
}
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");
delay(5000); // wait 5 seconds for next scan
}
출처 - https://playground.arduino.cc/Main/I2cScanner/
https://thewanderer.tistory.com/43
//#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(2);
display.setCursor(0,0);
display.print("HelloWorld\nPrivietMir");
if(SCREEN_HEIGHT < 33) goto displayThis;
display.setTextSize(1);
display.setCursor(0,32);
display.print("HelloWorld\nPrivietMir");
displayThis:
display.display();
}
void loop() {
static byte number = 0;
display.setCursor(0,32+16); display.setTextColor(SSD1306_BLACK); display.print(number);
display.setCursor(0,32+16); display.setTextColor(SSD1306_WHITE); display.print(++number);
display.display();
delay(100);
}
//저작권 - NSD, thewanderer.tistory.com
출처 - 이 블로그
기초 작업 끝.
Flash에 변수 저장 코드...
EEPROM과 PROGMEM이 다르다는 사실을 발견했다.
보안측면에서, EEPROM과 PROGMEM (인스트럭션 공간에 입력하는 것) 둘 다 문제가 있다.
필자
are there any known case(s) or information of dumping EEPROM or PROGMEM(and operation codes or software itself) and analysing? I need to be prepared for lost case, someone tries to crack it and get the stored password in it.
Chad GPT
Yes, there are well-known techniques for dumping and analyzing the contents of EEPROM, PROGMEM, and even the program code itself (operation codes) from microcontrollers like the ATmega32U4. Being aware of these methods is crucial for designing a secure system. Here’s an overview of the risks and how to mitigate them:
Known Attack Vectors
1. EEPROM Dumping
Attackers may try to dump EEPROM contents directly using:
• Physical Access and ISP Programming:
• The In-System Programming (ISP) interface on microcontrollers allows direct access to EEPROM. Tools like an AVR ISP programmer can dump EEPROM memory.
• Example tool: AVRDUDE is a common utility used for reading/writing AVR microcontroller memory.
• Bootloader Exploits:
• If your microcontroller’s bootloader isn’t secured, attackers can leverage it to read/write memory, including EEPROM.
• Debugging Interfaces:
• Some microcontrollers provide debugging interfaces (e.g., JTAG). If left enabled, these can provide full access to EEPROM.
2. Flash (PROGMEM) Dumping
Attackers may dump program memory to reverse-engineer the operation and extract sensitive data:
• ISP Programming Tools:
• Flash memory (including PROGMEM) can be dumped similarly to EEPROM using tools like AVRDUDE.
• Power Analysis (Side Channel Attack):
• By measuring power consumption during flash operations, attackers may infer stored data.
• Fault Injection Attacks:
• By inducing voltage or clock glitches, attackers can bypass security features and dump flash.
3. Firmware Reverse Engineering
• If an attacker extracts the flash memory (program code), they can decompile it using tools like:
• Ghidra
• IDA Pro
• AVR-specific disassemblers.
• This can reveal operation logic, encryption keys, or obfuscation methods.
How to Mitigate Risks
Here are strategies to protect EEPROM and PROGMEM from being dumped or cracked:
1. Secure the Physical Interface
• Disable ISP/JTAG:
• Fuse bits on AVR microcontrollers can disable programming and debugging interfaces after deployment.
• Example: Lock fuse settings to prevent EEPROM/flash reads.
• avrdude -c usbtiny -p atmega32u4 -U lock:w:0x2F:m
• Use a Secure Enclosure:
• Physically protect the device by making tampering evident (e.g., epoxy coating).
2. Encrypt Sensitive Data
• Encrypt Stored Passwords:
• Use symmetric encryption (e.g., AES) to store passwords in EEPROM or PROGMEM.
• Store the encryption key in a secure location (e.g., hardware fuses or derivable from user input like a PIN).
• Key Derivation:
• Derive the encryption key from a user-provided secret (e.g., a PIN) during authentication. Without the PIN, the stored data is useless.
3. Use Authentication for Access
• Require user authentication (e.g., a PIN or password) before allowing access to sensitive functions.
• Implement brute-force protections:
• Limit retries and lock the device after too many failed attempts.
• Example: Self-destruct the EEPROM data after 3 failed login attempts.
4. Obfuscate Program Logic
• Mix Data and Code:
• Store sensitive data interspersed with program code to make it harder to isolate.
• Use Non-Trivial Key Management:
• Avoid hardcoding encryption keys or secrets directly in the program.
5. Prevent Side Channel Attacks
• Use capacitors and filters to reduce power measurement accuracy during operations.
• Implement constant-time algorithms to prevent timing attacks.
Detection and Mitigation of Cracking Attempts
1. Intrusion Detection:
• Implement an intrusion detection mechanism (e.g., tamper switches) to detect physical access attempts and zero out sensitive memory.
2. Regular Integrity Checks:
• Periodically verify memory contents against a hash to detect tampering.
3. Self-Destruct Mechanism:
• If a tampering attempt is detected, erase EEPROM and flash entirely.
void selfDestruct() {
for (int i = 0; i < EEPROM.length(); i++) {
EEPROM.write(i, 0xFF); // Overwrite with garbage data
}
}
Case Studies
1. Hacking Commercial Devices:
• Many password manager devices (e.g., USB password dongles) have been reverse-engineered through flash and EEPROM dumps.
• Example: The hack of the YubiKey and similar devices involved extracting firmware and decoding the logic.
2. Gaming Consoles:
• EEPROM dumping and flash analysis have been used to crack DRM in gaming systems like PlayStation and Nintendo.
3. IoT Devices:
• Weak or unprotected EEPROM often exposes sensitive credentials (e.g., Wi-Fi passwords, API keys) in IoT devices.
Practical Implementation
Here’s a snippet of how you might store encrypted data in EEPROM:
Storing an Encrypted Password
#include <EEPROM.h>
#include <AESLib.h> // Include an AES library
char key[] = "securekey1234567"; // 16-byte key for AES
void encryptAndStore(const String &password, int address) {
byte encrypted[32]; // Buffer for encrypted data
aes128_enc_single(key, (byte*)password.c_str(), encrypted);
for (int i = 0; i < 32; i++) {
EEPROM.write(address + i, encrypted[i]);
}
}
String readAndDecrypt(int address) {
byte encrypted[32];
char decrypted[32];
for (int i = 0; i < 32; i++) {
encrypted[i] = EEPROM.read(address + i);
}
aes128_dec_single(key, encrypted, (byte*)decrypted);
return String(decrypted);
}
Summary
While dumping EEPROM or PROGMEM is possible with tools and techniques, implementing the following will significantly increase security:
1. Fuse-based interface locking.
2. Encryption of sensitive data (AES is a good choice).
3. Self-destruct mechanisms for tamper detection.
4. Obfuscation and authentication.
If you need help implementing any of these protections, let me know!
흠... 보안 기능을 다 넣으려면 플래시 공간이 모자랄거같은데..
EEPROM으로 비밀번호를 저장해야겠다.
EEPROM dumper
#include <EEPROM.h>
const int ROMlen = EEPROM.length();
const int shift = 4; // Defines the row size as 2^shift bytes (e.g., shift=3 means 2^3 = 8 bytes per row)
void setup() {
int each = 1 << shift;
int eachRow = ROMlen >> shift; // Divide ROMlen by 2^3 (8) using bit-shifting
Serial.begin(9600);
while (!Serial); // Wait for Serial Monitor to connect
Serial.print("Total Size: ");
Serial.print(ROMlen);
Serial.print("\nEEPROM Dump:");
for (int i = 0; i < eachRow; i++)
{
Serial.print("\nLine ");
Serial.print(i);
Serial.print(": ");
for (int j = 0; j < each; j++)
{
byte value = EEPROM.read((i << shift) + j); // Shift i by 3 to multiply by 8
Serial.print(value < 0x10 ? "0" : ""); // Ensure consistent two-digit hex format (e.g., 0F instead of F)
Serial.print(value, HEX);
Serial.print(" ");
}
}
Serial.println("\nEEPROM Dump Complete.\n\n");
}
void loop() {
delay(1000); // Nothing to do here
}
//저작권 - NSD, thewanderer.tistory.com
프로그래밍 계획을 세워야겠구만...
PMBOK을 참고해야하는건가...
8th Dec 2024 (Sun) 끝.
10th Dec 2024 (Tue)
계획 그딴거 없다.
바탐업으로 해야겠다 그냥.
https://docs.arduino.cc/language-reference/en/functions/usb/Keyboard/
//#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#include <AESLib.h>
#include <Keyboard.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define BTN_G 10
#define BTN_R 9
#define sizeOfStr_intrnl 12
void genNewStr(void);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
/* ASCII TABLE (Usable)
NULL 00
! " # $ % & ' ( ) * + , - . / 33-47
0-9 48-57
: ; < = > ? @ 58-64
A-Z 65-90
[ \ ] ^ _ `(backquoter) 91-96
a-z 97-122
{ | } ~ 123-126
~ ! @ # $ % ^ & * ( ) */
/*volatile*/ const byte spclChar[] {126, 33, 64, 35, 36, 37, 94, 38, 42, 40, 41};
//volatile const char presetStr[] = "qwerty12345!@#$%";
/*volatile*/ char ranStr[sizeOfStr_intrnl];
void setup() {
pinMode(BTN_G, INPUT);
pinMode(BTN_R, INPUT);
Keyboard.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
/*
Text size 3 -> 7 characters, ? pixels
Text size 2 -> 10 characters, 16 pixels
Text suze 1 -> 21 characters, 8 pixels
*/
/*
display.setTextSize(2);
display.setCursor(0,0);
//display.print("HelloWorld1\nPrivietMir");
display.print("ABCDE12345");
if(SCREEN_HEIGHT < 33) goto displayThis;
display.setTextSize(1);
display.setCursor(0,16);
display.print("012345678901234567891");
display.setCursor(0,24);
display.setTextSize(3);
display.print("ABCDEFG");
*/
displayThis:
//randomSeed(analogRead(0));
byte tester=0, line=0;
display.setTextSize(1);
while(true){
//genNewStr(tester);
genNewStr();
display.setCursor(0,line);
display.print((char*)ranStr);
tester += sizeOfStr_intrnl;
if(tester > 73) break;
line += 8;
}
display.display();
}
void loop() {
/*
static byte number = 0;
display.setCursor(0,32+16); display.setTextColor(SSD1306_BLACK); display.print(number);
display.setCursor(0,32+16); display.setTextColor(SSD1306_WHITE); display.print(++number);
display.display();
delay(100);
*/
delay(1000);
}
/* ASCII TABLE (Usable)
~-) spclChar[]
0-9 48-57
A-Z 65-90
a-z 97-122
*/
// ~ ! @ # $ % ^ & * ( )
//volatile const byte spclChar[] {126, 33, 64, 35, 36, 37, 94, 38, 42, 40, 41};
void genNewStr(void)
{
byte randNo;
byte dedicatedChar[4];
dedicatedChar[0] = random(sizeOfStr_intrnl);
for (byte i = 1; i < 4; i++)
{
redo:
dedicatedChar[i] = random(sizeOfStr_intrnl);
for (byte former = 0; former < i; former++)
{
if (dedicatedChar[i] == dedicatedChar[former]) goto redo;
}
}
for(byte i = 0; i < sizeOfStr_intrnl; i++)
{
/*
switch(i) // only works with predefined values (determined at the compile)
{
case dedicatedChar[0]: ranStr[i] = spclChar[random(11)]; goto next;
case dedicatedChar[1]: ranStr[i] = random(10) + '0'; goto next;
case dedicatedChar[2]: ranStr[i] = random(26) + 21 + 44; goto next;
case dedicatedChar[3]: ranStr[i] = random(26) + 47 + 50; goto next;
default: randNo = random(73); //0 ~ 72
}*/
//18728 bytes (65%) - 18722 (non-volatile)
///*
if (i == dedicatedChar[0]) { ranStr[i] = spclChar[random(11)]; goto next; }
else if (i == dedicatedChar[1]) { ranStr[i] = random(10) + '0'; goto next; }
else if (i == dedicatedChar[2]) { ranStr[i] = random(26) + 'A'; goto next; }
else if (i == dedicatedChar[3]) { ranStr[i] = random(26) + 'a'; goto next; }
//*/
//18648 bytes (65%) - 18654 (non-volatile)
/*
if (i == dedicatedChar[0]) { randNo = random(11); }
else if (i == dedicatedChar[1]) { randNo = random(10) + 11;}
else if (i == dedicatedChar[2]) { randNo = random(26) + 21;}
else if (i == dedicatedChar[3]) { randNo = random(26) + 47;}
*/
randNo = random(73);
if (randNo < 11) ranStr[i] = spclChar[randNo]; //~-), 11 cases, ranNo = 0 ~ 10
else if (randNo < 21) ranStr[i] = randNo + 37; //0-9, 10 cases, ranNo = 11 ~ 20, '0' - 11, 48 - 11 = 37
else if (randNo < 47) ranStr[i] = randNo + 44; //A-Z, 26 cases, ranNo = 21 ~ 46, 'A' - 21, 65 - 21 = 44
else ranStr[i] = randNo + 50; //a-z, 26 cases, ranNo = 47 ~ 72, 'a' - 47, 97 - 47 = 50}
next:;
}
ranStr[sizeOfStr_intrnl] = '\0'; // Null-terminate for safe display, even though it is oob
return;
}
/*
void genNewStr(byte tester) {
byte randNo = tester;
for (int i = 0; i < sizeOfStr_intrnl; i++) {
if (randNo < 11) ranStr[i] = spclChar[randNo];
else if (randNo < 21) ranStr[i] = randNo + 37;
else if (randNo < 47) ranStr[i] = randNo + 44;
else if (randNo < 73) ranStr[i] = randNo + 50;
else ranStr[i] = 0;
randNo++;
}
ranStr[sizeOfStr_intrnl] = '\0';
return;
}
*/
// 저작권 - NSD, thewanderer.tistory.com
미드 틀어놓고 코드 긁적긁적거리면서
플래시 메모리 (인스트럭션 메모리) 사용량 변화를 측정하였다.
연산량을 줄이려고 goto를 쓰다가
else 랑 비교해보니 else가 명령어 수가 적어 신기했는데
volatile 옵션을 제거했더니 goto버전은 명령어 수가 조금 줄어드는반면 else 버전은 명령어 수가 늘어나는 것을 보았다.
신기하구만.
여튼 각 네가지 (숫자, 영 대소문자, 특수문자)가 반드시 존재하도록 코드를 추가한
랜덤 문자열 생성 코드가 들어갔다.
아 귀찮아지려고 한다..
10th Dec 2024 끝.
11th Dec 2024
버튼 디바운싱을 구현해야하는데 넘 구찮구나.
일단 인터럽트를 쓸건 아니라.
1M ohm 풀다운 저항을 이용하였다.
(인터널 풀업이 있긴 하다만.. 비직관적이다)
Green - change
Red - Fire
메뉴는 뭘로 할까.
0. 랜덤 문자열 생성 후 화면 표출
1. 랜덤 문자열 생성 후 키보드 입력
//#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#include <AESLib.h>
#include <Keyboard.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define BTN_G 10
#define BTN_R 9
#define sizeOfStr_intrnl 10
void genNewStr(void);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
/* ~ ! @ # $ % ^ & * ( ) */
/*volatile*/ const byte spclChar[] {126, 33, 64, 35, 36, 37, 94, 38, 42, 40, 41};
//volatile const char presetStr[] = "qwerty12345!@#$%";
/*volatile*/ char ranStr[sizeOfStr_intrnl+1];
//char* ranStrPtr = ranStr;
byte menu = 0;
void setup() {
pinMode(BTN_G, INPUT);
pinMode(BTN_R, INPUT);
Keyboard.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextColor(SSD1306_WHITE);
disp(3, 1, " ?");
}
void loop() {
static byte timer = 0;
if (digitalRead(BTN_G)){
timer = 0;
if(++menu >= 2) menu = 0;
disp(2, 2, (menu) ? "Rnd Keybrd" : "Rnd Dsply");
} else if (digitalRead(BTN_R)) {
timer = 0;
switch (menu) {
case 0: randDisp(); break;
case 1: randKeybd(); break;
default : menu = 0; //bit error mitigation
}
} else { if(++timer >= 30) clsDis(); }
delay(100);
}
void randDisp(void)
{
genNewStr();
disp(2, 1, (char*)ranStr);
return;
}
void randKeybd(void)
{
randDisp();
Keyboard.print((char*)ranStr);
return;
}
void genNewStr(void)
{
byte randNo, dedicatedChar[4];
dedicatedChar[0] = random(sizeOfStr_intrnl);
for (byte i = 1; i < 4; i++)
{
redo:
dedicatedChar[i] = random(sizeOfStr_intrnl);
for (byte former = 0; former < i; former++)
{
if (dedicatedChar[i] == dedicatedChar[former]) goto redo;
}
}
for(byte i = 0; i < sizeOfStr_intrnl; i++)
{
/*18728 bytes (65%) - 18722 (non-volatile)*/ ///*
if (i == dedicatedChar[0]) { ranStr[i] = spclChar[random(11)]; goto next; }
else if (i == dedicatedChar[1]) { ranStr[i] = random(10) + '0'; goto next; }
else if (i == dedicatedChar[2]) { ranStr[i] = random(26) + 'A'; goto next; }
else if (i == dedicatedChar[3]) { ranStr[i] = random(26) + 'a'; goto next; } //*/
/*18648 bytes (65%) - 18654 (non-volatile)*/ /*
if (i == dedicatedChar[0]) { randNo = random(11); }
else if (i == dedicatedChar[1]) { randNo = random(10) + 11;}
else if (i == dedicatedChar[2]) { randNo = random(26) + 21;}
else if (i == dedicatedChar[3]) { randNo = random(26) + 47;} */
randNo = random(73);
if (randNo < 11) ranStr[i] = spclChar[randNo]; //~-), 11 cases, ranNo = 0 ~ 10
else if (randNo < 21) ranStr[i] = randNo + 37; //0-9, 10 cases, ranNo = 11 ~ 20, '0' - 11, 48 - 11 = 37
else if (randNo < 47) ranStr[i] = randNo + 44; //A-Z, 26 cases, ranNo = 21 ~ 46, 'A' - 21, 65 - 21 = 44
else ranStr[i] = randNo + 50; //a-z, 26 cases, ranNo = 47 ~ 72, 'a' - 47, 97 - 47 = 50}
next:;
}
ranStr[sizeOfStr_intrnl] = '\0'; // Null-terminate for safe display, even though it is oob
return;
}
void clsDis(void)
{
display.clearDisplay();
display.display();
return;
}
void disp(byte txtSize, byte line, const char* strPtr)
{/* Text size 3 -> 7 characters, ? pixels
Text size 2 -> 10 characters, 16 pixels
Text size 1 -> 21 characters, 8 pixels */
display.clearDisplay();
display.setTextSize(txtSize);
display.setCursor(0, txtSize * 8 * line);
display.print((char*)strPtr);
display.display();
return;
}
//저작권 - NSD, thewanderer.tistory.com
sm&9vid!)G
akBZ2iB@ES
ㅜ!ㅠㅖㅋ0삔0
Good. 잘 되네.
한글로 입력이 되는 문제는 있음.
(운영체제에서 영문으로 변환해서 하긴 할텐데)
디폴트가 Keyboard.begin(KeyboardLayout_en_US); 이라
의미가 없음. 여튼.
다음은 EEPROM...?
EEPROM은 1KB,
16 바이트씩 채우면 64개의 문자열 저장할 수 있음.
-> AES 암호화시 IV를 랜덤 생성하면 안전하고,
IV는 비밀일 필요가 없어서
비밀번호와 같이 저장.
이 경우 32개의 패스워드 저장 가능.
일단 디스플레잉 하는 부분만.
AES 암복호화 기능 추가 코드
(동일 IV 테스트 코드)
//#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#include <AESLib.h>
#include <Keyboard.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define BTN_G 10
#define BTN_R 9
#define MENU_LEN 4
#define StrSize 16
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
AESLib AES;
/* ~ ! @ # $ % ^ & * ( ) */
/*volatile*/ const byte spclChar[] {126, 33, 64, 35, 36, 37, 94, 38, 42, 40, 41};
/*volatile*/ char ranStr[StrSize + 1];
byte ranStr_Enc[StrSize + 1];
byte menu = 0;
/* struct something{
type name;
} */
const byte aes_key[16] = { // 16-byte AES key (128 bits)
0x2b, 0x7e, 0x15, 0x16,
0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0xcf, 0xf4,
0x21, 0x62, 0x45, 0x6b
};
const byte aes_iv[16] = { // 16-byte initialization vector (IV)
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f
};
void setup() {
pinMode(BTN_G, INPUT);
pinMode(BTN_R, INPUT);
Keyboard.begin(KeyboardLayout_en_US);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextColor(SSD1306_WHITE);
disp(3, 1, " ?");
}
void loop() {
static byte timer = 0;
if (digitalRead(BTN_G)){
timer = 0;
//if(++menu >= MENU_LEN) menu = 0; //circle back
switch(++menu){
case 0: disp(2, 2, "Rnd Keybrd"); break;
case 1: disp(2, 2, "Rnd Dsplay"); break;
case 2: disp(2, 2, "Rnd Encrpt"); break;
case 3: disp(2, 2, "Rnd Decrpt"); break;
default : menu = 0;
}
} else if (digitalRead(BTN_R)) {
timer = 0;
switch (menu) {
case 0: randDisp(); break;
case 1: randKeybd(); break;
case 2: randEncrpt(); break;
case 3: randDecrpt(); break;
default : menu = 0; //bit error mitigation
}
} else { if(++timer >= 30) clsDis(); }
delay(100);
}
void randDisp(void)
{
genNewStr();
disp(2, 1, (char*)ranStr);
return;
}
void randKeybd(void)
{
randDisp();
Keyboard.print((char*)ranStr);
return;
}
void randEncrpt(void)
{
AES_ENC((byte*)ranStr, (byte*)ranStr_Enc);
disp(2, 1, (char*)ranStr_Enc);
return;
}
void randDecrpt(void)
{
AES_DEC((byte*)ranStr_Enc, (byte*)ranStr);
disp(2, 1, (char*)ranStr);
return;
}
void genNewStr(void)
{
byte randNo, dedicatedChar[4];
dedicatedChar[0] = random(StrSize);
for (byte i = 1; i < 4; i++)
{
redo:
dedicatedChar[i] = random(StrSize);
for (byte former = 0; former < i; former++)
{
if (dedicatedChar[i] == dedicatedChar[former]) goto redo;
}
}
for(byte i = 0; i < StrSize; i++)
{
/*18728 bytes (65%) - 18722 (non-volatile)*/ ///*
if (i == dedicatedChar[0]) { ranStr[i] = spclChar[random(11)]; goto next; }
else if (i == dedicatedChar[1]) { ranStr[i] = random(10) + '0'; goto next; }
else if (i == dedicatedChar[2]) { ranStr[i] = random(26) + 'A'; goto next; }
else if (i == dedicatedChar[3]) { ranStr[i] = random(26) + 'a'; goto next; } //*/
/*18648 bytes (65%) - 18654 (non-volatile)*/ /*
if (i == dedicatedChar[0]) { randNo = random(11); }
else if (i == dedicatedChar[1]) { randNo = random(10) + 11;}
else if (i == dedicatedChar[2]) { randNo = random(26) + 21;}
else if (i == dedicatedChar[3]) { randNo = random(26) + 47;} */
randNo = random(73);
if (randNo < 11) ranStr[i] = spclChar[randNo]; //~-), 11 cases, ranNo = 0 ~ 10
else if (randNo < 21) ranStr[i] = randNo + 37; //0-9, 10 cases, ranNo = 11 ~ 20, '0' - 11, 48 - 11 = 37
else if (randNo < 47) ranStr[i] = randNo + 44; //A-Z, 26 cases, ranNo = 21 ~ 46, 'A' - 21, 65 - 21 = 44
else ranStr[i] = randNo + 50; //a-z, 26 cases, ranNo = 47 ~ 72, 'a' - 47, 97 - 47 = 50}
next:;
}
ranStr[StrSize] = '\0'; // Null-terminate for safe display, even though it is oob
return;
}
void clsDis(void)
{
display.clearDisplay();
display.display();
return;
}
void disp(byte txtSize, byte line, const char* strPtr)
{/* Text size 3 -> 7 characters, ? pixels
Text size 2 -> 10 characters, 16 pixels
Text size 1 -> 21 characters, 8 pixels */
display.clearDisplay();
display.setTextSize(txtSize);
display.setCursor(0, txtSize * 8 * line);
display.print((char*)strPtr);
display.display();
return;
}
void EEPROM_READ(byte rowNo, char* buffer){
unsigned int startAddr = rowNo * 16; //startAddr: 0-1024
for (byte i = 0; i < 16; i++) { buffer[i] = EEPROM.read(startAddr + i); } return;
}
void EEPROM_DELETE(byte rowNo)
{
byte empty[16];
memset(empty, 0xFF, 16); // Fill the array with 0xFF
EEPROM_UPDATE(rowNo, empty);
return;
}
void EEPROM_UPDATE(byte rowNo, const char* str) //LENGTH OF str MUST BE 16 BYTEs
{
unsigned int startAddr = rowNo * 16; //startAddr: 0-1024
for (byte i = 0; i < 16; i++) { EEPROM.update(startAddr + i, str[i]); }
return;
}
void EEPROM_DISTROY(void)
{
for(byte i = 0; i < 64; i++)
{
EEPROM_DELETE(i);
}
return;
}
void AES_ENC(byte* plain_text, byte* encrypted_text) {
byte iv[16]; // Temporary IV
memcpy(iv, aes_iv, 16); // Reset IV before encryption
AES.encrypt(plain_text, 16, encrypted_text, aes_key, 128, iv); return;
}
void AES_DEC(byte* encrypted_text, byte* plain_text) {
byte iv[16]; // Temporary IV
memcpy(iv, aes_iv, 16); // Reset IV before encryption
AES.decrypt(encrypted_text, 16, plain_text, aes_key, 128, iv); return;
}
//출처 - NSD, thewanderer.tistory.com
// Copyright, right?
아...
레이블이랑 레코드 스테이터스 정보도 저장해야하는구나 ㅜㅜ
EEPROM을 16바이트 단위로 잘랐을 경우
64개의 레코드 저장 가능.
Struct eachRecord{
byte iv[16];
byte data[16];
byte meta[16];
}
이렇게 하면 한 객체당 3 레코드 소모 (21개 객체 저장 + 1줄 여분)
아님 8 바이트씩 끊어도 되고.
그럼 2.5줄
2개 레코드에 5줄 -> 12 * 2개 객체 (24객체) + 1객체 = 25객체 (여분 1.5레코드)
객체당 3레코드 먹이는걸로 결정.
젠장 EEPROM 이미 입혔는데..
다시 입혀야겠다.
#include <EEPROM.h>
const int ROMlen = EEPROM.length();
const int shift = 4; // Defines the row size as 2^shift bytes (e.g., shift=3 means 2^3 = 8 bytes per row)
const byte predefined_ivs[21][16] = {
{0x12,... 0x0A},
...
{0x...}
};
void setup() {
int each = 1 << shift;
int eachRow = ROMlen >> shift; // Divide ROMlen by 2^3 (8) using bit-shifting
Serial.begin(9600);
while (!Serial); // Wait for Serial Monitor to connect
EEPROM_DISTROY();
Serial.print("Writing EEPROM\n\n");
for (int i=0;i<21;i++){
EEPROM_UPDATE(i*3, predefined_ivs[i]);
}
Serial.print("Job's finished\n");
Serial.print("Total Size: ");
Serial.print(ROMlen);
Serial.print("\nEEPROM Dump:");
for (int i = 0; i < eachRow; i++)
{
Serial.print("\nLine ");
Serial.print(i);
Serial.print(": ");
for (int j = 0; j < each; j++)
{
byte value = EEPROM.read((i << shift) + j); // Shift i by 3 to multiply by 8
Serial.print(value < 0x10 ? "0" : ""); // Ensure consistent two-digit hex format (e.g., 0F instead of F)
Serial.print(value, HEX);
Serial.print(" ");
}
}
Serial.println("\nEEPROM Dump Complete.\n\n");
}
void loop() {
// put your main code here, to run repeatedly:
delay(1000);
}
void EEPROM_READ(byte rowNo, char* buffer){
unsigned int startAddr = rowNo * 16; //startAddr: 0-1024
for (byte i = 0; i < 16; i++) { buffer[i] = EEPROM.read(startAddr + i); } return;
}
void EEPROM_DELETE(byte rowNo)
{
byte empty[16];
memset(empty, 0xFF, 16); // Fill the array with 0xFF
EEPROM_UPDATE(rowNo, empty);
return;
}
void EEPROM_UPDATE(byte rowNo, const char* str) //LENGTH OF str MUST BE 16 BYTEs
{
unsigned int startAddr = rowNo * 16; //startAddr: 0-1024
for (byte i = 0; i < 16; i++) { EEPROM.update(startAddr + i, str[i]); }
return;
}
void EEPROM_DISTROY(void)
{
for (unsigned int addr = 0; addr < EEPROM.length(); addr++) {
EEPROM.update(addr, 0xFF); // Clear byte-by-byte
}
return;
}
//Copyright to NSD, thewanderer.tistory.com
시리얼 통신부도 구현을 해야겠다.
여기부터는 검열을 심하게 해서 업로드해야하므로..
시간이 오래 걸린다.
(안그러면 내부 구조가 보여서 습득시 해킹에 활용될 수 있음)
이제 시리얼 매니지먼트 끝냈고.
객체 관리 프로그램 짜고
메뉴 구성이랑 UI 개편하고
Auth 개발해야겠다.
11th Dec 2024 끝.
12th Dec 2024
윤통이 계엄 이후로
한국 사람들에게 전투를 한 번 더 포고했다.
국민들이랑 싸워서 이나라를 완전히 부수겠다는데...
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
근데 뽑을애도 없고...
걍 빨리 한국을 버리는게 답이긴 함.
수신제가박사탈조선
(修身齊家 博士 脫㡽鮮) - 조선(朝鮮)에서 조를 비천할 조로 바꿈
(몸과 마음을 닦아 집안을 가지런하게 하며 박사학위를 따서 외국으로 이민간다)
덤인줄 당이든 계엄의 힘이든... 다 병신임.
전엔 안그랬는데 (지가 여당이라 그랬나) 지금은 똥루리 계엄의힘이랑 하는짓이 똑같음.
차라리 꾹이형 당은 지 잇속챙기는거 보이지라도 않았지..
뽑을 인간이 없다.
씨발 탈조선만이 답이다.
뽑아먹을거만 뽑아먹고.
여튼. 돌아와서.
12th Dec 2024 끝.
14th Dec 2024
윤통 탄핵소츄 통과 축축
대통령 욕만 하고 끝났다. 12일은.
Auth 메커니즘을 생각해봤는데
리스트는 아래와 같다.
- 저항 기반 키
auth key made out of PCB with analog resistors on it. Plug it in, then arduino will read each values of connected pins via analog read (since the key's interface have VCC and GND from arduino, thus each resisters will have unique voltages on them) and check. Arduino will tolerate the error or voltage change, since I might make few keys with same reading resisters or connection may degrade or so then the reading might be off from the first few times.
→ Easy to use (plug then done), small enough to carry arround, easy to make multiple keys, hard to reverse engineer if properly obfuscated, not able to brute force (based on What you Have, not what you know), if lost (the key) then easily changable, but need to print the housing with plug in port and metal pins.
- 포텐시오미터 기반 아날로그 금고
One Additional rotational variable resistor (forgot the name, ones used in guitar bodies or pedals, potentiometer that it is), and acts as analog safe. Rotate it into specific positions in a right sequence, then arduino will auth.
→ Will look standing out but still hard to crack unless someone dump the code, still going to make it hard to reverse engineer. Also slow to use, since you need to set the nob in correct positions.
- 디지털 토큰 용 아두이노
Another arduino with digital authentification functionality, acting as a security token
→ Need to carry multiple arduinos and use them, inconvinient and higher chance of failure.
- RFID 인증
Adding RFID reader and tagging
→ safe option but too bulky to carry around, Already running out of flash thus not viable option for additional codes.
지피티의 추가 의견
- Capacitive Touch Pattern
• Use a capacitive touch sensor to detect a unique pattern (e.g., tap sequence or finger placement).
• Pros:
• Compact, no additional hardware to carry.
• Unique patterns are hard to brute force.
• Cons:
• Requires precise touch handling code.
• Less intuitive for users compared to physical keys or knobs.
- Magnetic Hall Effect Sensor (나도 잠깐 생각해봤음)
• Use a small magnet that must be placed in a specific orientation or position near the Arduino to authenticate.
• Pros:
• Compact and lightweight.
• Hard to guess the correct position and orientation.
• Cons:
• Requires precision placement.
• Prone to wear if mechanical alignment is involved.
- Optical Authentication (구현하기 빡셈)
• Use an optical sensor (e.g., a photodiode) and a unique pattern of light (e.g., from an LED keychain) to authenticate.
• Pros:
• Completely contactless.
• Hard to clone without the correct light pattern or sequence.
• Cons:
• Requires an external light source.
• Sensitive to environmental lighting conditions.
터치 센서 맘에 드는걸.
아예 메트릭스나 I2C로 키패드 (접점스위치-푸쉬버튼식, 커패시턴스 터치식)를 판다.
그냥 기성제품 사다 쓰는게 편하긴 하겠구만.
하우징도 고려해야 하는디...
고민이다.
에폭시 vs 3d print vs 하이브리드 (both)
세개 옵션이 있다.
그냥 RFID로 갈까..?
그럼 PN523를 또 사야된다.
(지금 RC522밖에 없음. I2C 미지원 + 3.3V VCC)
14th Dec 2024 끝.
끝. End of Document.
'1.B. Low Level Engineering > Micro Controller Unit' 카테고리의 다른 글
[Arduino] 더이상의 포스팅은 없습니다. (0) | 2023.03.27 |
---|---|
[MCU] P3. 적외선 리모컨 사용하기 (수신, 발신) (0) | 2023.03.21 |
[MCU] P2. PWM 신호 만들기 (LED 밝기조절, 피에조, 모터 신호...) (0) | 2023.03.06 |
[MCU] P1. I2C 디스플레이 연결하기 (1602A LCD + Adafruit OLED) (0) | 2023.02.15 |
[MCU] T1.1 (정수) 데이터란 Intro. to Datum (0) | 2023.02.03 |
[MCU] P0. IDE & 드라이버 설치, 예제 (0) | 2023.01.31 |
[MCU] T0. 아두이노 개론. (0) | 2023.01.31 |
Comment(s)