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 b97b9a4
Show file tree
Hide file tree
Showing 17 changed files with 746 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
75 changes: 75 additions & 0 deletions apps/boltcard/aes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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 async function checkSignature(
uid: Uint8Array,
ctr: Uint8Array,
k2CmacKey: Buffer,
cmac: Buffer,
): Promise<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],
])

let calculatedCmac

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

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

function getSunMAC(key: Buffer, sv2: Buffer): Buffer {
const options = { returnAsBuffer: true }
const cmac1 = aesCmac(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]
}

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

@test "auth: create card" {
accountId="b12871e9-01e7-4aec-8597-873ebab7df1f"

RESPONSE=$(curl -s "http://localhost:3000/createboltcard?accountId=${accountId}")
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")
71 changes: 71 additions & 0 deletions apps/boltcard/decoder.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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: Buffer.from("CC61660C020B4D96", "hex"),
aes_decrypt_key: aesjs.utils.hex.toBytes("0c3b25d92b38ae443229dd59ad34b85d"),
aes_cmac_key: Buffer.from("b45775776cb224c75bcde7ca3704e933", "hex"),
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 = await 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 b97b9a4

Please sign in to comment.