You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
4.7 KiB
158 lines
4.7 KiB
import * as zbase32 from './zbase32'
|
|
import {signBuffer} from './lightning'
|
|
import * as path from 'path'
|
|
|
|
const env = process.env.NODE_ENV || 'development'
|
|
const config = require(path.join(__dirname,'../../config/app.json'))[env]
|
|
|
|
/*
|
|
Lightning Data Access Token
|
|
Base64 strings separated by dots:
|
|
{host}.{muid}.{buyerPubKey}.{exp}.{metadata}.{signature}
|
|
|
|
- host: web host for data (ascii->base64)
|
|
- muid: ID of media
|
|
- buyerPubKey
|
|
- exp: unix timestamp expiration (encoded into 4 bytes)
|
|
- meta: key/value pairs, url query encoded (alphabetically ordered, ascii->base64)
|
|
- signature of all that (concatenated bytes of each)
|
|
*/
|
|
|
|
async function tokenFromTerms({host,muid,ttl,pubkey,meta}){
|
|
const theHost = host || config.media_host || ''
|
|
|
|
const pubkeyBytes = Buffer.from(pubkey, 'hex')
|
|
const pubkey64 = urlBase64FromBytes(pubkeyBytes)
|
|
|
|
const now = Math.floor(Date.now()/1000)
|
|
const exp = ttl ? now + (60*60*24*365) : 0
|
|
|
|
const ldat = startLDAT(theHost,muid,pubkey64,exp,meta)
|
|
if(pubkey!=''){
|
|
const sig = await signBuffer(ldat.bytes)
|
|
const sigBytes = zbase32.decode(sig)
|
|
return ldat.terms + "." + urlBase64FromBytes(sigBytes)
|
|
} else {
|
|
return ldat.terms
|
|
}
|
|
}
|
|
|
|
// host.muid.pk.exp.meta
|
|
function startLDAT(host:string,muid:string,pk:string,exp:number,meta:{[k:string]:any}={}){
|
|
const empty = Buffer.from([])
|
|
var hostBuf = Buffer.from(host, 'ascii')
|
|
var muidBuf = Buffer.from(muid, 'base64')
|
|
var pkBuf = pk ? Buffer.from(pk, 'base64') : empty
|
|
var expBuf = exp ? Buffer.from(exp.toString(16), 'hex') : empty
|
|
var metaBuf = meta ? Buffer.from(serializeMeta(meta), 'ascii') : empty
|
|
|
|
const totalLength = hostBuf.length + muidBuf.length + pkBuf.length + expBuf.length + metaBuf.length
|
|
const buf = Buffer.concat([hostBuf, muidBuf, pkBuf, expBuf, metaBuf], totalLength)
|
|
let terms = `${urlBase64(hostBuf)}.${urlBase64(muidBuf)}.${urlBase64(pkBuf)}.${urlBase64(expBuf)}.${urlBase64(metaBuf)}`
|
|
return {terms, bytes: buf}
|
|
}
|
|
|
|
const termKeys = [{
|
|
key:'host',
|
|
func: buf=> buf.toString('ascii')
|
|
},{
|
|
key:'muid',
|
|
func: buf=> urlBase64(buf)
|
|
},{
|
|
key:'pubkey',
|
|
func: buf=> buf.toString('hex')
|
|
},{
|
|
key:'ts',
|
|
func: buf=> parseInt('0x' + buf.toString('hex'))
|
|
},{
|
|
key:'meta',
|
|
func: buf=> {
|
|
const ascii = buf.toString('ascii')
|
|
return ascii?deserializeMeta(ascii):{} // parse this
|
|
}
|
|
},{
|
|
key:'sig',
|
|
func: buf=> urlBase64(buf)
|
|
}]
|
|
|
|
function parseLDAT(ldat){
|
|
const a = ldat.split('.')
|
|
const o: {[k:string]:any} = {}
|
|
termKeys.forEach((t,i)=>{
|
|
if(a[i]) o[t.key] = t.func(Buffer.from(a[i], 'base64'))
|
|
})
|
|
return o
|
|
}
|
|
|
|
export {
|
|
startLDAT, parseLDAT, tokenFromTerms,
|
|
urlBase64, urlBase64FromAscii,
|
|
urlBase64FromBytes, testLDAT,
|
|
urlBase64FromHex
|
|
}
|
|
|
|
async function testLDAT(){
|
|
console.log('testLDAT')
|
|
const terms = {
|
|
host:'',
|
|
ttl:31536000, //one year
|
|
muid:'qFSOa50yWeGSG8oelsMvctLYdejPRD090dsypBSx_xg=',
|
|
pubkey:'0373ca36a331d8fd847f190908715a34997b15dc3c5d560ca032cf3412fcf494e4',
|
|
meta:{
|
|
amt:100,
|
|
ttl:31536000,
|
|
dim:'1500x1300'
|
|
}
|
|
}
|
|
const token = await tokenFromTerms(terms)
|
|
console.log(token)
|
|
|
|
const terms2 = {
|
|
host:'',
|
|
ttl:0, //one year
|
|
muid:'qFSOa50yWeGSG8oelsMvctLYdejPRD090dsypBSx_xg=',
|
|
pubkey:'',
|
|
meta:{
|
|
amt:100,
|
|
ttl:31536000,
|
|
}
|
|
}
|
|
const token2 = await tokenFromTerms(terms2)
|
|
console.log(token2)
|
|
|
|
console.log(parseLDAT(token2))
|
|
}
|
|
|
|
function serializeMeta(obj) {
|
|
var str: string[] = []
|
|
for (var p in obj) {
|
|
if (obj.hasOwnProperty(p)) {
|
|
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
|
|
}
|
|
}
|
|
str.sort((a,b)=>(a > b ? 1 : -1))
|
|
return str.join("&");
|
|
}
|
|
|
|
function deserializeMeta(str){
|
|
const json = str && str.length>2 ? JSON.parse('{"' + str.replace(/&/g, '","').replace(/=/g,'":"') + '"}', function(key, value) { return key===""?value:decodeURIComponent(value) }) : {}
|
|
const ret = {}
|
|
for (let [k, v] of Object.entries(json)) {
|
|
const value = (typeof v==='string' && parseInt(v)) || v
|
|
ret[k] = value
|
|
}
|
|
return ret
|
|
}
|
|
|
|
function urlBase64(buf){
|
|
return buf.toString('base64').replace(/\//g, '_').replace(/\+/g, '-')
|
|
}
|
|
function urlBase64FromBytes(buf){
|
|
return Buffer.from(buf).toString('base64').replace(/\//g, '_').replace(/\+/g, '-')
|
|
}
|
|
function urlBase64FromAscii(ascii){
|
|
return Buffer.from(ascii,'ascii').toString('base64').replace(/\//g, '_').replace(/\+/g, '-')
|
|
}
|
|
function urlBase64FromHex(ascii){
|
|
return Buffer.from(ascii,'hex').toString('base64').replace(/\//g, '_').replace(/\+/g, '-')
|
|
}
|
|
|