From 1df26bf3736caabcfe64a50ed2d9b434cbd96910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 5 Mar 2025 12:11:15 +0100 Subject: [PATCH 1/7] feat: change to aes-cbc encryption more fixes move forward fix between --- fossa/100_config.ino | 2 + fossa/101_translations.ino | 4 +- fossa/102_helpers.ino | 4 +- fossa/103_lnurl.ino | 112 ++++++++++++++++--------------------- fossa/105_display.ino | 80 +++++++++++++++++--------- fossa/fossa.ino | 105 +++++++++++++++++++++++----------- 6 files changed, 179 insertions(+), 128 deletions(-) diff --git a/fossa/100_config.ino b/fossa/100_config.ino index e6ed7c0..6fc592c 100644 --- a/fossa/100_config.ino +++ b/fossa/100_config.ino @@ -97,6 +97,8 @@ void setDefaultValues() { Serial.println("Max amount: " + String(maxamount)); maxBeforeReset = MAX_BEFORE_RESET; Serial.println("Max before reset: " + String(maxBeforeReset)); + convertStringToFloatArray(COIN_AMOUNTS, coinAmountFloat); + Serial.println("Coin amounts: " + String(COIN_AMOUNTS)); } void readFiles() { diff --git a/fossa/101_translations.ino b/fossa/101_translations.ino index 5617626..3df0685 100644 --- a/fossa/101_translations.ino +++ b/fossa/101_translations.ino @@ -4,8 +4,8 @@ String translate(String key, String language) { [en] usbT = USB config mode tapScreenT = TAP SCREEN WHEN FINISHED -scanMeT = SCAN ME TAP SCREEN WHEN FINISHED -totalT = Total: +scanMeT = SCAN ME +totalT = Total: fossaT = FOSSA! Bitcoin ATM satsT = SATS forT = FOR diff --git a/fossa/102_helpers.ino b/fossa/102_helpers.ino index 7fc88aa..a692484 100644 --- a/fossa/102_helpers.ino +++ b/fossa/102_helpers.ino @@ -41,7 +41,7 @@ void splitSettings(String str) { } void convertStringToFloatArray(const char* str, float* floatArray) { - char buffer[20]; // Temporary buffer to hold each substring + char buffer[30]; // Temporary buffer to hold each substring int index = 0; // Index for the float array int bufferIndex = 0; // Index for the buffer @@ -55,7 +55,7 @@ void convertStringToFloatArray(const char* str, float* floatArray) { buffer[bufferIndex++] = str[i]; // Copy characters to buffer } } - + // Don't forget to convert the last number in the string buffer[bufferIndex] = '\0'; // Null-terminate the buffer floatArray[index] = atof(buffer); // Convert buffer to float diff --git a/fossa/103_lnurl.ino b/fossa/103_lnurl.ino index dde6ffb..d9f58b6 100644 --- a/fossa/103_lnurl.ino +++ b/fossa/103_lnurl.ino @@ -1,23 +1,53 @@ -//////////////////////////////////////////// -///////////////LNURL STUFF////////////////// -////USING STEPAN SNIGREVS GREAT CRYTPO////// -////////////THANK YOU STEPAN//////////////// -//////////////////////////////////////////// - -void makeLNURL() { - int randomPin = random(1000, 9999); - byte nonce[8]; - for (int i = 0; i < 8; i++) { - nonce[i] = random(256); +void encrypt(const char* key, unsigned char* iv, int length, const char* plainText, unsigned char* outputBuffer){ + if (strlen(key) != 16) { + Serial.println("Key must be 16 bytes long. not " + String(strlen(key)) + " bytes."); + return; } + mbedtls_aes_context aes; + mbedtls_aes_init(&aes); + mbedtls_aes_setkey_enc(&aes, (const unsigned char*)key, strlen(key)*8); + mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, length, iv, (const unsigned char*)plainText, outputBuffer); + mbedtls_aes_free(&aes); +} - byte payload[51]; // 51 bytes is max one can get with xor-encryption - size_t payload_len = xor_encrypt(payload, sizeof(payload), (uint8_t *)secretATM.c_str(), secretATM.length(), nonce, sizeof(nonce), randomPin, float(total)); - String preparedURL = baseURLATM + "?atm=1&p="; - preparedURL += toBase64(payload, payload_len, BASE64_URLSAFE | BASE64_NOPADDING); +bool makeLNURL() { + unsigned char iv_init[16]; + unsigned char iv[16]; + for (int i = 0; i < 16; i++) { + iv[i] = random(0, 255); + iv_init[i] = iv[i]; + } + int randomPin = random(1000, 9999); + String payload = String(randomPin) + String(":") + String(total); + size_t payload_len = payload.length(); + int padding = 16 - (payload_len % 16); + unsigned char encrypted[payload_len + padding] = {0}; + encrypt(secretATM.c_str(), iv, payload_len + padding, payload.c_str(), encrypted); + String preparedURL = baseURLATM + "?p="; + String s; + for (int i = 0; i < sizeof(encrypted); i++) { + s = String(encrypted[i], HEX); + if (s.length() == 1) { + s = "0" + s; + } + preparedURL += s; + } + preparedURL += "&iv="; + for (int i = 0; i < sizeof(iv_init); i++) { + s = String(iv_init[i], HEX); + if (s.length() == 1) { + s = "0" + s; + } + preparedURL += s; + } Serial.println(preparedURL); + lnurl_encode(preparedURL); + return true; +} + +void lnurl_encode(String preparedURL) { char Buf[200]; preparedURL.toCharArray(Buf, 200); char *url = Buf; @@ -27,54 +57,6 @@ void makeLNURL() { char *charLnurl = (char *)calloc(strlen(url) * 2, sizeof(byte)); bech32_encode(charLnurl, "lnurl", data, len); to_upper(charLnurl); - qrData = baseURLATM.substring(0, baseURLATM.length() - 18) + "atm" + "?lightning=" + charLnurl; + qrData = charLnurl; + Serial.println(qrData); } - -int xor_encrypt(uint8_t *output, size_t outlen, uint8_t *key, size_t keylen, uint8_t *nonce, size_t nonce_len, uint64_t pin, uint64_t amount_in_cents) { - // check we have space for all the data: - // - if (outlen < 2 + nonce_len + 1 + lenVarInt(pin) + 1 + lenVarInt(amount_in_cents) + 8) { - return 0; - } - - int cur = 0; - output[cur] = 1; // variant: XOR encryption - cur++; - - // nonce_len | nonce - output[cur] = nonce_len; - cur++; - memcpy(output + cur, nonce, nonce_len); - cur += nonce_len; - - // payload, unxored first - - int payload_len = lenVarInt(pin) + 1 + lenVarInt(amount_in_cents); - output[cur] = (uint8_t)payload_len; - cur++; - uint8_t *payload = output + cur; // pointer to the start of the payload - cur += writeVarInt(pin, output + cur, outlen - cur); // pin code - cur += writeVarInt(amount_in_cents, output + cur, outlen - cur); // amount - cur++; - - // xor it with round key - uint8_t hmacresult[32]; - SHA256 h; - h.beginHMAC(key, keylen); - h.write((uint8_t *)"Round secret:", 13); - h.write(nonce, nonce_len); - h.endHMAC(hmacresult); - for (int i = 0; i < payload_len; i++) { - payload[i] = payload[i] ^ hmacresult[i]; - } - - // add hmac to authenticate - h.beginHMAC(key, keylen); - h.write((uint8_t *)"Data:", 5); - h.write(output, cur); - h.endHMAC(hmacresult); - memcpy(output + cur, hmacresult, 8); - cur += 8; - - // return number of bytes written to the output - return cur; -} \ No newline at end of file diff --git a/fossa/105_display.ino b/fossa/105_display.ino index ad0f7cc..ff429fe 100644 --- a/fossa/105_display.ino +++ b/fossa/105_display.ino @@ -1,66 +1,101 @@ +#ifdef LAYOUT_1 + int horizontalOffset = 480; + int verticalOffset = 40; + int qrPixelSize = 4; + int qrHorizontalOffset = 120; + int qrVerticalOffset = 20; + int qrTextHorizontalOffset = 40; + int qrTextVerticalOffset = 290; +#endif +#ifdef LAYOUT_2 + int horizontalOffset = 240; + int verticalOffset = 20; + int qrPixelSize = 3; + int qrHorizontalOffset = 28; + int qrVerticalOffset = 28; + int qrTextHorizontalOffset = 10; + int qrTextVerticalOffset = 220; +#endif + void printMessage(String text1, String text2, String text3, int ftcolor, int bgcolor) { tft.fillScreen(bgcolor); tft.setTextColor(ftcolor, bgcolor); - tft.setTextSize(4); - tft.setCursor((480 - textWidth(text1, 4)) / 2, 40); + tft.setTextSize(3); + tft.setCursor((horizontalOffset - tft.textWidth(text1)) / 2, verticalOffset); tft.println(text1); - tft.setCursor((480 - textWidth(text2, 4)) / 2, 120); + tft.setCursor((horizontalOffset - tft.textWidth(text2)) / 2, verticalOffset + 60); tft.println(text2); tft.setTextSize(3); - tft.setCursor((480 - textWidth(text3, 3)) / 2, 200); + tft.setCursor((horizontalOffset - tft.textWidth(text3)) / 2, verticalOffset + 140); tft.println(text3); } void feedmefiat() { tft.setTextColor(TFT_WHITE); - tft.setCursor((480 - textWidth(fossaT, 2)) / 2, 40); tft.setTextSize(2); + tft.setCursor((horizontalOffset - tft.textWidth(fossaT)) / 2, verticalOffset); tft.println(fossaT); - tft.setCursor((480 - textWidth(feedT + " " + String(charge) + chargeT, 2)) / 2, 280); - tft.setTextSize(2); - tft.println(feedT + " " + String(charge) + chargeT); +#ifdef LAYOUT_1 + String printFeedT = feedT + " " + String(charge) + chargeT; + tft.setCursor((horizontalOffset - tft.textWidth(printFeedT)) / 2, verticalOffset + 260); + tft.println(printFeedT); +#endif +#ifdef LAYOUT_2 + tft.setCursor((horizontalOffset - tft.textWidth(feedT)) / 2, verticalOffset + 240); + tft.println(feedT); + tft.setCursor((horizontalOffset - tft.textWidth(String(charge) + chargeT)) / 2, verticalOffset + 260); + tft.println(String(charge) + chargeT); +#endif } void feedmefiatloop() { tft.setTextColor(homeScreenColors[homeScreenNumColorCount]); tft.setTextSize(8); - tft.setCursor((480 - textWidth(satsT, 8)) / 2, 80); + tft.setCursor((horizontalOffset - tft.textWidth(satsT)) / 2, verticalOffset + 40); tft.println(satsT); - tft.setCursor((480 - textWidth(forT, 8)) / 2, 140); + tft.setCursor((horizontalOffset - tft.textWidth(forT)) / 2, verticalOffset + 100); tft.println(forT); - tft.setCursor((480 - textWidth(fiatT, 8)) / 2, 200); + tft.setCursor((horizontalOffset - tft.textWidth(fiatT)) / 2, verticalOffset + 160); tft.println(fiatT); delay(100); } -void qrShowCodeLNURL(String message) { +void qrShowCodeLNURL() { #ifdef PRINTER printMessage(printingT, waitT, "", TFT_WHITE, TFT_BLACK); delay(3000); printReceipt(); #endif - Serial.println(qrData); tft.fillScreen(TFT_WHITE); const char *qrDataChar = qrData.c_str(); QRCode qrcoded; - uint8_t qrcodeData[qrcode_getBufferSize(20)]; + uint8_t qrcodeData[qrcode_getBufferSize(30)]; qrcode_initText(&qrcoded, qrcodeData, 11, 0, qrDataChar); for (uint8_t y = 0; y < qrcoded.size; y++) { for (uint8_t x = 0; x < qrcoded.size; x++) { if (qrcode_getModule(&qrcoded, x, y)) { - tft.fillRect(120 + 4 * x, 20 + 4 * y, 4, 4, TFT_BLACK); + tft.fillRect(qrHorizontalOffset + qrPixelSize * x, qrVerticalOffset + qrPixelSize * y, qrPixelSize, qrPixelSize, TFT_BLACK); } else { - tft.fillRect(120 + 4 * x, 20 + 4 * y, 4, 4, TFT_WHITE); + tft.fillRect(qrHorizontalOffset + qrPixelSize * x, qrVerticalOffset + qrPixelSize * y, qrPixelSize, qrPixelSize, TFT_WHITE); } } } - - tft.setCursor(40, 290); - tft.setTextSize(2); tft.setTextColor(TFT_BLACK, TFT_WHITE); - tft.println(message); + tft.setTextSize(2); +#ifdef LAYOUT_1 + tft.setCursor(qrTextHorizontalOffset, qrTextVerticalOffset); + tft.println(scanMeT + " " + tapScreenT); +#endif +#ifdef LAYOUT_2 + tft.setCursor(qrTextHorizontalOffset, qrTextVerticalOffset); + tft.println(scanMeT); + tft.setCursor(qrTextHorizontalOffset, qrTextVerticalOffset + 20); + tft.println(tapScreenT); +#endif + delay(2000); + waitForTap = true; while (waitForTap) { BTNA.read(); @@ -69,8 +104,3 @@ void qrShowCodeLNURL(String message) { } } } - -int textWidth(String text, int textSize) { - tft.setTextSize(textSize); - return tft.textWidth(text); -} diff --git a/fossa/fossa.ino b/fossa/fossa.ino index cf44db8..7a4c1b6 100644 --- a/fossa/fossa.ino +++ b/fossa/fossa.ino @@ -4,28 +4,36 @@ #include #include #include -#include #include #include "qrcoded.h" #include "Bitcoin.h" -#include -#include +#include +#include "mbedtls/aes.h" + #define FORMAT_ON_FAIL true #define PARAM_FILE "/elements.json" -#include +#define VERSION "0.3.0" + +// LAYOUT_1 is horizontal 480x320 +//#define LAYOUT_1 + +// LAYOUT_2 is vertical 240x320 +#define LAYOUT_2 + +//#define BILL_ACCEPTOR +#define BILL_RX 32 // RX Bill acceptor +#define BILL_TX 33 // TX Bill acceptor -//#define PRINTER -#define BILL_ACCEPTOR #define COIN_ACCEPTOR +#define COIN_TX 34 // TX Coinmech +#define COIN_INHIBIT 35 // Coinmech interrupt -#define BTN1 39 // Screen tap button -#define BILL_RX 32 // RX Bill acceptor -#define BILL_TX 33 // TX Bill acceptor -#define COIN_TX 4 // TX Coinmech -#define COIN_INHIBIT 2 // Coinmech -#define PRINTER_RX 22 // RX of the thermal printer -#define PRINTER_TX 23 // TX of the thermal printer +//#define PRINTER +#define PRINTER_RX 22 // RX of the thermal printer +#define PRINTER_TX 23 // TX of the thermal printer + +#define BTN1 21 // Screen tap button // uncomment to use always hardcoded default settings #define HARDCODED @@ -35,7 +43,9 @@ #define CHARGE 10 // % you will charge people for service, set in LNbits extension #define MAX_AMOUNT 30 // max amount per withdraw #define MAX_BEFORE_RESET 300 // max amount you want to sell in the atm before having to reset power -#define DEVICE_STRING "https://XXXX.lnbits.com/fossa/api/v1/lnurl/XXXXX,XXXXXXXXXXXXXXXXXXXXXX,USD" +#define DEVICE_STRING "https://XXXX.lnbits.com/fossa/api/v1/lnurl/XXXXX,XXXXXXXXXXXXXXXX,EUR" +#define COIN_AMOUNTS "0.05,1.0,0.25,0.5,0.1,2.0" +//#define BILL_AMOUNTS "0.01,0.05,0.1,0.25,0.5,1" int billAmountInt[16]; float coinAmountFloat[6]; @@ -86,8 +96,10 @@ Adafruit_Thermal printer(&SerialPrinter); TFT_eSPI tft = TFT_eSPI(TFT_WIDTH, TFT_HEIGHT); Button BTNA(BTN1); + void setup() { Serial.begin(115200); + Serial.println("Welcome to FOSSA, running on version: " + String(VERSION)); Serial.println("TFT: " + String(TFT_WIDTH) + "x" + String(TFT_HEIGHT)); Serial.println("TFT pin MISO: " + String(TFT_MISO)); Serial.println("TFT pin CS: " + String(TFT_CS)); @@ -116,21 +128,31 @@ void setup() { readFiles(); translateAll(language); # endif + BTNA.begin(); FlashFS.begin(FORMAT_ON_FAIL); + tft.init(); +#ifdef LAYOUT_1 tft.setRotation(1); +#endif tft.invertDisplay(false); + + char buf[100]; + sprintf(buf, "TFT (%dx%d) initialized.", TFT_WIDTH, TFT_HEIGHT); + Serial.println(buf); + printMessage("", "Loading..", "", TFT_WHITE, TFT_BLACK); splitSettings(deviceString); #ifdef BILL_ACCEPTOR - SerialBillAcceptor.begin(300, SERIAL_8N2, BILL_TX, BILL_RX); + SerialBillAcceptor.begin(300, SERIAL_8N2, BILL_RX, BILL_TX); Serial.println("Bill Acceptor serial started."); #endif #ifdef COIN_ACCEPTOR + pinMode(COIN_TX, INPUT); + pinMode(COIN_INHIBIT, INPUT); SerialCoinAcceptor.begin(4800, SERIAL_8N1, COIN_TX); - pinMode(COIN_INHIBIT, OUTPUT); Serial.println("Coin Acceptor serial started."); #endif #ifdef PRINTER @@ -141,24 +163,30 @@ void setup() { void loop() { if (maxBeforeResetTally >= maxBeforeReset) { + Serial.println("Max amount reached, please reset power."); printMessage("", tooMuchFiatT, contactOwnerT, TFT_WHITE, TFT_BLACK); delay(100000000); } else { +#ifdef BILL_ACCEPTOR SerialBillAcceptor.write(184); - digitalWrite(COIN_INHIBIT, HIGH); +#endif +#ifdef COIN_ACCEPTOR + digitalWrite(COIN_INHIBIT, LOW); +#endif tft.fillScreen(TFT_BLACK); BTNA.read(); // needed to clear accidental taps moneyTimerFun(); - Serial.println(total); - Serial.println(maxBeforeResetTally); + Serial.println("Total: " + String(total)); + Serial.println("maxBeforeResetTally: " + String(maxBeforeResetTally)); maxBeforeResetTally = maxBeforeResetTally + (total / 100); - Serial.println(maxBeforeResetTally); + Serial.println("maxBeforeResetTally: " + String(maxBeforeResetTally)); makeLNURL(); - qrShowCodeLNURL(scanMeT); + qrShowCodeLNURL(); } } void moneyTimerFun() { + Serial.println("Waiting for money..."); waitForTap = true; coins = 0; bills = 0; @@ -171,10 +199,10 @@ void moneyTimerFun() { feedmefiat(); feedmefiatloop(); } + #ifdef BILL_ACCEPTOR if (SerialBillAcceptor.available()) { int x = SerialBillAcceptor.read(); - for (int i = 0; i < billAmountSize; i++) { if ((i + 1) == x) { bills = bills + billAmountInt[i]; @@ -183,24 +211,25 @@ void moneyTimerFun() { } } } - // Turn off - SerialBillAcceptor.write(185); #endif + #ifdef COIN_ACCEPTOR if (SerialCoinAcceptor.available()) { int x = SerialCoinAcceptor.read(); - printMessage("", "WARNING: print bool", String(x), TFT_WHITE, TFT_BLACK); - delay(3000); - for (int i = 0; i < coinAmountSize; i++) { - if ((i + 1) == x) { - coins = coins + coinAmountFloat[i]; - total = (coins + bills); - printMessage(coinAmountFloat[i] + currencyATM, totalT + String(total) + currencyATM, tapScreenT, TFT_WHITE, TFT_BLACK); - } + //printMessage("", "WARNING: print bool", String(x), TFT_WHITE, TFT_BLACK); + //delay(3000); + for (int i = 0; i < coinAmountSize; i++) { + if (i + 1 == x) { + coins = coins + coinAmountFloat[i]; + total = (coins + bills); + String printCoin = coinAmountFloat[i] + currencyATM; + Serial.println("Coin inserted: " + String(i) + " -> " + printCoin); + String printTotal = totalT + String(total) + currencyATM; + Serial.println(printTotal); + printMessage(printCoin, printTotal, tapScreenT, TFT_WHITE, TFT_BLACK); } + } } - // Turn off - digitalWrite(COIN_INHIBIT, LOW); #endif BTNA.read(); if (BTNA.wasReleased() || total > maxamount) { @@ -209,4 +238,12 @@ void moneyTimerFun() { homeScreenNumColorCount++; } total = (coins + bills) * 100; +#ifdef BILL_ACCEPTOR + // Turn off + SerialBillAcceptor.write(185); +#endif +#ifdef COIN_ACCEPTOR + // Turn off + digitalWrite(COIN_INHIBIT, LOW); +#endif } From 584556d5e7567cb9d4b5612513c922634ecbd0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Sun, 9 Mar 2025 22:36:12 +0100 Subject: [PATCH 2/7] revert layout --- fossa/101_translations.ino | 4 +- fossa/105_display.ino | 80 ++++++++++++-------------------------- fossa/fossa.ino | 8 ---- 3 files changed, 27 insertions(+), 65 deletions(-) diff --git a/fossa/101_translations.ino b/fossa/101_translations.ino index 3df0685..5617626 100644 --- a/fossa/101_translations.ino +++ b/fossa/101_translations.ino @@ -4,8 +4,8 @@ String translate(String key, String language) { [en] usbT = USB config mode tapScreenT = TAP SCREEN WHEN FINISHED -scanMeT = SCAN ME -totalT = Total: +scanMeT = SCAN ME TAP SCREEN WHEN FINISHED +totalT = Total: fossaT = FOSSA! Bitcoin ATM satsT = SATS forT = FOR diff --git a/fossa/105_display.ino b/fossa/105_display.ino index ff429fe..ad0f7cc 100644 --- a/fossa/105_display.ino +++ b/fossa/105_display.ino @@ -1,101 +1,66 @@ -#ifdef LAYOUT_1 - int horizontalOffset = 480; - int verticalOffset = 40; - int qrPixelSize = 4; - int qrHorizontalOffset = 120; - int qrVerticalOffset = 20; - int qrTextHorizontalOffset = 40; - int qrTextVerticalOffset = 290; -#endif -#ifdef LAYOUT_2 - int horizontalOffset = 240; - int verticalOffset = 20; - int qrPixelSize = 3; - int qrHorizontalOffset = 28; - int qrVerticalOffset = 28; - int qrTextHorizontalOffset = 10; - int qrTextVerticalOffset = 220; -#endif - void printMessage(String text1, String text2, String text3, int ftcolor, int bgcolor) { tft.fillScreen(bgcolor); tft.setTextColor(ftcolor, bgcolor); - tft.setTextSize(3); - tft.setCursor((horizontalOffset - tft.textWidth(text1)) / 2, verticalOffset); + tft.setTextSize(4); + tft.setCursor((480 - textWidth(text1, 4)) / 2, 40); tft.println(text1); - tft.setCursor((horizontalOffset - tft.textWidth(text2)) / 2, verticalOffset + 60); + tft.setCursor((480 - textWidth(text2, 4)) / 2, 120); tft.println(text2); tft.setTextSize(3); - tft.setCursor((horizontalOffset - tft.textWidth(text3)) / 2, verticalOffset + 140); + tft.setCursor((480 - textWidth(text3, 3)) / 2, 200); tft.println(text3); } void feedmefiat() { tft.setTextColor(TFT_WHITE); + tft.setCursor((480 - textWidth(fossaT, 2)) / 2, 40); tft.setTextSize(2); - tft.setCursor((horizontalOffset - tft.textWidth(fossaT)) / 2, verticalOffset); tft.println(fossaT); -#ifdef LAYOUT_1 - String printFeedT = feedT + " " + String(charge) + chargeT; - tft.setCursor((horizontalOffset - tft.textWidth(printFeedT)) / 2, verticalOffset + 260); - tft.println(printFeedT); -#endif -#ifdef LAYOUT_2 - tft.setCursor((horizontalOffset - tft.textWidth(feedT)) / 2, verticalOffset + 240); - tft.println(feedT); - tft.setCursor((horizontalOffset - tft.textWidth(String(charge) + chargeT)) / 2, verticalOffset + 260); - tft.println(String(charge) + chargeT); -#endif + tft.setCursor((480 - textWidth(feedT + " " + String(charge) + chargeT, 2)) / 2, 280); + tft.setTextSize(2); + tft.println(feedT + " " + String(charge) + chargeT); } void feedmefiatloop() { tft.setTextColor(homeScreenColors[homeScreenNumColorCount]); tft.setTextSize(8); - tft.setCursor((horizontalOffset - tft.textWidth(satsT)) / 2, verticalOffset + 40); + tft.setCursor((480 - textWidth(satsT, 8)) / 2, 80); tft.println(satsT); - tft.setCursor((horizontalOffset - tft.textWidth(forT)) / 2, verticalOffset + 100); + tft.setCursor((480 - textWidth(forT, 8)) / 2, 140); tft.println(forT); - tft.setCursor((horizontalOffset - tft.textWidth(fiatT)) / 2, verticalOffset + 160); + tft.setCursor((480 - textWidth(fiatT, 8)) / 2, 200); tft.println(fiatT); delay(100); } -void qrShowCodeLNURL() { +void qrShowCodeLNURL(String message) { #ifdef PRINTER printMessage(printingT, waitT, "", TFT_WHITE, TFT_BLACK); delay(3000); printReceipt(); #endif + Serial.println(qrData); tft.fillScreen(TFT_WHITE); const char *qrDataChar = qrData.c_str(); QRCode qrcoded; - uint8_t qrcodeData[qrcode_getBufferSize(30)]; + uint8_t qrcodeData[qrcode_getBufferSize(20)]; qrcode_initText(&qrcoded, qrcodeData, 11, 0, qrDataChar); for (uint8_t y = 0; y < qrcoded.size; y++) { for (uint8_t x = 0; x < qrcoded.size; x++) { if (qrcode_getModule(&qrcoded, x, y)) { - tft.fillRect(qrHorizontalOffset + qrPixelSize * x, qrVerticalOffset + qrPixelSize * y, qrPixelSize, qrPixelSize, TFT_BLACK); + tft.fillRect(120 + 4 * x, 20 + 4 * y, 4, 4, TFT_BLACK); } else { - tft.fillRect(qrHorizontalOffset + qrPixelSize * x, qrVerticalOffset + qrPixelSize * y, qrPixelSize, qrPixelSize, TFT_WHITE); + tft.fillRect(120 + 4 * x, 20 + 4 * y, 4, 4, TFT_WHITE); } } } - tft.setTextColor(TFT_BLACK, TFT_WHITE); - tft.setTextSize(2); -#ifdef LAYOUT_1 - tft.setCursor(qrTextHorizontalOffset, qrTextVerticalOffset); - tft.println(scanMeT + " " + tapScreenT); -#endif -#ifdef LAYOUT_2 - tft.setCursor(qrTextHorizontalOffset, qrTextVerticalOffset); - tft.println(scanMeT); - tft.setCursor(qrTextHorizontalOffset, qrTextVerticalOffset + 20); - tft.println(tapScreenT); -#endif + tft.setCursor(40, 290); + tft.setTextSize(2); + tft.setTextColor(TFT_BLACK, TFT_WHITE); + tft.println(message); delay(2000); - waitForTap = true; while (waitForTap) { BTNA.read(); @@ -104,3 +69,8 @@ void qrShowCodeLNURL() { } } } + +int textWidth(String text, int textSize) { + tft.setTextSize(textSize); + return tft.textWidth(text); +} diff --git a/fossa/fossa.ino b/fossa/fossa.ino index 7a4c1b6..e551f9c 100644 --- a/fossa/fossa.ino +++ b/fossa/fossa.ino @@ -15,12 +15,6 @@ #define VERSION "0.3.0" -// LAYOUT_1 is horizontal 480x320 -//#define LAYOUT_1 - -// LAYOUT_2 is vertical 240x320 -#define LAYOUT_2 - //#define BILL_ACCEPTOR #define BILL_RX 32 // RX Bill acceptor #define BILL_TX 33 // TX Bill acceptor @@ -133,9 +127,7 @@ void setup() { FlashFS.begin(FORMAT_ON_FAIL); tft.init(); -#ifdef LAYOUT_1 tft.setRotation(1); -#endif tft.invertDisplay(false); char buf[100]; From ca72a1f1bb9747c562904e91bdbdb764829fae42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Sun, 9 Mar 2025 22:38:33 +0100 Subject: [PATCH 3/7] fixup! --- fossa/105_display.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fossa/105_display.ino b/fossa/105_display.ino index ad0f7cc..4b9c939 100644 --- a/fossa/105_display.ino +++ b/fossa/105_display.ino @@ -33,7 +33,7 @@ void feedmefiatloop() { delay(100); } -void qrShowCodeLNURL(String message) { +void qrShowCodeLNURL() { #ifdef PRINTER printMessage(printingT, waitT, "", TFT_WHITE, TFT_BLACK); delay(3000); @@ -59,7 +59,7 @@ void qrShowCodeLNURL(String message) { tft.setCursor(40, 290); tft.setTextSize(2); tft.setTextColor(TFT_BLACK, TFT_WHITE); - tft.println(message); + tft.println(scanMeT); delay(2000); waitForTap = true; while (waitForTap) { From bb5992d6b0f68e2664017f3591842a2356636509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 19 Mar 2025 10:07:16 +0100 Subject: [PATCH 4/7] baseurlatm --- fossa/102_helpers.ino | 8 ++++++++ fossa/103_lnurl.ino | 2 +- fossa/fossa.ino | 7 +++++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/fossa/102_helpers.ino b/fossa/102_helpers.ino index a692484..9716a23 100644 --- a/fossa/102_helpers.ino +++ b/fossa/102_helpers.ino @@ -36,8 +36,16 @@ void splitSettings(String str) { int firstComma = str.indexOf(','); int secondComma = str.indexOf(',', firstComma + 1); baseURLATM = str.substring(0, firstComma); + Serial.println("baseURLATM: " + baseURLATM); + // remove /api/v1.... and add /atm?lightning= + int apiPos = baseURLATM.indexOf("api"); + baseUrlAtmPage = baseURLATM.substring(0, apiPos); + baseUrlAtmPage += "atm?lightning="; + Serial.println("baseUrlAtmPage: " + baseUrlAtmPage); secretATM = str.substring(firstComma + 1, secondComma); + Serial.println("secretATM: " + secretATM); currencyATM = str.substring(secondComma + 1); + Serial.println("currencyATM: " + currencyATM); } void convertStringToFloatArray(const char* str, float* floatArray) { diff --git a/fossa/103_lnurl.ino b/fossa/103_lnurl.ino index d9f58b6..7c5a47c 100644 --- a/fossa/103_lnurl.ino +++ b/fossa/103_lnurl.ino @@ -57,6 +57,6 @@ void lnurl_encode(String preparedURL) { char *charLnurl = (char *)calloc(strlen(url) * 2, sizeof(byte)); bech32_encode(charLnurl, "lnurl", data, len); to_upper(charLnurl); - qrData = charLnurl; + qrData = baseUrlAtmPage + charLnurl; Serial.println(qrData); } diff --git a/fossa/fossa.ino b/fossa/fossa.ino index e551f9c..0bf33d2 100644 --- a/fossa/fossa.ino +++ b/fossa/fossa.ino @@ -37,7 +37,8 @@ #define CHARGE 10 // % you will charge people for service, set in LNbits extension #define MAX_AMOUNT 30 // max amount per withdraw #define MAX_BEFORE_RESET 300 // max amount you want to sell in the atm before having to reset power -#define DEVICE_STRING "https://XXXX.lnbits.com/fossa/api/v1/lnurl/XXXXX,XXXXXXXXXXXXXXXX,EUR" +//#define DEVICE_STRING "https://XXXX.lnbits.com/fossa/api/v1/lnurl/XXXXX,XXXXXXXXXXXXXXXX,EUR" +#define DEVICE_STRING "https://test.b1tco1n.org/fossa/api/v1/lnurl/atm/T7HxgedvRJCZVHTm,9rtVvLAsx22hk9W6,USD" #define COIN_AMOUNTS "0.05,1.0,0.25,0.5,0.1,2.0" //#define BILL_AMOUNTS "0.01,0.05,0.1,0.25,0.5,1" @@ -50,6 +51,7 @@ String language; String deviceString; String baseURLATM; +String baseUrlAtmPage; String secretATM; String currencyATM; @@ -123,6 +125,8 @@ void setup() { translateAll(language); # endif + splitSettings(deviceString); + BTNA.begin(); FlashFS.begin(FORMAT_ON_FAIL); @@ -135,7 +139,6 @@ void setup() { Serial.println(buf); printMessage("", "Loading..", "", TFT_WHITE, TFT_BLACK); - splitSettings(deviceString); #ifdef BILL_ACCEPTOR SerialBillAcceptor.begin(300, SERIAL_8N2, BILL_RX, BILL_TX); From 937b3231f11d925bfb8bc37371c4373f024b7103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 19 Mar 2025 10:11:18 +0100 Subject: [PATCH 5/7] use b64 encoding --- fossa/103_lnurl.ino | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/fossa/103_lnurl.ino b/fossa/103_lnurl.ino index 7c5a47c..8cf32e2 100644 --- a/fossa/103_lnurl.ino +++ b/fossa/103_lnurl.ino @@ -12,7 +12,6 @@ void encrypt(const char* key, unsigned char* iv, int length, const char* plainTe bool makeLNURL() { - unsigned char iv_init[16]; unsigned char iv[16]; for (int i = 0; i < 16; i++) { @@ -26,22 +25,9 @@ bool makeLNURL() { unsigned char encrypted[payload_len + padding] = {0}; encrypt(secretATM.c_str(), iv, payload_len + padding, payload.c_str(), encrypted); String preparedURL = baseURLATM + "?p="; - String s; - for (int i = 0; i < sizeof(encrypted); i++) { - s = String(encrypted[i], HEX); - if (s.length() == 1) { - s = "0" + s; - } - preparedURL += s; - } + preparedURL += toBase64(encrypted, payload_len, BASE64_URLSAFE | BASE64_NOPADDING); preparedURL += "&iv="; - for (int i = 0; i < sizeof(iv_init); i++) { - s = String(iv_init[i], HEX); - if (s.length() == 1) { - s = "0" + s; - } - preparedURL += s; - } + preparedURL += toBase64(iv_init, iv_length, BASE64_URLSAFE | BASE64_NOPADDING); Serial.println(preparedURL); lnurl_encode(preparedURL); return true; From dd965a7e71ed897e1986f74245b2f5cafb05e59c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 19 Mar 2025 10:21:39 +0100 Subject: [PATCH 6/7] fixup! --- fossa/103_lnurl.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fossa/103_lnurl.ino b/fossa/103_lnurl.ino index 8cf32e2..67dfe59 100644 --- a/fossa/103_lnurl.ino +++ b/fossa/103_lnurl.ino @@ -12,9 +12,10 @@ void encrypt(const char* key, unsigned char* iv, int length, const char* plainTe bool makeLNURL() { - unsigned char iv_init[16]; - unsigned char iv[16]; - for (int i = 0; i < 16; i++) { + int iv_length = 16; + unsigned char iv_init[iv_length]; + unsigned char iv[iv_length]; + for (int i = 0; i < iv_length; i++) { iv[i] = random(0, 255); iv_init[i] = iv[i]; } From 0f54515a6cff6d7a9b0cb5f97576219967b88612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 28 May 2025 08:48:36 +0200 Subject: [PATCH 7/7] fixup use lnbits aes --- fossa/103_lnurl.ino | 78 +++++++++++++++++++++++++++++++++++---------- fossa/fossa.ino | 3 +- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/fossa/103_lnurl.ino b/fossa/103_lnurl.ino index 67dfe59..042312b 100644 --- a/fossa/103_lnurl.ino +++ b/fossa/103_lnurl.ino @@ -1,34 +1,78 @@ -void encrypt(const char* key, unsigned char* iv, int length, const char* plainText, unsigned char* outputBuffer){ - if (strlen(key) != 16) { - Serial.println("Key must be 16 bytes long. not " + String(strlen(key)) + " bytes."); - return; - } +void encrypt(unsigned char* key, unsigned char* iv, int length, const char* plainText, unsigned char* outputBuffer){ mbedtls_aes_context aes; mbedtls_aes_init(&aes); - mbedtls_aes_setkey_enc(&aes, (const unsigned char*)key, strlen(key)*8); + mbedtls_aes_setkey_enc(&aes, key, 256); // AES-256 requires a 32-byte key mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, length, iv, (const unsigned char*)plainText, outputBuffer); mbedtls_aes_free(&aes); } +void deriveKeyAndIV(const char* secret, unsigned char* salt, unsigned char* outputBuffer) { + mbedtls_md5_context md5_ctx; + unsigned char data[24]; // 16 bytes key + 8 bytes salt + unsigned char md5Output[16]; // 16 bytes for MD5 output + + memcpy(data, secret, 16); + memcpy(data + 16, salt, 8); + + // first iteration + mbedtls_md5_init(&md5_ctx); + mbedtls_md5_update(&md5_ctx, data, sizeof(data)); + mbedtls_md5_finish(&md5_ctx, md5Output); + + // Copy the first 16 bytes to the output buffer for the key + memcpy(outputBuffer, md5Output, 16); + + unsigned char data_md5[16 + 16 + 8]; // 16 bytes md5 output + 16 bytes key + 8 bytes salt + + for (int i = 16; i <= 48; i+=16) { + memcpy(data_md5, md5Output, 16); + memcpy(data_md5 + 16, data, 24); + mbedtls_md5_init(&md5_ctx); + mbedtls_md5_update(&md5_ctx, data_md5, sizeof(data_md5)); + mbedtls_md5_finish(&md5_ctx, md5Output); + // Copy the next 16 bytes to the output buffer + memcpy(outputBuffer + i, md5Output, 16); + } + + mbedtls_md5_free(&md5_ctx); +} + bool makeLNURL() { - int iv_length = 16; - unsigned char iv_init[iv_length]; - unsigned char iv[iv_length]; - for (int i = 0; i < iv_length; i++) { - iv[i] = random(0, 255); - iv_init[i] = iv[i]; + int salt_length = 8; + unsigned char salt[salt_length]; + for (int i = 0; i < salt_length; i++) { + salt[i] = random(0, 255); } + unsigned char keyIV[32 + 16] = {0}; + deriveKeyAndIV(secretATM.c_str(), salt, keyIV); + + unsigned char key[32] = {0}; + unsigned char iv[16] = {0}; + int randomPin = random(1000, 9999); + String payload = String(randomPin) + String(":") + String(total); size_t payload_len = payload.length(); int padding = 16 - (payload_len % 16); - unsigned char encrypted[payload_len + padding] = {0}; - encrypt(secretATM.c_str(), iv, payload_len + padding, payload.c_str(), encrypted); + payload_len += padding; + for (int i = 0; i < padding; i++) { + payload += String((char)padding); + } + + + unsigned char encrypted[payload_len] = {0}; + encrypt(key, iv, payload_len, payload.c_str(), encrypted); + + const unsigned char *saltedChars = (const unsigned char *)"Salted__"; + unsigned char salted[payload_len + 16]; + memcpy(salted, saltedChars, 8); + memcpy(salted + 8, salt, salt_length); + memcpy(salted + 16, encrypted, payload_len); + String preparedURL = baseURLATM + "?p="; - preparedURL += toBase64(encrypted, payload_len, BASE64_URLSAFE | BASE64_NOPADDING); - preparedURL += "&iv="; - preparedURL += toBase64(iv_init, iv_length, BASE64_URLSAFE | BASE64_NOPADDING); + preparedURL += toBase64(salted, payload_len+16, BASE64_URLSAFE); + Serial.println(preparedURL); lnurl_encode(preparedURL); return true; diff --git a/fossa/fossa.ino b/fossa/fossa.ino index 0bf33d2..732dd38 100644 --- a/fossa/fossa.ino +++ b/fossa/fossa.ino @@ -9,11 +9,12 @@ #include "Bitcoin.h" #include #include "mbedtls/aes.h" +#include "mbedtls/md5.h" #define FORMAT_ON_FAIL true #define PARAM_FILE "/elements.json" -#define VERSION "0.3.0" +#define VERSION "1.0.0" //#define BILL_ACCEPTOR #define BILL_RX 32 // RX Bill acceptor