Skip to content

Commit

Permalink
feat: poc for boltcard
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas Burtey committed Sep 16, 2023
1 parent ab93735 commit 7f8cec6
Show file tree
Hide file tree
Showing 18 changed files with 694 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,6 @@ codegen:

gen-test-jwt:
yarn gen-test-jwt

boltcard:
bun run ./apps/boltcard/index.ts
1 change: 1 addition & 0 deletions apps/boltcard/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.lockb binary diff=lockb
24 changes: 24 additions & 0 deletions apps/boltcard/aes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const aesCmac = require("node-aes-cmac").aesCmac

describe("aes", () => {
test(`testing signature`, async () => {
const c = Buffer.from("E19CCB1FED8892CE", "hex")
const aes_cmac_key = Buffer.from("b45775776cb224c75bcde7ca3704e933", "hex")

const sv2 = Buffer.from([
60, 195, 0, 1, 0, 128, 4, 153, 108, 106, 146, 105, 128, 3, 0, 0,
])

const options = { returnAsBuffer: true }
const cmac1 = aesCmac(aes_cmac_key, sv2, options)
const cmac2 = aesCmac(cmac1, new Buffer(""), options)

const halfMac = Buffer.alloc(cmac2.length / 2)
for (let i = 1; i < cmac2.length; i += 2) {
halfMac[i >> 1] = cmac2[i]
}

console.log({ c, aes_cmac_key, sv2, cmac1, cmac2 })
expect(Buffer.compare(halfMac, c)).toEqual(0)
})
})
86 changes: 86 additions & 0 deletions apps/boltcard/aes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import crypto from "crypto"

// import { AesCmac } from "aes-cmac"
const aesCmac = require("node-aes-cmac").aesCmac

const aesjs = require("aes-js")

class DecryptionError extends Error {
constructor(err: Error) {
super(err.message)
}
}

class UnknownError extends Error {}

export function aesDecrypt(key: Buffer, data: Buffer): Buffer | DecryptionError {
try {
const aesCtr = new aesjs.ModeOfOperation.cbc(key)
const decryptedBytes = aesCtr.decrypt(data)
return decryptedBytes
} catch (err) {
console.log(err)
if (err instanceof Error) return new DecryptionError(err)
return new UnknownError()
}
}

export function checkSignature(
uid: Uint8Array,
ctr: Uint8Array,
k2CmacKey: Buffer,
cmac: Buffer,
): boolean {
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],
])

console.log({ sv2 })

let calculatedCmac

try {
calculatedCmac = getSunMAC(k2CmacKey, sv2)
} catch (error) {
console.error(error)
throw new Error("issue with cMac")
}

console.log({ calculatedCmac, cmac })

// Compare the result
return Buffer.compare(calculatedCmac, cmac) === 0
}

function getSunMAC(key: Buffer, sv2: Buffer): Buffer {
// const aesCmac = new AesCmac(key)
// const cmac = Buffer.from(await aesCmac.calculate(sv2))

const options = { returnAsBuffer: true }
const fullCmacComputed = aesCmac(key, sv2, options)

// const cmac = aesCmac(key, sv2, options)
console.log({ fullCmacComputed })

const result = Buffer.alloc(fullCmacComputed.length / 2)
for (let i = 1; i < fullCmacComputed.length; i += 2) {
result[i >> 1] = fullCmacComputed[i]
}

return result
}
29 changes: 29 additions & 0 deletions apps/boltcard/bats/e2e-test.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

@test "auth: create card" {
RESPONSE=$(curl -s "http://localhost:3000/createboltcard")
CALLBACK_URL=$(echo $RESPONSE | jq -r '.url')

# Making the follow-up curl request
RESPONSE=$(curl -s "${CALLBACK_URL}")
echo "$RESPONSE"
[[ $(echo $RESPONSE | jq -r '.PROTOCOL_NAME') == "create_bolt_card_response" ]] || exit 1
}


@test "auth: create payment and follow up" {

P_VALUE="4E2E289D945A66BB13377A728884E867"
C_VALUE="E19CCB1FED8892CE"

RESPONSE=$(curl -s "http://localhost:3000/ln?p=${P_VALUE}&c=${C_VALUE}")
echo "$RESPONSE"

CALLBACK_URL=$(echo $RESPONSE | jq -r '.callback')
K1_VALUE=$(echo $RESPONSE | jq -r '.k1')

echo "CALLBACK_URL: $CALLBACK_URL"
echo "K1_VALUE: $K1_VALUE"

# Making the follow-up curl request
curl -s "${CALLBACK_URL}?k1=${K1_VALUE}"
}
Binary file added apps/boltcard/bun.lockb
Binary file not shown.
31 changes: 31 additions & 0 deletions apps/boltcard/callback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import express from "express"

import { boltcardRouter } from "./router"
import { fetchByK1 } from "./knex"

