Skip to content

Latest commit

 

History

History
88 lines (63 loc) · 3 KB

10.md

File metadata and controls

88 lines (63 loc) · 3 KB

LUD-10: aes success action in payRequest.

author: akumaigorodski discussion: https://t.me/lnurl/709


New aes field on successAction

This defines a new successAction type for payRequest: aes. Besides the tag field it requires description, ciphertext and iv.

The encryption key is the payment preimage for the invoice specified in the pr field, so it's safe to include data in the ciphertext that the user will only be able to see if they complete the payment.

See example below:

{
   "tag": "aes",
   "description": "Here is your redeem code", // Up to 144 characters
   "ciphertext": string, // base64, AES-encrypted data where encryption key is payment preimage, up to 4kb of characters
   "iv": string // base64, initialization vector, exactly 24 characters
}

The decrypted content must be a string, to be presented to the user along with description. It could be, for example, a pin code to be provided elsewhere, or just a secret word or phrase the user can then use to perform something in the world with.

LNURL-pay flow change

An aes successAction should be displayed and stored like the message (LUD-09) type, but with the message corresponding to the plaintext after being decrypted with the payment preimage.

Implementation details

Used encryption type is 256-bit AES in AES/CBC/PKCS5Padding mode.

An encryption example in Scala:

val iv = Tools.random.getBytes(16) // exactly 16 bytes, unique for each secret
val key = Tools.random.getBytes(32) // payment preimage
val data = "Secret data".getBytes

val aesCipher = Cipher getInstance "AES/CBC/PKCS5Padding"
val ivParameterSpec = new IvParameterSpec(iv)
aesCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivParameterSpec)
val cipherbytes = aesCipher.doFinal(data)

val ciphertext64 = ByteVector.view(cipherbytes).toBase64 // Base 64 alphabet as defined by http://tools.ietf.org/html/rfc4648#section-4 RF4648 section 4. Whitespace is ignored.
val iv64 = ByteVector.view(iv).toBase64 // 16 bytes results in exactly 24 characters

A decryption example in JavaScript:

import aesjs from 'aes-js'
import Base64 from 'base64-js'

let key = aesjs.utils.hex.toBytes(preimage)
let iv = Base64.toByteArray(iv)
let ciphertext = Base64.toByteArray(ciphertext)

let CBC = new aesjs.ModeOfOperation.cbc(key, iv)
var plaintextBytes = CBC.decrypt(ciphertext)

// remove padding
let size = plaintext.length
let pad = plaintext[size - 1]
plaintextBytes = plaintext.slice(0, size - pad)

let plaintext = aesjs.utils.utf8.fromBytes(plaintextBytes)

A decryption example in Go:

import (
    "crypto/aes"
    "crypto/cipher"
)

ciphertext, _ := base64.StdEncoding.DecodeString(ciphertext)
iv, _ := base64.StdEncoding.DecodeString(iv)
block, _ := aes.NewCipher(preimage)
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(ciphertext, ciphertext)
size := len(ciphertext)
pad := ciphertext[size-1]
plaintext := ciphertext[:size-int(pad)]