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

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, '-')
}