From 76e37a8b970a8fd3195a6ca57573ba1e11db4754 Mon Sep 17 00:00:00 2001 From: Nicolas Burtey Date: Sun, 17 Sep 2023 19:36:09 +0100 Subject: [PATCH] test: make e2e tests work --- apps/boltcard/TODO.txt | 1 + apps/boltcard/app/api/ln/route.ts | 4 +- apps/boltcard/app/crypto/aes.ts | 39 +++++----- .../boltcard/app/{ => crypto}/decoder.spec.ts | 54 +++++++++++++- apps/boltcard/app/crypto/decoder.ts | 73 +++++++++++++++++++ apps/boltcard/app/decoder.ts | 23 ------ apps/boltcard/bats/e2e-test.bats | 26 +++++-- apps/boltcard/debug/getpandc.ts | 34 +++++++++ apps/boltcard/knexfile.ts | 10 --- 9 files changed, 195 insertions(+), 69 deletions(-) rename apps/boltcard/app/{ => crypto}/decoder.spec.ts (57%) create mode 100644 apps/boltcard/app/crypto/decoder.ts delete mode 100644 apps/boltcard/app/decoder.ts create mode 100644 apps/boltcard/debug/getpandc.ts delete mode 100644 apps/boltcard/knexfile.ts diff --git a/apps/boltcard/TODO.txt b/apps/boltcard/TODO.txt index 566422e4c01..cddd8f4734e 100644 --- a/apps/boltcard/TODO.txt +++ b/apps/boltcard/TODO.txt @@ -1,3 +1,4 @@ - rate limits +- passing IP to backend - proper link of .gql (instead of copy) - Tilt setup \ No newline at end of file diff --git a/apps/boltcard/app/api/ln/route.ts b/apps/boltcard/app/api/ln/route.ts index 5ab6e6fa230..3c9473bf054 100644 --- a/apps/boltcard/app/api/ln/route.ts +++ b/apps/boltcard/app/api/ln/route.ts @@ -15,7 +15,7 @@ import { markCardInitAsUsed, } from "../../knex" -import { decryptedPToUidCtr } from "../../decoder" +import { decodePToUidCtr } from "../../crypto/decoder" function generateSecureRandomString(length: number): string { return randomBytes(Math.ceil(length / 2)) @@ -88,7 +88,7 @@ export async function GET(req: NextRequest) { } // TODO error management - const { uidRaw, uid, ctrRawInverseBytes, ctr } = decryptedPToUidCtr(decryptedP) + const { uidRaw, uid, ctrRawInverseBytes, ctr } = decodePToUidCtr(decryptedP) console.log({ uid, diff --git a/apps/boltcard/app/crypto/aes.ts b/apps/boltcard/app/crypto/aes.ts index b49e4ce62d9..ac47ee59b59 100644 --- a/apps/boltcard/app/crypto/aes.ts +++ b/apps/boltcard/app/crypto/aes.ts @@ -1,4 +1,5 @@ -// import { AesCmac } from "aes-cmac" +import { createSV2 } from "./decoder" + const aesCmac = require("node-aes-cmac").aesCmac const aesjs = require("aes-js") @@ -23,35 +24,29 @@ export function aesDecrypt(key: Buffer, data: Buffer): Buffer | DecryptionError } } +// used for simulating the coldcard +export function aesEncrypt(key: Buffer, data: Buffer): Buffer | DecryptionError { + try { + const aesCtr = new aesjs.ModeOfOperation.cbc(key) + const decryptedBytes = aesCtr.encrypt(data) + return decryptedBytes + } catch (err) { + console.log(err) + if (err instanceof Error) return new DecryptionError(err) + return new UnknownError() + } +} + export async function checkSignature( uid: Uint8Array, ctr: Uint8Array, k2CmacKey: Buffer, cmac: Buffer, ): Promise { - const sv2 = Buffer.from([ - 0x3c, - 0xc3, - 0x00, - 0x01, - 0x00, - 0x80, - uid[0], - uid[1], - uid[2], - uid[3], - uid[4], - uid[5], - uid[6], - ctr[0], - ctr[1], - ctr[2], - ]) - let calculatedCmac try { - calculatedCmac = getSunMAC(k2CmacKey, sv2) + calculatedCmac = getSunMAC(k2CmacKey, createSV2(uid, ctr)) } catch (error) { console.error(error) throw new Error("issue with cMac") @@ -61,7 +56,7 @@ export async function checkSignature( return Buffer.compare(calculatedCmac, cmac) === 0 } -function getSunMAC(key: Buffer, sv2: Buffer): Buffer { +export function getSunMAC(key: Buffer, sv2: Buffer): Buffer { const options = { returnAsBuffer: true } const cmac1 = aesCmac(key, sv2, options) const cmac2 = aesCmac(cmac1, new Buffer(""), options) diff --git a/apps/boltcard/app/decoder.spec.ts b/apps/boltcard/app/crypto/decoder.spec.ts similarity index 57% rename from apps/boltcard/app/decoder.spec.ts rename to apps/boltcard/app/crypto/decoder.spec.ts index 9ef8ae3fd74..fd7cb068721 100644 --- a/apps/boltcard/app/decoder.spec.ts +++ b/apps/boltcard/app/crypto/decoder.spec.ts @@ -1,5 +1,6 @@ -import { aesDecrypt, checkSignature } from "../boltcard-galoy/app/crypto/aes" -import { decryptedPToUidCtr } from "./decoder" +import { aesDecrypt, aesEncrypt, checkSignature, getSunMAC } from "./aes" + +import { createSV2, decodePToUidCtr, encodeUidCtrToP } from "./decoder" const aesjs = require("aes-js") @@ -12,6 +13,7 @@ const values = [ decrypted_uid: "04996c6a926980", decrypted_ctr: "030000", decoded_ctr: 3, + sv2: new Buffer([60, 195, 0, 1, 0, 128, 4, 153, 108, 106, 146, 105, 128, 3, 0, 0]), }, { p: aesjs.utils.hex.toBytes("00F48C4F8E386DED06BCDC78FA92E2FE"), @@ -33,7 +35,7 @@ const values = [ }, ] -describe("crypto", () => { +describe("decoding", () => { values.forEach( ({ p, @@ -50,7 +52,7 @@ describe("crypto", () => { throw decryptedP } - const { uid, uidRaw, ctr, ctrRawInverseBytes } = decryptedPToUidCtr(decryptedP) + const { uid, uidRaw, ctr, ctrRawInverseBytes } = decodePToUidCtr(decryptedP) expect(uid).toEqual(decrypted_uid) expect(ctr).toEqual(decoded_ctr) @@ -69,3 +71,47 @@ describe("crypto", () => { }, ) }) + +function uint8ArrayToHex(uint8Array: Uint8Array) { + return Array.from(uint8Array) + .map((byte) => byte.toString(16).padStart(2, "0")) + .join("") +} + +describe("encoding", () => { + it("should encrypt uid and ctr", () => { + const value = values[0] + + const uid = Buffer.from(value.decrypted_uid, "hex") + const ctr = Buffer.from(value.decrypted_ctr, "hex") + + // create P + const p1 = encodeUidCtrToP(uid, ctr) + const p2 = Buffer.from(p1) + + // encrypt P + const encryptP = aesEncrypt(value.aes_decrypt_key, p2) + if (encryptP instanceof Error) { + throw encryptP + } + + expect(uint8ArrayToHex(encryptP)).toEqual(uint8ArrayToHex(value.p)) + }) + + it("should sign P", () => { + const value = values[0] + const p = value.p + p + + const uid = Buffer.from(value.decrypted_uid, "hex") + const ctr = Buffer.from(value.decrypted_ctr, "hex") + + // create cv2 + const cv2 = createSV2(uid, ctr) + expect(cv2).toEqual(value.sv2) + + // create cmac + const cmac = getSunMAC(value.aes_cmac_key, cv2) + expect(cmac).toEqual(value.c) + }) +}) diff --git a/apps/boltcard/app/crypto/decoder.ts b/apps/boltcard/app/crypto/decoder.ts new file mode 100644 index 00000000000..38d95e2779a --- /dev/null +++ b/apps/boltcard/app/crypto/decoder.ts @@ -0,0 +1,73 @@ +const aesjs = require("aes-js") + +// used for encryption +export const decodePToUidCtr = ( + decryptedP: Uint8Array, +): { uid: string; uidRaw: Uint8Array; ctr: number; ctrRawInverseBytes: Uint8Array } => { + if (decryptedP[0] !== 0xc7) { + throw new Error("data not starting with 0xC7") + } + + const uidRaw = decryptedP.slice(1, 8) + const uid = aesjs.utils.hex.fromBytes(uidRaw) + + const ctrRawInverseBytes = decryptedP.slice(8, 11) + const ctr = + (ctrRawInverseBytes[2] << 16) | (ctrRawInverseBytes[1] << 8) | ctrRawInverseBytes[0] + + return { + uid, + uidRaw, + ctr, + ctrRawInverseBytes, + } +} + +// only used to simulate the cold card +export const encodeUidCtrToP = (uid: Buffer, ctr: Buffer): Uint8Array => { + return new Uint8Array([ + 0xc7, + uid[0], + uid[1], + uid[2], + uid[3], + uid[4], + uid[5], + uid[6], + ctr[0], + ctr[1], + ctr[2], + + // those value can be random, but they are set as is so that + // tests pass for the simulation of the encryption fo the coldcard + 2, + 63, + 181, + 243, + 74, + ]) +} + +// used for signature +export const createSV2 = (uid: Uint8Array, ctr: Uint8Array) => { + const sv2 = Buffer.from([ + 0x3c, + 0xc3, + 0x00, + 0x01, + 0x00, + 0x80, + uid[0], + uid[1], + uid[2], + uid[3], + uid[4], + uid[5], + uid[6], + ctr[0], + ctr[1], + ctr[2], + ]) + + return sv2 +} diff --git a/apps/boltcard/app/decoder.ts b/apps/boltcard/app/decoder.ts deleted file mode 100644 index 19c666d222a..00000000000 --- a/apps/boltcard/app/decoder.ts +++ /dev/null @@ -1,23 +0,0 @@ -const aesjs = require("aes-js") - -export const decryptedPToUidCtr = ( - decryptedP: Uint8Array, -): { uid: string; uidRaw: Uint8Array; ctr: number; ctrRawInverseBytes: Uint8Array } => { - if (decryptedP[0] !== 0xc7) { - throw new Error("data not starting with 0xC7") - } - - const uidRaw = decryptedP.slice(1, 8) - const uid = aesjs.utils.hex.fromBytes(uidRaw) - - const ctrRawInverseBytes = decryptedP.slice(8, 11) - const ctr = - (ctrRawInverseBytes[2] << 16) | (ctrRawInverseBytes[1] << 8) | ctrRawInverseBytes[0] - - return { - uid, - uidRaw, - ctr, - ctrRawInverseBytes, - } -} diff --git a/apps/boltcard/bats/e2e-test.bats b/apps/boltcard/bats/e2e-test.bats index 1a1683b94a6..13fd3d495fd 100644 --- a/apps/boltcard/bats/e2e-test.bats +++ b/apps/boltcard/bats/e2e-test.bats @@ -16,26 +16,35 @@ load "../../../test/bats/helpers/ln" RESPONSE=$(curl -s "${CALLBACK_URL}") echo "$RESPONSE" [[ $(echo $RESPONSE | jq -r '.protocol_name') == "create_bolt_card_response" ]] || exit 1 + + K1_VALUE=$(echo $RESPONSE | jq -r '.k1') + K2_VALUE=$(echo $RESPONSE | jq -r '.k2') + + cache_value "k1" "$K1_VALUE" + cache_value "k2" "$K2_VALUE" } @test "auth: create payment and follow up" { - P_VALUE="4E2E289D945A66BB13377A728884E867" - C_VALUE="E19CCB1FED8892CE" + K1=$(read_value "k1") + K2=$(read_value "k2") + + RESPONSE=$(bun run debug/getpandc.ts $K1 $K2) + + P_VALUE=$(echo $RESPONSE | jq -r '.p') + C_VALUE=$(echo $RESPONSE | jq -r '.c') RESPONSE=$(curl -s "http://localhost:3000/api/ln?p=${P_VALUE}&c=${C_VALUE}") echo "$RESPONSE" CALLBACK_URL=$(echo $RESPONSE | jq -r '.callback') - K1_VALUE=$(echo $RESPONSE | jq -r '.k1') - - echo "K1_VALUE: $K1_VALUE" - cache_value "k1" "$K1_VALUE" + K1_CALLBACK=$(echo $RESPONSE | jq -r '.k1') - exit 1 + echo "K1_CALLBACK: $K1_CALLBACK" + cache_value "k1_callback" "$K1_CALLBACK" } @test "callback" { - K1_VALUE=$(read_value "k1") + K1_VALUE=$(read_value "k1_callback") CALLBACK_URL=http://localhost:3000/api/callback invoice_response="$(lnd_outside_2_cli addinvoice --amt 1000)" @@ -43,6 +52,7 @@ load "../../../test/bats/helpers/ln" echo $payment_request result=$(curl -s "${CALLBACK_URL}?k1=${K1_VALUE}&pr=${payment_request}") + echo "$result" [[ result.status == "OK" ]] || exit 1 } diff --git a/apps/boltcard/debug/getpandc.ts b/apps/boltcard/debug/getpandc.ts new file mode 100644 index 00000000000..62a2c502e2e --- /dev/null +++ b/apps/boltcard/debug/getpandc.ts @@ -0,0 +1,34 @@ +import { aesEncrypt, getSunMAC } from "@/app/crypto/aes" +import { createSV2, encodeUidCtrToP } from "@/app/crypto/decoder" + +const aesjs = require("aes-js") + +const uidInit = "04996c6a926980" +const ctrInit = "030000" + +export const main = (k1: string, k2: string) => { + // "0c3b25d92b38ae443229dd59ad34b85d" + // k2: b45775776cb224c75bcde7ca3704e933 + + const uid = Buffer.from(uidInit, "hex") + const ctr = Buffer.from(ctrInit, "hex") + + const p1 = encodeUidCtrToP(uid, ctr) + const p2 = Buffer.from(p1) + + // encrypt P + const encryptP = aesEncrypt(Buffer.from(k1, "hex"), p2) + if (encryptP instanceof Error) { + throw encryptP + } + + const cv2 = createSV2(uid, ctr) + const cmac = getSunMAC(Buffer.from(k2, "hex"), cv2) + + return JSON.stringify({ + c: cmac.toString("hex"), + p: aesjs.utils.hex.fromBytes(encryptP), + }) +} + +console.log(main(process.argv[2], process.argv[3])) diff --git a/apps/boltcard/knexfile.ts b/apps/boltcard/knexfile.ts deleted file mode 100644 index 81227f08ee7..00000000000 --- a/apps/boltcard/knexfile.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Knex } from "knex" - -// Update with your config settings. - -const config: { [key: string]: Knex.Config } = { - client: "pg", - connection: "postgres://dbuser:secret@localhost:5436/default?sslmode=disable", -} - -module.exports = config