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