boltcardRouter.get("/callback", async (req: express.Request, res: express.Response) => {
const k1 = req?.query?.k1
const pr = req?.query?.pr

console.log({ k1, pr })

if (!k1 || !pr) {
res.status(400).send({ status: "ERROR", reason: "missing k1 or pr" })
return
}

if (typeof k1 !== "string" || typeof pr !== "string") {
res.status(400).send({ status: "ERROR", reason: "invalid k1 or pr" })
return
}

const payment = await fetchByK1(k1)
console.log(payment)
// fetch user from k1
// payInvoice(pr)

res.json({ status: "OK" })
})

const callback = "dummy"
export { callback }
5 changes: 5 additions & 0 deletions apps/boltcard/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const serverUrl = process.env.SERVER_URL ?? "http://localhost:3000"

const AES_DECRYPT_KEY = process.env.AES_DECRYPT_KEY ?? "0c3b25d92b38ae443229dd59ad34b85d"

export const aesDecryptKey = Buffer.from(AES_DECRYPT_KEY, "hex")
66 changes: 66 additions & 0 deletions apps/boltcard/decoder.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { aesDecrypt, checkSignature } from "./aes"
import { decryptedPToUidCtr } from "./decoder"

const aesjs = require("aes-js")

const values = [
{
p: aesjs.utils.hex.toBytes("4E2E289D945A66BB13377A728884E867"),
c: Buffer.from("E19CCB1FED8892CE", "hex"),
aes_decrypt_key: aesjs.utils.hex.toBytes("0c3b25d92b38ae443229dd59ad34b85d"),
aes_cmac_key: Buffer.from("b45775776cb224c75bcde7ca3704e933", "hex"),
decrypted_uid: "04996c6a926980",
decrypted_ctr: "030000",
decoded_ctr: 3,
},
// {
// p: aesjs.utils.hex.toBytes("00F48C4F8E386DED06BCDC78FA92E2FE"),
// c: Buffer.from("66B4826EA4C155B4", "hex"),
// aes_decrypt_key: aesjs.utils.hex.toBytes("0c3b25d92b38ae443229dd59ad34b85d"),
// aes_cmac_key: Buffer.from("b45775776cb224c75bcde7ca3704e933", "hex"),
// decrypted_uid: "04996c6a926980",
// decrypted_ctr: "050000",
// decoded_ctr: 5,
// },
// {
// p: aesjs.utils.hex.toBytes("0DBF3C59B59B0638D60B5842A997D4D1"),
// c: aesjs.utils.hex.toBytes("66B4826EA4C155B4"),
// aes_decrypt_key: aesjs.utils.hex.toBytes("0c3b25d92b38ae443229dd59ad34b85d"),
// aes_cmac_key: aesjs.utils.hex.toBytes("b45775776cb224c75bcde7ca3704e933"),
// decrypted_uid: "04996c6a926980",
// decrypted_ctr: "070000",
// decoded_ctr: 7,
// },
]

describe("crypto", () => {
values.forEach(
({
p,
c,
aes_decrypt_key,
aes_cmac_key,
decrypted_uid,
decrypted_ctr,
decoded_ctr,
}) => {
test(`testing ${aesjs.utils.hex.fromBytes(p)}`, async () => {
const decryptedP = aesDecrypt(aes_decrypt_key, p)
if (decryptedP instanceof Error) {
throw decryptedP
}

const { uid, uidRaw, ctr, ctrRawInverseBytes } = decryptedPToUidCtr(decryptedP)

expect(uid).toEqual(decrypted_uid)
expect(ctr).toEqual(decoded_ctr)
// expect(ctrRawInverseBytes).toEqual(decrypted_ctr)

console.log({ uidRaw, ctrRawInverseBytes, aes_cmac_key, c })

const cmacVerified = checkSignature(uidRaw, ctrRawInverseBytes, aes_cmac_key, c)
expect(cmacVerified).toEqual(true)
})
},
)
})
23 changes: 23 additions & 0 deletions apps/boltcard/decoder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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,
}
}
12 changes: 12 additions & 0 deletions apps/boltcard/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: "3"
services:
boltcard-pg:
image: postgres:14.1
ports:
- "5436:5432"
expose:
- "5432"
environment:
- POSTGRES_USER=dbuser
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=default
33 changes: 33 additions & 0 deletions apps/boltcard/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// server.js

import express from "express"
import bodyParser from "body-parser"

import { boltcardRouter } from "./router"

// loading router
import { lnurlw } from "./lnurlw"
import { callback } from "./callback"
import { createboltcard } from "./new"
import { createTable } from "./knex"

lnurlw
callback
createboltcard

await createTable()

const app = express()
const PORT = 3000

// Middleware to parse POST requests
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

// Use the router
app.use(boltcardRouter)

// Start the server
app.listen(PORT, () => {
console.log(`Server started on http://localhost:${PORT}`)
})
Loading

0 comments on commit 7f8cec6

Please sign in to comment.