Browse Source

Merge pull request #35 from stakwork/tribes

Tribes
feature/dockerfile-arm v0.9.0
Evan Feenstra 5 years ago
committed by GitHub
parent
commit
7d3f71d338
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 355
      api/controllers/chats.ts
  2. 6
      api/controllers/confirmations.ts
  3. 42
      api/controllers/contacts.ts
  4. 33
      api/controllers/index.ts
  5. 16
      api/controllers/invoices.ts
  6. 32
      api/controllers/media.ts
  7. 22
      api/controllers/messages.ts
  8. 17
      api/controllers/payment.ts
  9. 3
      api/controllers/subscriptions.ts
  10. 27
      api/crypto/rsa.ts
  11. 99
      api/grpc/index.ts
  12. 94
      api/helpers.ts
  13. 3
      api/models/index.ts
  14. 18
      api/models/ts/chat.ts
  15. 24
      api/models/ts/chatMember.ts
  16. 3
      api/models/ts/contact.ts
  17. 3
      api/models/ts/message.ts
  18. 14
      api/network/index.ts
  19. 88
      api/network/modify.ts
  20. 209
      api/network/receive.ts
  21. 153
      api/network/send.ts
  22. 11
      api/utils/json.ts
  23. 6
      api/utils/ldat.ts
  24. 37
      api/utils/lightning.ts
  25. 8
      api/utils/meme.ts
  26. 108
      api/utils/msg.ts
  27. 5
      api/utils/rncryptor/index.js
  28. 104
      api/utils/rncryptor/rncryptor.js
  29. 55
      api/utils/rncryptor/sjcl.js
  30. 28
      api/utils/setup.ts
  31. 118
      api/utils/signer.ts
  32. 101
      api/utils/tribes.ts
  33. 9
      app.ts
  34. 6
      config/app.json
  35. 11
      config/constants.json
  36. 336
      dist/api/controllers/chats.js
  37. 2
      dist/api/controllers/chats.js.map
  38. 6
      dist/api/controllers/confirmations.js
  39. 2
      dist/api/controllers/confirmations.js.map
  40. 41
      dist/api/controllers/contacts.js
  41. 2
      dist/api/controllers/contacts.js.map
  42. 34
      dist/api/controllers/index.js
  43. 2
      dist/api/controllers/index.js.map
  44. 16
      dist/api/controllers/invoices.js
  45. 2
      dist/api/controllers/invoices.js.map
  46. 31
      dist/api/controllers/media.js
  47. 2
      dist/api/controllers/media.js.map
  48. 23
      dist/api/controllers/messages.js
  49. 2
      dist/api/controllers/messages.js.map
  50. 15
      dist/api/controllers/payment.js
  51. 2
      dist/api/controllers/payment.js.map
  52. 3
      dist/api/controllers/subscriptions.js
  53. 2
      dist/api/controllers/subscriptions.js.map
  54. 27
      dist/api/crypto/rsa.js
  55. 2
      dist/api/crypto/rsa.js.map
  56. 105
      dist/api/grpc/index.js
  57. 2
      dist/api/grpc/index.js.map
  58. 83
      dist/api/helpers.js
  59. 2
      dist/api/helpers.js.map
  60. 3
      dist/api/models/index.js
  61. 2
      dist/api/models/index.js.map
  62. 24
      dist/api/models/ts/chat.js
  63. 2
      dist/api/models/ts/chat.js.map
  64. 43
      dist/api/models/ts/chatMember.js
  65. 1
      dist/api/models/ts/chatMember.js.map
  66. 4
      dist/api/models/ts/contact.js
  67. 2
      dist/api/models/ts/contact.js.map
  68. 4
      dist/api/models/ts/message.js
  69. 2
      dist/api/models/ts/message.js.map
  70. 10
      dist/api/network/index.js
  71. 1
      dist/api/network/index.js.map
  72. 84
      dist/api/network/modify.js
  73. 1
      dist/api/network/modify.js.map
  74. 240
      dist/api/network/receive.js
  75. 1
      dist/api/network/receive.js.map
  76. 155
      dist/api/network/send.js
  77. 1
      dist/api/network/send.js.map
  78. 14
      dist/api/utils/json.js
  79. 2
      dist/api/utils/json.js.map
  80. 4
      dist/api/utils/ldat.js
  81. 2
      dist/api/utils/ldat.js.map
  82. 44
      dist/api/utils/lightning.js
  83. 2
      dist/api/utils/lightning.js.map
  84. 9
      dist/api/utils/meme.js
  85. 1
      dist/api/utils/meme.js.map
  86. 106
      dist/api/utils/msg.js
  87. 2
      dist/api/utils/msg.js.map
  88. 5
      dist/api/utils/rncryptor/index.js
  89. 1
      dist/api/utils/rncryptor/index.js.map
  90. 78
      dist/api/utils/rncryptor/rncryptor.js
  91. 1
      dist/api/utils/rncryptor/rncryptor.js.map
  92. 503
      dist/api/utils/rncryptor/sjcl.js
  93. 1
      dist/api/utils/rncryptor/sjcl.js.map
  94. 30
      dist/api/utils/setup.js
  95. 2
      dist/api/utils/setup.js.map
  96. 136
      dist/api/utils/signer.js
  97. 1
      dist/api/utils/signer.js.map
  98. 121
      dist/api/utils/tribes.js
  99. 1
      dist/api/utils/tribes.js.map
  100. 9
      dist/app.js

355
api/controllers/chats.ts

@ -2,10 +2,13 @@ import { models } from '../models'
import * as jsonUtils from '../utils/json'
import { success, failure } from '../utils/res'
import * as helpers from '../helpers'
import * as network from '../network'
import * as socket from '../utils/socket'
import { sendNotification } from '../hub'
import * as md5 from 'md5'
import * as path from 'path'
import * as rsa from '../crypto/rsa'
import * as tribes from '../utils/tribes'
const constants = require(path.join(__dirname,'../../config/constants.json'))
@ -34,11 +37,20 @@ async function mute(req, res) {
success(res, jsonUtils.chatToJson(chat))
}
// just add self here if tribes
// or can u add contacts as members?
async function createGroupChat(req, res) {
const {
name,
contact_ids,
is_tribe,
is_listed,
price_per_message,
price_to_join,
img,
description,
tags,
} = req.body
const contact_ids = req.body.contact_ids||[]
const members: { [k: string]: {[k:string]:string} } = {} //{pubkey:{key,alias}, ...}
const owner = await models.Contact.findOne({ where: { isOwner: true } })
@ -54,9 +66,27 @@ async function createGroupChat(req, res) {
}
})
const chatParams = createGroupChatParams(owner, contact_ids, members, name)
let chatParams:any = null
if(is_tribe){
chatParams = await createTribeChatParams(owner, contact_ids, name, img, price_per_message, price_to_join)
if(is_listed && chatParams.uuid){
// publish to tribe server
tribes.declare({
...chatParams,
pricePerMessage: price_per_message||0,
priceToJoin: price_to_join||0,
description, tags, img,
ownerPubkey: owner.publicKey,
ownerAlias: owner.alias,
})
}
// make me owner when i create
members[owner.publicKey].role = constants.chat_roles.owner
} else {
chatParams = createGroupChatParams(owner, contact_ids, members, name)
}
helpers.sendMessage({
network.sendMessage({
chat: { ...chatParams, members },
sender: owner,
type: constants.message_types.group_create,
@ -66,11 +96,19 @@ async function createGroupChat(req, res) {
},
success: async function () {
const chat = await models.Chat.create(chatParams)
if(chat.type===constants.chat_types.tribe){ // save me as owner when i create
await models.ChatMember.create({
contactId: owner.id,
chatId: chat.id,
role: constants.chat_roles.owner,
})
}
success(res, jsonUtils.chatToJson(chat))
}
})
}
// only owner can do for tribe?
async function addGroupMembers(req, res) {
const {
contact_ids,
@ -84,6 +122,10 @@ async function addGroupMembers(req, res) {
const contactIds = JSON.parse(chat.contactIds || '[]')
// for all members (existing and new)
members[owner.publicKey] = {key:owner.contactKey, alias:owner.alias}
if(chat.type===constants.chat_types.tribe){
const me = await models.ChatMember.findOne({where:{contactId: owner.id, chatId: chat.id}})
if(me) members[owner.publicKey].role = me.role
}
const allContactIds = contactIds.concat(contact_ids)
await asyncForEach(allContactIds, async cid => {
const contact = await models.Contact.findOne({ where: { id: cid } })
@ -92,12 +134,14 @@ async function addGroupMembers(req, res) {
key: contact.contactKey,
alias: contact.alias
}
const member = await models.ChatMember.findOne({where:{contactId: owner.id, chatId: chat.id}})
if(member) members[contact.publicKey].role = member.role
}
})
success(res, jsonUtils.chatToJson(chat))
helpers.sendMessage({ // send ONLY to new members
network.sendMessage({ // send ONLY to new members
chat: { ...chat.dataValues, contactIds:contact_ids, members },
sender: owner,
type: constants.message_types.group_invite,
@ -110,7 +154,16 @@ const deleteChat = async (req, res) => {
const owner = await models.Contact.findOne({ where: { isOwner: true } })
const chat = await models.Chat.findOne({ where: { id } })
helpers.sendMessage({
if(!chat) {
return failure(res, "you are not in this group")
}
const tribeOwnerPubKey = chat.ownerPubkey
if(owner.publicKey===tribeOwnerPubKey) {
return failure(res, "cannot leave your own tribe")
}
network.sendMessage({
chat,
sender: owner,
message: {},
@ -120,6 +173,9 @@ const deleteChat = async (req, res) => {
await chat.update({
deleted: true,
uuid:'',
groupKey:'',
host:'',
photoUrl:'',
contactIds:'[]',
name:''
})
@ -128,33 +184,132 @@ const deleteChat = async (req, res) => {
success(res, { chat_id: id })
}
async function joinTribe(req, res){
console.log('=> joinTribe')
const { uuid, group_key, name, host, amount, img, owner_pubkey, owner_alias } = req.body
const existing = await models.Chat.findOne({where:{uuid}})
if(existing) {
console.log('[tribes] u are already in this tribe')
return
}
if(!owner_pubkey || !group_key || !uuid) {
console.log('[tribes] missing required params')
return
}
const ownerPubKey = owner_pubkey
// verify signature here?
const tribeOwner = await models.Contact.findOne({ where: { publicKey: ownerPubKey } })
let theTribeOwner
const owner = await models.Contact.findOne({ where: { isOwner: true } })
const contactIds = [owner.id]
if (tribeOwner) {
theTribeOwner = tribeOwner // might already include??
if(!contactIds.includes(tribeOwner.id)) contactIds.push(tribeOwner.id)
} else {
const createdContact = await models.Contact.create({
publicKey: ownerPubKey,
contactKey: '',
alias: owner_alias||'Unknown',
status: 1,
fromGroup: true,
})
theTribeOwner = createdContact
contactIds.push(createdContact.id)
}
let date = new Date()
date.setMilliseconds(0)
const chatParams = {
uuid: uuid,
contactIds: JSON.stringify(contactIds),
photoUrl: img||'',
createdAt: date,
updatedAt: date,
name: name,
type: constants.chat_types.tribe,
host: host || tribes.getHost(),
groupKey: group_key,
ownerPubkey: owner_pubkey,
}
network.sendMessage({ // send my data to tribe owner
chat: {
...chatParams, members: {
[owner.publicKey]: {
key: owner.contactKey,
alias: owner.alias||''
}
}
},
amount:amount||0,
sender: owner,
message: {},
type: constants.message_types.group_join,
failure: function (e) {
failure(res, e)
},
success: async function () {
const chat = await models.Chat.create(chatParams)
models.ChatMember.create({
contactId: theTribeOwner.id,
chatId: chat.id,
role: constants.chat_roles.owner,
lastActive: date,
})
success(res, jsonUtils.chatToJson(chat))
}
})
}
async function receiveGroupLeave(payload) {
console.log('=> receiveGroupLeave')
const { sender_pub_key, chat_uuid } = await helpers.parseReceiveParams(payload)
const { sender_pub_key, chat_uuid, chat_type, sender_alias, isTribeOwner } = await helpers.parseReceiveParams(payload)
const chat = await models.Chat.findOne({ where: { uuid: chat_uuid } })
if (!chat) return
const sender = await models.Contact.findOne({ where: { publicKey: sender_pub_key } })
if (!sender) return
const isTribe = chat_type===constants.chat_types.tribe
let sender
if(!isTribe || isTribeOwner) {
sender = await models.Contact.findOne({ where: { publicKey: sender_pub_key } })
if (!sender) return
const oldContactIds = JSON.parse(chat.contactIds || '[]')
const contactIds = oldContactIds.filter(cid => cid !== sender.id)
await chat.update({ contactIds: JSON.stringify(contactIds) })
const oldContactIds = JSON.parse(chat.contactIds || '[]')
const contactIds = oldContactIds.filter(cid => cid !== sender.id)
await chat.update({ contactIds: JSON.stringify(contactIds) })
if(isTribeOwner) {
if(chat_type===constants.chat_types.tribe){
try {
await models.ChatMember.destroy({where:{chatId: chat.id, contactId: sender.id}})
} catch(e) {}
}
}
}
var date = new Date();
date.setMilliseconds(0)
const msg = {
const msg:{[k:string]:any} = {
chatId: chat.id,
type: constants.message_types.group_leave,
sender: sender.id,
sender: (sender && sender.id) || 0,
date: date,
messageContent: '',
messageContent: `${sender_alias} has left the group`,
remoteMessageContent: '',
status: constants.statuses.confirmed,
createdAt: date,
updatedAt: date
}
if(isTribe) {
msg.senderAlias = sender_alias
}
const message = await models.Message.create(msg)
socket.sendJson({
@ -169,81 +324,129 @@ async function receiveGroupLeave(payload) {
async function receiveGroupJoin(payload) {
console.log('=> receiveGroupJoin')
const { sender_pub_key, chat_uuid, chat_members } = await helpers.parseReceiveParams(payload)
const { sender_pub_key, sender_alias, chat_uuid, chat_members, chat_type, isTribeOwner } = await helpers.parseReceiveParams(payload)
const chat = await models.Chat.findOne({ where: { uuid: chat_uuid } })
if (!chat) return
const isTribe = chat_type===constants.chat_types.tribe
var date = new Date()
date.setMilliseconds(0)
let theSender: any = null
const sender = await models.Contact.findOne({ where: { publicKey: sender_pub_key } })
const contactIds = JSON.parse(chat.contactIds || '[]')
if (sender) {
theSender = sender // might already include??
if(!contactIds.includes(sender.id)) contactIds.push(sender.id)
} else {
const member = chat_members[sender_pub_key]
if(member && member.key) {
const createdContact = await models.Contact.create({
publicKey: sender_pub_key,
contactKey: member.key,
alias: member.alias||'Unknown',
status: 1
const member = chat_members[sender_pub_key]
const senderAlias = sender_alias || (member && member.alias) || 'Unknown'
if(!isTribe || isTribeOwner) { // dont need to create contacts for these
const sender = await models.Contact.findOne({ where: { publicKey: sender_pub_key } })
const contactIds = JSON.parse(chat.contactIds || '[]')
if (sender) {
theSender = sender // might already include??
if(!contactIds.includes(sender.id)) contactIds.push(sender.id)
} else {
if(member && member.key) {
const createdContact = await models.Contact.create({
publicKey: sender_pub_key,
contactKey: member.key,
alias: senderAlias,
status: 1,
fromGroup: true,
})
theSender = createdContact
contactIds.push(createdContact.id)
}
}
if(!theSender) return // fail (no contact key?)
await chat.update({ contactIds: JSON.stringify(contactIds) })
if(isTribeOwner){ // IF TRIBE, ADD TO XREF
models.ChatMember.create({
contactId: theSender.id,
chatId: chat.id,
role: constants.chat_roles.reader,
lastActive: date,
})
theSender = createdContact
contactIds.push(createdContact.id)
}
}
await chat.update({ contactIds: JSON.stringify(contactIds) })
var date = new Date();
date.setMilliseconds(0)
const msg = {
const msg:{[k:string]:any} = {
chatId: chat.id,
type: constants.message_types.group_join,
sender: theSender.id,
sender: (theSender && theSender.id) || 0,
date: date,
messageContent: '',
messageContent: `${senderAlias} has joined the group`,
remoteMessageContent: '',
status: constants.statuses.confirmed,
createdAt: date,
updatedAt: date
}
if(isTribe) {
msg.senderAlias = sender_alias
}
const message = await models.Message.create(msg)
socket.sendJson({
type: 'group_join',
response: {
contact: jsonUtils.contactToJson(theSender),
contact: jsonUtils.contactToJson(theSender||{}),
chat: jsonUtils.chatToJson(chat),
message: jsonUtils.messageToJson(message, null)
}
})
}
async function validateTribeOwner(chat_uuid: string, pubkey: string){
const verifiedOwnerPubkey = await tribes.verifySignedTimestamp(chat_uuid)
if(verifiedOwnerPubkey===pubkey){
return true
}
return false
}
async function receiveGroupCreateOrInvite(payload) {
const { chat_members, chat_name, chat_uuid } = await helpers.parseReceiveParams(payload)
const { sender_pub_key, chat_members, chat_name, chat_uuid, chat_type, chat_host, chat_key } = await helpers.parseReceiveParams(payload)
const contactIds: number[] = []
// maybe this just needs to move to adding tribe owner ChatMember?
const isTribe = chat_type===constants.chat_types.tribe
if(isTribe){ // must be sent by tribe owner?????
const validOwner = await validateTribeOwner(chat_uuid, sender_pub_key)
if(!validOwner) return console.log('[tribes] invalid uuid signature!')
}
const contacts: any[] = []
const newContacts: any[] = []
for (let [pubkey, member] of Object.entries(chat_members)) {
const contact = await models.Contact.findOne({ where: { publicKey: pubkey } })
if (!contact && member && member.key) {
const createdContact = await models.Contact.create({
publicKey: pubkey,
contactKey: member.key,
alias: member.alias||'Unknown',
status: 1
})
contactIds.push(createdContact.id)
newContacts.push(createdContact.dataValues)
} else {
contactIds.push(contact.id)
let addContact = false
if (chat_type===constants.chat_types.group && member && member.key) {
addContact = true
} else if(isTribe && member && member.role) {
if (member.role===constants.chat_roles.owner || member.role===constants.chat_roles.admin || member.role===constants.chat_roles.mod){
addContact = true
}
}
if(addContact){
if (!contact) {
const createdContact = await models.Contact.create({
publicKey: pubkey,
contactKey: member.key,
alias: member.alias||'Unknown',
status: 1,
fromGroup: true,
})
contacts.push({...createdContact.dataValues,role:member.role})
newContacts.push(createdContact.dataValues)
} else {
contacts.push({...contact.dataValues,role:member.role})
}
}
}
const owner = await models.Contact.findOne({ where: { isOwner: true } })
const contactIds = contacts.map(c=>c.id)
if(!contactIds.includes(owner.id)) contactIds.push(owner.id)
// make chat
let date = new Date();
let date = new Date()
date.setMilliseconds(0)
const chat = await models.Chat.create({
uuid: chat_uuid,
@ -251,9 +454,22 @@ async function receiveGroupCreateOrInvite(payload) {
createdAt: date,
updatedAt: date,
name: chat_name,
type: constants.chat_types.group
type: chat_type || constants.chat_types.group,
...chat_host && { host: chat_host },
...chat_key && { groupKey: chat_key },
})
if(isTribe){ // IF TRIBE, ADD TO XREF
contacts.forEach(c=>{
models.ChatMember.create({
contactId: c.id,
chatId: chat.id,
role: c.role||constants.chat_roles.reader,
lastActive: date,
})
})
}
socket.sendJson({
type: 'group_create',
response: jsonUtils.messageToJson({ newContacts }, chat)
@ -263,7 +479,7 @@ async function receiveGroupCreateOrInvite(payload) {
if (payload.type === constants.message_types.group_invite) {
const owner = await models.Contact.findOne({ where: { isOwner: true } })
helpers.sendMessage({
network.sendMessage({
chat: {
...chat.dataValues, members: {
[owner.publicKey]: {
@ -280,7 +496,7 @@ async function receiveGroupCreateOrInvite(payload) {
}
function createGroupChatParams(owner, contactIds, members, name) {
let date = new Date();
let date = new Date()
date.setMilliseconds(0)
if (!(owner && members && contactIds && Array.isArray(contactIds))) {
return
@ -305,10 +521,39 @@ function createGroupChatParams(owner, contactIds, members, name) {
}
}
async function createTribeChatParams(owner, contactIds, name, img, price_per_message, price_to_join): Promise<{[k:string]:any}> {
let date = new Date()
date.setMilliseconds(0)
if (!(owner && contactIds && Array.isArray(contactIds))) {
return {}
}
// make ts sig here w LNd pubkey - that is UUID
const keys:{[k:string]:string} = await rsa.genKeys()
const groupUUID = await tribes.genSignedTimestamp()
const theContactIds = contactIds.includes(owner.id) ? contactIds : [owner.id].concat(contactIds)
return {
uuid: groupUUID,
ownerPubkey: owner.publicKey,
contactIds: JSON.stringify(theContactIds),
photoUrl: img||'',
createdAt: date,
updatedAt: date,
name: name,
type: constants.chat_types.tribe,
groupKey: keys.public,
groupPrivateKey: keys.private,
host: tribes.getHost(),
pricePerMessage: price_per_message||0,
priceToJoin: price_to_join||0,
}
}
export {
getChats, mute, addGroupMembers,
receiveGroupCreateOrInvite, createGroupChat,
deleteChat, receiveGroupLeave, receiveGroupJoin
deleteChat, receiveGroupLeave, receiveGroupJoin,
joinTribe,
}

6
api/controllers/confirmations.ts

@ -2,13 +2,13 @@ import lock from '../utils/lock'
import {models} from '../models'
import * as socket from '../utils/socket'
import * as jsonUtils from '../utils/json'
import * as helpers from '../helpers'
import * as network from '../network'
import * as path from 'path'
const constants = require(path.join(__dirname,'../../config/constants.json'))
export function sendConfirmation({ chat, sender, msg_id }) {
helpers.sendMessage({
network.sendMessage({
chat,
sender,
message: {id:msg_id},
@ -17,7 +17,7 @@ export function sendConfirmation({ chat, sender, msg_id }) {
}
export async function receiveConfirmation(payload) {
console.log('received confirmation', { payload })
console.log('=> received confirmation')
const dat = payload.content || payload
const chat_uuid = dat.chat.uuid

42
api/controllers/contacts.ts

@ -6,6 +6,7 @@ import * as jsonUtils from '../utils/json'
import {success, failure} from '../utils/res'
import password from '../utils/password'
import * as path from 'path'
import { Op } from 'sequelize'
const constants = require(path.join(__dirname,'../../config/constants.json'))
@ -110,6 +111,14 @@ const createContact = async (req, res) => {
const owner = await models.Contact.findOne({ where: { isOwner: true }})
const existing = attrs['public_key'] && await models.Contact.findOne({where:{publicKey:attrs['public_key']}})
if(existing) {
const updateObj:{[k:string]:any} = {fromGroup:false}
if(attrs['alias']) updateObj.alias = attrs['alias']
await existing.update(updateObj)
return success(res, jsonUtils.contactToJson(existing))
}
const createdContact = await models.Contact.create(attrs)
const contact = await createdContact.update(jsonUtils.jsonToContact(attrs))
@ -130,13 +139,30 @@ const deleteContact = async (req, res) => {
}
const contact = await models.Contact.findOne({ where: { id } })
await contact.update({
deleted:true,
publicKey:'',
photoUrl:'',
alias:'Unknown',
contactKey:'',
})
if(!contact) return
const owner = await models.Contact.findOne({ where: { isOwner: true }})
const tribesImAdminOf = await models.Chat.findAll({where:{ownerPubkey:owner.publicKey}})
const tribesIdArray = tribesImAdminOf && tribesImAdminOf.length && tribesImAdminOf.map(t=>t.id)
let okToDelete = true
if(tribesIdArray && tribesIdArray.length) {
const thisContactMembers = await models.ChatMember.findAll({where:{contactId:id,chatId:{[Op.in]:tribesIdArray}}})
if(thisContactMembers&&thisContactMembers.length){
// IS A MEMBER! dont delete, instead just set from_group=true
okToDelete=false
await contact.update({fromGroup:true})
}
}
if(okToDelete){
await contact.update({
deleted:true,
publicKey:'',
photoUrl:'',
alias:'Unknown',
contactKey:'',
})
}
// find and destroy chat & messages
const chats = await models.Chat.findAll({where:{deleted:false}})
@ -225,7 +251,7 @@ const receiveContactKey = async (payload) => {
}
const extractAttrs = body => {
let fields_to_update = ["public_key", "node_alias", "alias", "photo_url", "device_id", "status", "contact_key"]
let fields_to_update = ["public_key", "node_alias", "alias", "photo_url", "device_id", "status", "contact_key", "from_group"]
let attrs = {}
Object.keys(body).forEach(key => {
if (fields_to_update.includes(key)) {

33
api/controllers/index.ts

@ -1,10 +1,5 @@
import {models} from '../models'
import * as lndService from '../grpc'
import {checkTag} from '../utils/gitinfo'
import {checkConnection} from '../utils/lightning'
import * as path from 'path'
const constants = require(path.join(__dirname,'../../config/constants.json'))
const env = process.env.NODE_ENV || 'development';
console.log("=> env:",env)
@ -23,31 +18,6 @@ let controllers = {
confirmations: require('./confirmations')
}
async function iniGrpcSubscriptions() {
try{
await checkConnection()
const types = constants.message_types
await lndService.subscribeInvoices({
[types.contact_key]: controllers.contacts.receiveContactKey,
[types.contact_key_confirmation]: controllers.contacts.receiveConfirmContactKey,
[types.message]: controllers.messages.receiveMessage,
[types.invoice]: controllers.invoices.receiveInvoice,
[types.direct_payment]: controllers.payments.receivePayment,
[types.confirmation]: controllers.confirmations.receiveConfirmation,
[types.attachment]: controllers.media.receiveAttachment,
[types.purchase]: controllers.media.receivePurchase,
[types.purchase_accept]: controllers.media.receivePurchaseAccept,
[types.purchase_deny]: controllers.media.receivePurchaseDeny,
[types.group_create]: controllers.chats.receiveGroupCreateOrInvite,
[types.group_invite]: controllers.chats.receiveGroupCreateOrInvite,
[types.group_join]: controllers.chats.receiveGroupJoin,
[types.group_leave]: controllers.chats.receiveGroupLeave,
})
} catch(e) {
throw e
}
}
async function set(app) {
if(models && models.Subscription){
@ -64,6 +34,7 @@ async function set(app) {
app.post('/chats/:chat_id/:mute_unmute', controllers.chats.mute)
app.delete('/chat/:id', controllers.chats.deleteChat)
app.put('/chat/:id', controllers.chats.addGroupMembers)
app.post('/tribe', controllers.chats.joinTribe)
app.post('/contacts/tokens', controllers.contacts.generateToken)
@ -140,4 +111,4 @@ const login = (req, res) => {
}
}
export {set, iniGrpcSubscriptions}
export {set, controllers}

16
api/controllers/invoices.ts

@ -8,6 +8,7 @@ import { sendNotification } from '../hub'
import { success } from '../utils/res'
import {sendConfirmation} from './confirmations'
import * as path from 'path'
import * as network from '../network'
const constants = require(path.join(__dirname,'../../config/constants.json'))
@ -140,7 +141,7 @@ const createInvoice = async (req, res) => {
})
success(res, jsonUtils.messageToJson(message, chat))
helpers.sendMessage({
network.sendMessage({
chat: chat,
sender: owner,
type: constants.message_types.invoice,
@ -188,14 +189,14 @@ const receiveInvoice = async (payload) => {
var date = new Date();
date.setMilliseconds(0)
const { owner, sender, chat, msg_id } = await helpers.parseReceiveParams(payload)
const { owner, sender, chat, msg_id, chat_type, sender_alias } = await helpers.parseReceiveParams(payload)
if (!owner || !sender || !chat) {
return console.log('=> no group chat!')
}
const { memo, sat, msat, paymentHash, invoiceDate, expirationSeconds } = decodePaymentRequest(payment_request)
const message = await models.Message.create({
const msg:{[k:string]:any} = {
chatId: chat.id,
type: constants.message_types.invoice,
sender: sender.id,
@ -210,7 +211,12 @@ const receiveInvoice = async (payload) => {
status: constants.statuses.pending,
createdAt: date,
updatedAt: date
})
}
const isTribe = chat_type===constants.chat_types.tribe
if(isTribe) {
msg.senderAlias = sender_alias
}
const message = await models.Message.create(msg)
console.log('received keysend invoice message', message.id)
socket.sendJson({
@ -218,7 +224,7 @@ const receiveInvoice = async (payload) => {
response: jsonUtils.messageToJson(message, chat, sender)
})
sendNotification(chat, sender.alias, 'message')
sendNotification(chat, msg.senderAlias||sender.alias, 'message')
const theChat = {...chat.dataValues, contactIds:[sender.id]}
sendConfirmation({ chat:theChat, sender: owner, msg_id })

32
api/controllers/media.ts

@ -13,6 +13,8 @@ import * as zbase32 from '../utils/zbase32'
import * as schemas from './schemas'
import {sendConfirmation} from './confirmations'
import * as path from 'path'
import * as network from '../network'
import * as meme from '../utils/meme'
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname,'../../config/app.json'))[env]
@ -115,7 +117,7 @@ const sendAttachmentMessage = async (req, res) => {
mediaKey: media_key_map,
mediaType: mediaType,
}
helpers.sendMessage({
network.sendMessage({
chat: chat,
sender: owner,
type: constants.message_types.attachment,
@ -183,7 +185,7 @@ const purchase = async (req, res) => {
const msg={
amount, mediaToken:media_token, id:message.id,
}
helpers.sendMessage({
network.sendMessage({
chat: {...chat.dataValues, contactIds:[contact_id]},
sender: owner,
type: constants.message_types.purchase,
@ -210,7 +212,6 @@ const receivePurchase = async (payload) => {
return console.log('=> group chat not found!')
}
const message = await models.Message.create({
chatId: chat.id,
sender: sender.id,
@ -258,7 +259,7 @@ const receivePurchase = async (payload) => {
}
if (amount < price) { // didnt pay enough
return helpers.sendMessage({ // "purchase_deny"
return network.sendMessage({ // "purchase_deny"
chat: {...chat.dataValues, contactIds:[sender.id]}, // only send back to sender
sender: owner,
amount: amount,
@ -287,7 +288,7 @@ const receivePurchase = async (payload) => {
meta: {amt:amount},
pubkey: sender.publicKey,
})
helpers.sendMessage({
network.sendMessage({
chat: {...chat.dataValues, contactIds:[sender.id]}, // only to sender
sender: owner,
type: constants.message_types.purchase_accept,
@ -391,7 +392,7 @@ const receiveAttachment = async (payload) => {
var date = new Date();
date.setMilliseconds(0)
const {owner, sender, chat, mediaToken, mediaKey, mediaType, content, msg_id} = await helpers.parseReceiveParams(payload)
const {owner, sender, chat, mediaToken, mediaKey, mediaType, content, msg_id, chat_type, sender_alias} = await helpers.parseReceiveParams(payload)
if(!owner || !sender || !chat) {
return console.log('=> no group chat!')
}
@ -408,17 +409,21 @@ const receiveAttachment = async (payload) => {
if(mediaToken) msg.mediaToken = mediaToken
if(mediaKey) msg.mediaKey = mediaKey
if(mediaType) msg.mediaType = mediaType
const isTribe = chat_type===constants.chat_types.tribe
if(isTribe) {
msg.senderAlias = sender_alias
}
const message = await models.Message.create(msg)
console.log('saved attachment', message.dataValues)
// console.log('saved attachment', message.dataValues)
socket.sendJson({
type: 'attachment',
response: jsonUtils.messageToJson(message, chat, sender)
})
sendNotification(chat, sender.alias, 'message')
sendNotification(chat, msg.senderAlias||sender.alias, 'message')
const theChat = {...chat.dataValues, contactIds:[sender.id]}
sendConfirmation({ chat:theChat, sender: owner, msg_id })
@ -466,7 +471,10 @@ async function cycleMediaToken() {
if (process.env.TEST_LDAT) testLDAT()
const mt = await getMediaToken(null)
if(mt) console.log('=> [meme] authed!')
if(mt) {
console.log('=> [meme] authed!')
meme.setMediaToken(mt)
}
new CronJob('1 * * * *', function() { // every hour
getMediaToken(true)
@ -477,9 +485,9 @@ async function cycleMediaToken() {
}
const mediaURL = 'http://' + config.media_host + '/'
let mediaToken;
async function getMediaToken(force) {
if(!force && mediaToken) return mediaToken
if(!force && meme.mediaToken) return meme.mediaToken
await helpers.sleep(3000)
try {
const res = await rp.get(mediaURL+'ask')
@ -507,7 +515,7 @@ async function getMediaToken(force) {
if(!(body && body.token)){
throw new Error('no token')
}
mediaToken = body.token
meme.setMediaToken(body.token)
return body.token
} catch(e) {
throw e

22
api/controllers/messages.ts

@ -8,6 +8,7 @@ import * as helpers from '../helpers'
import { success } from '../utils/res'
import {sendConfirmation} from './confirmations'
import * as path from 'path'
import * as network from '../network'
const constants = require(path.join(__dirname,'../../config/constants.json'))
@ -105,9 +106,10 @@ const sendMessage = async (req, res) => {
remote_text,
chat_id,
remote_text_map,
amount,
} = req.body
console.log('[sendMessage]',)
console.log('[sendMessage]',remote_text_map)
var date = new Date();
date.setMilliseconds(0)
@ -124,21 +126,23 @@ const sendMessage = async (req, res) => {
chatId: chat.id,
type: constants.message_types.message,
sender: owner.id,
amount: amount||0,
date: date,
messageContent: text,
remoteMessageContent,
status: constants.statuses.pending,
createdAt: date,
updatedAt: date
updatedAt: date,
}
// console.log(msg)
const message = await models.Message.create(msg)
success(res, jsonUtils.messageToJson(message, chat))
helpers.sendMessage({
network.sendMessage({
chat: chat,
sender: owner,
amount: amount||0,
type: constants.message_types.message,
message: {
id: message.id,
@ -154,13 +158,13 @@ const receiveMessage = async (payload) => {
date.setMilliseconds(0)
const total_spent = 1
const {owner, sender, chat, content, msg_id} = await helpers.parseReceiveParams(payload)
const {owner, sender, chat, content, msg_id, chat_type, sender_alias} = await helpers.parseReceiveParams(payload)
if(!owner || !sender || !chat) {
return console.log('=> no group chat!')
}
const text = content
const message = await models.Message.create({
const msg:{[k:string]:any} = {
chatId: chat.id,
type: constants.message_types.message,
asciiEncodedTotal: total_spent,
@ -170,7 +174,11 @@ const receiveMessage = async (payload) => {
createdAt: date,
updatedAt: date,
status: constants.statuses.received
})
}
if(chat_type===constants.chat_types.tribe) {
msg.senderAlias = sender_alias
}
const message = await models.Message.create(msg)
console.log('saved message', message.dataValues)
@ -179,7 +187,7 @@ const receiveMessage = async (payload) => {
response: jsonUtils.messageToJson(message, chat, sender)
})
sendNotification(chat, sender.alias, 'message')
sendNotification(chat, msg.senderAlias||sender.alias, 'message')
const theChat = {...chat.dataValues, contactIds:[sender.id]}
sendConfirmation({ chat:theChat, sender: owner, msg_id })

17
api/controllers/payment.ts

@ -7,6 +7,7 @@ import { success } from '../utils/res'
import * as lightning from '../utils/lightning'
import {tokenFromTerms} from '../utils/ldat'
import * as constants from '../../config/constants.json'
import * as network from '../network'
const sendPayment = async (req, res) => {
const {
@ -25,11 +26,14 @@ const sendPayment = async (req, res) => {
console.log('[send payment]', req.body)
const owner = await models.Contact.findOne({ where: { isOwner: true }})
if (destination_key && !contact_id && !chat_id) {
return helpers.performKeysendMessage({
sender:owner,
destination_key,
amount,
msg:'{}',
msg:{},
success: () => {
console.log('payment sent!')
success(res, {destination_key, amount})
@ -42,8 +46,6 @@ const sendPayment = async (req, res) => {
})
}
const owner = await models.Contact.findOne({ where: { isOwner: true }})
const chat = await helpers.findOrCreateChat({
chat_id,
owner_id: owner.id,
@ -95,7 +97,7 @@ const sendPayment = async (req, res) => {
theChat = {...chat.dataValues, contactIds:contact_ids}
if(remote_text_map) msgToSend.content = remote_text_map
}
helpers.sendMessage({
network.sendMessage({
chat: theChat,
sender: owner,
type: constants.message_types.direct_payment,
@ -123,7 +125,7 @@ const receivePayment = async (payload) => {
var date = new Date();
date.setMilliseconds(0)
const {owner, sender, chat, amount, content, mediaType, mediaToken} = await helpers.parseReceiveParams(payload)
const {owner, sender, chat, amount, content, mediaType, mediaToken, chat_type, sender_alias} = await helpers.parseReceiveParams(payload)
if(!owner || !sender || !chat) {
return console.log('=> no group chat!')
}
@ -141,6 +143,9 @@ const receivePayment = async (payload) => {
if(content) msg.messageContent = content
if(mediaType) msg.mediaType = mediaType
if(mediaToken) msg.mediaToken = mediaToken
if(chat_type===constants.chat_types.tribe) {
msg.senderAlias = sender_alias
}
const message = await models.Message.create(msg)
@ -151,7 +156,7 @@ const receivePayment = async (payload) => {
response: jsonUtils.messageToJson(message, chat, sender)
})
sendNotification(chat, sender.alias, 'message')
sendNotification(chat, msg.senderAlias||sender.alias, 'message')
}
const listPayments = async (req, res) => {

3
api/controllers/subscriptions.ts

@ -9,6 +9,7 @@ import * as helpers from '../helpers'
import * as rsa from '../crypto/rsa'
import * as moment from 'moment'
import * as path from 'path'
import * as network from '../network'
const constants = require(path.join(__dirname,'../../config/constants.json'))
@ -121,7 +122,7 @@ async function sendSubscriptionPayment(sub, isFirstMessage) {
const contact = await models.Contact.findByPk(sub.contactId)
const enc = rsa.encrypt(contact.contactKey, text)
helpers.sendMessage({
network.sendMessage({
chat: chat,
sender: owner,
type: constants.message_types.direct_payment,

27
api/crypto/rsa.ts

@ -26,6 +26,27 @@ export function decrypt(privateKey, enc){
}
}
export function genKeys(): Promise<{[k:string]:string}>{
return new Promise((resolve, reject)=>{
crypto.generateKeyPair('rsa', {
modulusLength: 2048
}, (err, publicKey, privKey)=>{
const pubPEM = publicKey.export({
type:'pkcs1',format:'pem'
})
const pubBase64 = cert.unpub(pubPEM)
const privPEM = privKey.export({
type:'pkcs1',format:'pem'
})
const privBase64 = cert.unpriv(privPEM)
resolve({
public: pubBase64,
private: privBase64,
})
})
})
}
export function testRSA(){
crypto.generateKeyPair('rsa', {
modulusLength: 2048
@ -50,6 +71,12 @@ const cert = {
s = s.replace('-----END RSA PUBLIC KEY-----','')
return s.replace(/[\r\n]+/gm, '')
},
unpriv: function(key){
let s = key
s = s.replace('-----BEGIN RSA PRIVATE KEY-----','')
s = s.replace('-----END RSA PRIVATE KEY-----','')
return s.replace(/[\r\n]+/gm, '')
},
pub:function(key){
return '-----BEGIN RSA PUBLIC KEY-----\n' +
key + '\n' +

99
api/grpc/index.ts

@ -3,8 +3,8 @@ import * as socket from '../utils/socket'
import { sendNotification, sendInvoice } from '../hub'
import * as jsonUtils from '../utils/json'
import * as decodeUtils from '../utils/decode'
import {loadLightning, SPHINX_CUSTOM_RECORD_KEY, verifyAscii} from '../utils/lightning'
import * as controllers from '../controllers'
import {loadLightning} from '../utils/lightning'
import * as network from '../network'
import * as moment from 'moment'
import * as path from 'path'
@ -12,107 +12,18 @@ const constants = require(path.join(__dirname,'../../config/constants.json'))
const ERR_CODE_UNAVAILABLE = 14
const ERR_CODE_STREAM_REMOVED = 2
// VERIFY PUBKEY OF SENDER
async function parseAndVerifyPayload(data){
let payload
const li = data.lastIndexOf('}')
const msg = data.substring(0,li+1)
const sig = data.substring(li+1)
try {
payload = JSON.parse(msg)
if(payload) {
const v = await verifyAscii(msg, sig)
if(v && v.valid && v.pubkey) {
payload.sender = payload.sender||{}
payload.sender.pub_key=v.pubkey
return payload
} else {
console.error('[GRPC] invalid payload signature')
}
}
} catch(e) {
console.error('[GRPC] failed to parse msg')
return null
}
}
async function parseKeysendInvoice(i, actions){
const recs = i.htlcs && i.htlcs[0] && i.htlcs[0].custom_records
const buf = recs && recs[SPHINX_CUSTOM_RECORD_KEY]
const data = buf && buf.toString()
const value = i && i.value && parseInt(i.value)
if(!data) {
console.error('[GRPC] no keysend data received')
return
}
let payload
if(data[0]==='{'){
try {
payload = await parseAndVerifyPayload(data)
} catch(e){
console.error('[GRPC] failed to parse and verify payload')
}
} else {
const threads = weave(data)
if(threads) {
try {
payload = await parseAndVerifyPayload(threads)
} catch(e){
console.error('[GRPC] failed to parse and verify payload II')
}
}
}
if(payload){
const dat = payload.content || payload
if(value && dat && dat.message){
dat.message.amount = value // ADD IN TRUE VALUE
}
if(actions[payload.type]) {
actions[payload.type](payload)
} else {
console.log('Incorrect payload type:', payload.type)
}
} else {
console.error('[GRPC] no payload')
}
}
const chunks = {}
function weave(p){
const pa = p.split('_')
if(pa.length<4) return
const ts = pa[0]
const i = pa[1]
const n = pa[2]
const m = pa.filter((u,i)=>i>2).join('_')
chunks[ts] = chunks[ts] ? [...chunks[ts], {i,n,m}] : [{i,n,m}]
if(chunks[ts].length===parseInt(n)){
// got em all!
const all = chunks[ts]
let payload = ''
all.slice().sort((a,b)=>a.i-b.i).forEach(obj=>{
payload += obj.m
})
delete chunks[ts]
return payload
}
}
function subscribeInvoices(actions) {
function subscribeInvoices(parseKeysendInvoice) {
return new Promise(async(resolve,reject)=>{
const lightning = await loadLightning()
var call = lightning.subscribeInvoices()
call.on('data', async function(response) {
// console.log('subscribed invoices', { response })
console.log('[GRPC] subscribeInvoices received')
if (response['state'] !== 'SETTLED') {
return
}
// console.log("IS KEYSEND", response.is_keysend)
if(response.is_keysend) {
parseKeysendInvoice(response, actions)
parseKeysendInvoice(response)
} else {
const invoice = await models.Message.findOne({ where: { type: constants.message_types.invoice, payment_request: response['payment_request'] } })
if (invoice == null) {
@ -210,7 +121,7 @@ async function reconnectToLND(innerCtx:number){
i++
console.log(`=> [lnd] reconnecting... attempt #${i}`)
try {
await controllers.iniGrpcSubscriptions()
await network.initGrpcSubscriptions()
const now = moment().format('YYYY-MM-DD HH:mm:ss').trim();
console.log(`=> [lnd] reconnected! ${now}`)
} catch(e) {

94
api/helpers.ts

@ -1,7 +1,6 @@
import { models } from './models'
import * as md5 from 'md5'
import { keysendMessage } from './utils/lightning'
import {personalizeMessage} from './utils/msg'
import * as network from './network'
const constants = require('../config/constants.json');
@ -57,9 +56,10 @@ const sendContactKeys = async (args) => {
destination_key = contact.publicKey
}
performKeysendMessage({
sender,
destination_key,
amount: 3,
msg: JSON.stringify(msg),
msg,
success: (data) => {
yes = data
},
@ -76,55 +76,14 @@ const sendContactKeys = async (args) => {
}
}
const sendMessage = async (params) => {
const { type, chat, message, sender, amount, success, failure } = params
const m = newmsg(type, chat, sender, message)
const contactIds = typeof chat.contactIds==='string' ? JSON.parse(chat.contactIds) : chat.contactIds
let yes:any = null
let no:any = null
console.log('all contactIds',contactIds)
await asyncForEach(contactIds, async contactId => {
if (contactId == sender.id) {
return
}
console.log('-> sending to contact #', contactId)
const contact = await models.Contact.findOne({ where: { id: contactId } })
const destkey = contact.publicKey
const finalMsg = await personalizeMessage(m, contactId, destkey)
const opts = {
dest: destkey,
data: JSON.stringify(finalMsg),
amt: amount || 3,
}
try {
const r = await keysendMessage(opts)
yes = r
} catch (e) {
console.log("KEYSEND ERROR", e)
no = e
}
})
if(yes){
if(success) success(yes)
} else {
if(failure) failure(no)
}
}
const performKeysendMessage = async ({ destination_key, amount, msg, success, failure }) => {
const performKeysendMessage = async ({ destination_key, amount, msg, success, failure, sender }) => {
const opts = {
dest: destination_key,
data: msg || JSON.stringify({}),
data: msg || {},
amt: Math.max(amount, 3)
}
try {
const r = await keysendMessage(opts)
const r = await network.signAndSend(opts, sender.publicKey)
console.log("=> external keysend")
if (success) success(r)
} catch (e) {
@ -175,36 +134,46 @@ async function sleep(ms) {
async function parseReceiveParams(payload) {
const dat = payload.content || payload
const sender_pub_key = dat.sender.pub_key
const sender_alias = dat.sender.alias
const chat_uuid = dat.chat.uuid
const chat_type = dat.chat.type
const chat_members: { [k: string]: any } = dat.chat.members || {}
const chat_name = dat.chat.name
const chat_key = dat.chat.groupKey
const chat_host = dat.chat.host
const amount = dat.message.amount
const content = dat.message.content
const mediaToken = dat.message.mediaToken
const msg_id = dat.message.id||0
const mediaKey = dat.message.mediaKey
const mediaType = dat.message.mediaType
const isTribeOwner = dat.isTribeOwner?true:false
const isGroup = chat_type && chat_type == constants.chat_types.group
const isConversation = !chat_type || (chat_type && chat_type == constants.chat_types.conversation)
let sender
let chat
const owner = await models.Contact.findOne({ where: { isOwner: true } })
if (isGroup) {
sender = await models.Contact.findOne({ where: { publicKey: sender_pub_key } })
chat = await models.Chat.findOne({ where: { uuid: chat_uuid } })
} else {
if (isConversation) {
sender = await findOrCreateContactByPubkey(sender_pub_key)
chat = await findOrCreateChatByUUID(
chat_uuid, [parseInt(owner.id), parseInt(sender.id)]
)
if(sender.fromGroup) { // if a private msg received, update the contact
await sender.update({fromGroup:false})
}
} else { // group
sender = await models.Contact.findOne({ where: { publicKey: sender_pub_key } })
// inject a "sender" with an alias
if(!sender && chat_type == constants.chat_types.tribe){
sender = {id:0, alias:sender_alias}
}
chat = await models.Chat.findOne({ where: { uuid: chat_uuid } })
}
return { owner, sender, chat, sender_pub_key, chat_uuid, amount, content, mediaToken, mediaKey, mediaType, chat_type, msg_id, chat_members, chat_name }
return { owner, sender, chat, sender_pub_key, sender_alias, isTribeOwner, chat_uuid, amount, content, mediaToken, mediaKey, mediaType, chat_type, msg_id, chat_members, chat_name, chat_host, chat_key }
}
export {
findOrCreateChat,
sendMessage,
sendContactKeys,
findOrCreateContactByPubkey,
findOrCreateChatByUUID,
@ -219,23 +188,6 @@ async function asyncForEach(array, callback) {
}
}
function newmsg(type, chat, sender, message){
return {
type: type,
chat: {
uuid: chat.uuid,
...chat.name && { name: chat.name },
...chat.type && { type: chat.type },
...chat.members && { members: chat.members },
},
message: message,
// sender: {
// pub_key: sender.publicKey,
// // ...sender.contactKey && {contact_key: sender.contactKey}
// }
}
}
function newkeyexchangemsg(type, sender){
return {
type: type,

3
api/models/index.ts

@ -6,6 +6,7 @@ import Invite from './ts/invite'
import Message from './ts/message'
import Subscription from './ts/subscription'
import MediaKey from './ts/mediaKey'
import ChatMember from './ts/chatMember'
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname,'../../config/config.json'))[env]
@ -13,7 +14,7 @@ const config = require(path.join(__dirname,'../../config/config.json'))[env]
const sequelize = new Sequelize({
...config,
logging: process.env.SQL_LOG==='true' ? console.log : false,
models: [Chat,Contact,Invite,Message,Subscription,MediaKey]
models: [Chat,Contact,Invite,Message,Subscription,MediaKey,ChatMember]
})
const models = sequelize.models

18
api/models/ts/chat.ts

@ -45,4 +45,22 @@ export default class Chat extends Model<Chat> {
})
deleted: boolean
@Column
groupKey: string
@Column
groupPrivateKey: string
@Column
host: string
@Column
priceToJoin: number
@Column
pricePerMessage: number
@Column
ownerPubkey: string
}

24
api/models/ts/chatMember.ts

@ -0,0 +1,24 @@
import { Table, Column, Model } from 'sequelize-typescript';
@Table({tableName: 'sphinx_chat_members', underscored: true})
export default class ChatMember extends Model<ChatMember> {
@Column
chatId: number
@Column
contactId: number
@Column
role: number
@Column
totalSpent: number
@Column
totalMessages: number
@Column
lastActive: Date
}

3
api/models/ts/contact.ts

@ -54,4 +54,7 @@ export default class Contact extends Model<Contact> {
@Column
updatedAt: Date
@Column
fromGroup: boolean
}

3
api/models/ts/message.ts

@ -80,4 +80,7 @@ export default class Message extends Model<Message> {
@Column
updatedAt: Date
@Column
senderAlias: string // for tribes, no "sender" id maybe
}

14
api/network/index.ts

@ -0,0 +1,14 @@
import {sendMessage,signAndSend} from './send'
import {initGrpcSubscriptions,initTribesSubscriptions,parseKeysendInvoice} from './receive'
/*
Abstracts between lightning network and MQTT depending on Chat type and sender
*/
export {
sendMessage,signAndSend,
initGrpcSubscriptions,
initTribesSubscriptions,
parseKeysendInvoice,
}

88
api/network/modify.ts

@ -0,0 +1,88 @@
import * as path from 'path'
import RNCryptor from '../utils/rncryptor'
import * as fetch from 'node-fetch'
import {parseLDAT} from '../utils/ldat'
import * as rsa from '../crypto/rsa'
import * as crypto from 'crypto'
import * as meme from '../utils/meme'
import * as FormData from 'form-data'
const constants = require(path.join(__dirname,'../../config/constants.json'))
const msgtypes = constants.message_types
export async function modifyPayload(payload, chat) {
if(payload.type===msgtypes.attachment) {
const mt = payload.message && payload.message.mediaToken
const key = payload.message && payload.message.mediaKey
const typ = payload.message && payload.message.mediaType
if(!mt || !key) return payload
const terms = parseLDAT(mt)
if(!terms.host) return payload
try {
const r = await fetch(`https://${terms.host}/file/${mt}`, {
headers: {'Authorization': `Bearer ${meme.mediaToken}`}
})
const buf = await r.buffer()
const decMediaKey = rsa.decrypt(chat.groupPrivateKey, key)
const imgBase64 = RNCryptor.Decrypt(decMediaKey, buf.toString('base64'))
const newKey = crypto.randomBytes(20).toString('hex')
const encImg = RNCryptor.Encrypt(newKey, imgBase64)
var encImgBuffer = Buffer.from(encImg,'base64');
const form = new FormData()
form.append('file', encImgBuffer, {
contentType: typ||'image/jpg',
filename: 'Image.jpg',
knownLength:encImgBuffer.length,
})
const formHeaders = form.getHeaders()
const resp = await fetch(`https://${terms.host}/file`, {
method: 'POST',
headers: {
...formHeaders, // THIS IS REQUIRED!!!
'Authorization': `Bearer ${meme.mediaToken}`,
},
body:form
})
let json = await resp.json()
if(!json.muid) return payload
// PUT NEW TERMS, to finish in personalizeMessage
const amt = terms.meta&&terms.meta.amt
const ttl = terms.meta&&terms.meta.ttl
const mediaTerms: {[k:string]:any} = {
muid:json.muid, ttl:ttl||31536000,
meta:{...amt && {amt}},
skipSigning: amt ? true : false // only sign if its free
}
const encKey = rsa.encrypt(chat.groupKey, newKey)
return fillmsg(payload, {mediaTerms,mediaKey:encKey}) // key is re-encrypted later
} catch(e) {
console.log("[modify] error", e)
return payload
}
// how to link w og msg? ogMediaToken?
} else {
return payload
}
}
function fillmsg(full, props){
return {
...full, message: {
...full.message,
...props,
}
}
}

209
api/network/receive.ts

@ -0,0 +1,209 @@
import * as path from 'path'
import * as lndService from '../grpc'
import {getInfo} from '../utils/lightning'
import {controllers} from '../controllers'
import * as tribes from '../utils/tribes'
import {SPHINX_CUSTOM_RECORD_KEY, verifyAscii} from '../utils/lightning'
import { models } from '../models'
import {sendMessage} from './send'
import {modifyPayload} from './modify'
import {decryptMessage,encryptTribeBroadcast} from '../utils/msg'
const constants = require(path.join(__dirname,'../../config/constants.json'))
const msgtypes = constants.message_types
const typesToForward=[
msgtypes.message, msgtypes.group_join, msgtypes.group_leave, msgtypes.attachment
]
const typesToModify=[
msgtypes.attachment
]
const typesThatNeedPricePerMessage = [
msgtypes.message, msgtypes.attachment
]
async function onReceive(payload){
console.log("=>>> onReceive",payload)
// if tribe, owner must forward to MQTT
let doAction = true
const toAddIn:{[k:string]:any} = {}
const isTribe = payload.chat && payload.chat.type===constants.chat_types.tribe
if(isTribe && typesToForward.includes(payload.type)){
const needsPricePerJoin = typesThatNeedPricePerMessage.includes(payload.type)
const chat = await models.Chat.findOne({where:{uuid:payload.chat.uuid}})
const tribeOwnerPubKey = chat && chat.ownerPubkey
const owner = await models.Contact.findOne({where: {isOwner:true}})
if(owner.publicKey===tribeOwnerPubKey){
toAddIn.isTribeOwner = true
// CHECK THEY ARE IN THE GROUP if message
if(needsPricePerJoin) {
const senderContact = await models.Contact.findOne({where:{publicKey:payload.sender.pub_key}})
const senderMember = senderContact && await models.ChatMember.findOne({where:{contactId:senderContact.id, chatId:chat.id}})
if(!senderMember) doAction=false
}
// CHECK PRICES
if(needsPricePerJoin) {
if(payload.message.amount<chat.pricePerMessage) doAction=false
}
// check price to join
if(payload.type===msgtypes.group_join) {
if(payload.message.amount<chat.priceToJoin) doAction=false
}
if(doAction) forwardMessageToTribe(payload)
else console.log('=> insufficient payment for this action')
}
}
if(doAction) doTheAction({...payload, ...toAddIn})
}
async function doTheAction(data){
let payload = data
if(payload.isTribeOwner) {
// decrypt and re-encrypt with self pubkey
const chat = await models.Chat.findOne({where:{uuid:payload.chat.uuid}})
const pld = await decryptMessage(data, chat)
const me = await models.Contact.findOne({where:{isOwner:true}})
payload = await encryptTribeBroadcast(pld, me, true) // true=isTribeOwner
}
if(ACTIONS[payload.type]) {
ACTIONS[payload.type](payload)
} else {
console.log('Incorrect payload type:', payload.type)
}
}
async function forwardMessageToTribe(ogpayload){
const chat = await models.Chat.findOne({where:{uuid:ogpayload.chat.uuid}})
let payload
if(typesToModify.includes(ogpayload.type)){
payload = await modifyPayload(ogpayload, chat)
} else {
payload = ogpayload
}
//console.log("FORWARD TO TRIBE",payload) // filter out the sender?
//const sender = await models.Contact.findOne({where:{publicKey:payload.sender.pub_key}})
const owner = await models.Contact.findOne({where:{isOwner:true}})
const type = payload.type
const message = payload.message
// HERE: NEED TO MAKE SURE ALIAS IS UNIQUE
// ASK xref TABLE and put alias there too?
sendMessage({
sender: {
...owner.dataValues,
...payload.sender&&payload.sender.alias && {alias:payload.sender.alias}
},
chat, type, message,
skipPubKey: payload.sender.pub_key,
success: ()=>{},
receive: ()=>{}
})
}
const ACTIONS = {
[msgtypes.contact_key]: controllers.contacts.receiveContactKey,
[msgtypes.contact_key_confirmation]: controllers.contacts.receiveConfirmContactKey,
[msgtypes.message]: controllers.messages.receiveMessage,
[msgtypes.invoice]: controllers.invoices.receiveInvoice,
[msgtypes.direct_payment]: controllers.payments.receivePayment,
[msgtypes.confirmation]: controllers.confirmations.receiveConfirmation,
[msgtypes.attachment]: controllers.media.receiveAttachment,
[msgtypes.purchase]: controllers.media.receivePurchase,
[msgtypes.purchase_accept]: controllers.media.receivePurchaseAccept,
[msgtypes.purchase_deny]: controllers.media.receivePurchaseDeny,
[msgtypes.group_create]: controllers.chats.receiveGroupCreateOrInvite,
[msgtypes.group_invite]: controllers.chats.receiveGroupCreateOrInvite,
[msgtypes.group_join]: controllers.chats.receiveGroupJoin,
[msgtypes.group_leave]: controllers.chats.receiveGroupLeave,
}
export async function initGrpcSubscriptions() {
try{
await getInfo()
await lndService.subscribeInvoices(parseKeysendInvoice)
} catch(e) {
throw e
}
}
export async function initTribesSubscriptions(){
tribes.connect(async(topic, message)=>{ // onMessage callback
try{
const msg = message.toString()
console.log("=====> msg received! TOPIC", topic, "MESSAGE", msg)
// check topic is signed by sender?
const payload = await parseAndVerifyPayload(msg)
onReceive(payload)
} catch(e){}
})
}
// VERIFY PUBKEY OF SENDER from sig
async function parseAndVerifyPayload(data){
let payload
const li = data.lastIndexOf('}')
const msg = data.substring(0,li+1)
const sig = data.substring(li+1)
try {
payload = JSON.parse(msg)
if(payload) {
const v = await verifyAscii(msg, sig)
if(v && v.valid && v.pubkey) {
payload.sender = payload.sender||{}
payload.sender.pub_key=v.pubkey
return payload
} else {
return payload // => RM THIS
}
}
} catch(e) {
if(payload) return payload // => RM THIS
return null
}
}
export async function parseKeysendInvoice(i){
const recs = i.htlcs && i.htlcs[0] && i.htlcs[0].custom_records
const buf = recs && recs[SPHINX_CUSTOM_RECORD_KEY]
const data = buf && buf.toString()
const value = i && i.value && parseInt(i.value)
if(!data) return
let payload
if(data[0]==='{'){
try {
payload = await parseAndVerifyPayload(data)
} catch(e){}
} else {
const threads = weave(data)
if(threads) payload = await parseAndVerifyPayload(threads)
}
if(payload){
const dat = payload
if(value && dat && dat.message){
dat.message.amount = value // ADD IN TRUE VALUE
}
onReceive(dat)
}
}
const chunks = {}
function weave(p){
const pa = p.split('_')
if(pa.length<4) return
const ts = pa[0]
const i = pa[1]
const n = pa[2]
const m = pa.filter((u,i)=>i>2).join('_')
chunks[ts] = chunks[ts] ? [...chunks[ts], {i,n,m}] : [{i,n,m}]
if(chunks[ts].length===parseInt(n)){
// got em all!
const all = chunks[ts]
let payload = ''
all.slice().sort((a,b)=>a.i-b.i).forEach(obj=>{
payload += obj.m
})
delete chunks[ts]
return payload
}
}

153
api/network/send.ts

@ -0,0 +1,153 @@
import { models } from '../models'
import * as LND from '../utils/lightning'
import {personalizeMessage, decryptMessage} from '../utils/msg'
import * as path from 'path'
import * as tribes from '../utils/tribes'
const constants = require(path.join(__dirname,'../../config/constants.json'))
type NetworkType = undefined | 'mqtt' | 'lightning'
export async function sendMessage(params) {
const { type, chat, message, sender, amount, success, failure, skipPubKey } = params
const m = newmsg(type, chat, sender, message)
let msg = m
// console.log(type,message)
if(!(sender&&sender.publicKey)) {
console.log("NO SENDER?????")
return
}
let contactIds = (typeof chat.contactIds==='string' ? JSON.parse(chat.contactIds) : chat.contactIds) || []
if(contactIds.length===1) {
if (contactIds[0]===1) {
if(success) success(true)
return // if no contacts thats fine (like create public tribe)
}
}
let networkType:NetworkType = undefined
const isTribe = chat.type===constants.chat_types.tribe
let isTribeOwner = false
const chatUUID = chat.uuid
if(isTribe) {
if(type===constants.message_types.confirmation) {
return // dont send confs for tribe
}
console.log("is tribe!")
const tribeOwnerPubKey = chat.ownerPubkey
if(sender.publicKey===tribeOwnerPubKey){
console.log('im owner! mqtt!')
isTribeOwner = true
networkType = 'mqtt' // broadcast to all
// decrypt message.content and message.mediaKey w groupKey
msg = await decryptMessage(msg, chat)
} else {
// if tribe, send to owner only
const tribeOwner = await models.Contact.findOne({where: {publicKey:tribeOwnerPubKey}})
contactIds = tribeOwner ? [tribeOwner.id] : []
}
}
let yes:any = null
let no:any = null
console.log('all contactIds',contactIds)
await asyncForEach(contactIds, async contactId => {
if (contactId == 1) { // dont send to self
return
}
const contact = await models.Contact.findOne({ where: { id: contactId } })
const destkey = contact.publicKey
if(destkey===skipPubKey) {
return // skip (for tribe owner broadcasting, not back to the sender)
}
console.log('-> sending to ', contact.id, destkey)
const m = await personalizeMessage(msg, contact, isTribeOwner)
const opts = {
dest: destkey,
data: m,
amt: Math.max((amount||0), 3)
}
try {
const mqttTopic = networkType==='mqtt' ? `${destkey}/${chatUUID}` : ''
const r = await signAndSend(opts, sender.publicKey, mqttTopic)
yes = r
} catch (e) {
console.log("KEYSEND ERROR", e)
no = e
}
await sleep(2)
})
if(yes){
if(success) success(yes)
} else {
if(failure) failure(no)
}
}
export function signAndSend(opts, pubkey, mqttTopic?:string){
// console.log('sign and send!!!!',opts.data)
return new Promise(async function(resolve, reject) {
if(!opts.data || typeof opts.data!=='object') {
return reject('object plz')
}
let data = JSON.stringify(opts.data)
const sig = await LND.signAscii(data)
data = data + sig
// console.log("ACTUALLY SEND", mqttTopic)
try {
if(mqttTopic) {
await tribes.publish(mqttTopic, data)
} else {
await LND.keysendMessage({...opts,data})
}
resolve(true)
} catch(e) {
reject(e)
}
})
}
function newmsg(type, chat, sender, message){
const includeGroupKey = type===constants.message_types.group_create || type===constants.message_types.group_invite
const includeAlias = sender && sender.alias && chat.type===constants.chat_types.tribe
return {
type: type,
chat: {
uuid: chat.uuid,
...chat.name && { name: chat.name },
...(chat.type||chat.type===0) && { type: chat.type },
...chat.members && { members: chat.members },
...(includeGroupKey&&chat.groupKey) && { groupKey: chat.groupKey },
...(includeGroupKey&&chat.host) && { host: chat.host }
},
message: message,
sender: {
...includeAlias && {alias: sender.alias},
pub_key: sender.publicKey,
// ...sender.contactKey && {contact_key: sender.contactKey}
}
}
}
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
// function urlBase64FromHex(ascii){
// return Buffer.from(ascii,'hex').toString('base64').replace(/\//g, '_').replace(/\+/g, '-')
// }
// function urlBase64FromBytes(buf){
// return Buffer.from(buf).toString('base64').replace(/\//g, '_').replace(/\+/g, '-')
// }

11
api/utils/json.ts

@ -2,11 +2,14 @@ import {toSnake,toCamel} from '../utils/case'
import * as cronUtils from './cron'
function chatToJson(c) {
const chat = c.dataValues||c
if(!c) return {}
const ch = c.dataValues||c
const chat = JSON.parse(JSON.stringify(ch))
let contactIds = chat.contactIds || null
if(chat.contactIds && typeof chat.contactIds==='string'){
contactIds = JSON.parse(chat.contactIds)
}
delete chat.groupPrivateKey
return toSnake({
...chat,
contactIds
@ -14,6 +17,7 @@ function chatToJson(c) {
}
function messageToJson(msg, chat, contact?) {
if(!msg) return {}
const message = msg.dataValues||msg
let statusMap = message.statusMap || null
if(message.statusMap && typeof message.statusMap==='string'){
@ -27,7 +31,10 @@ function messageToJson(msg, chat, contact?) {
})
}
const contactToJson = (contact) => toSnake(contact.dataValues||contact)
function contactToJson(contact){
if(!contact) return {}
return toSnake(contact.dataValues||contact)
}
const inviteToJson = (invite) => toSnake(invite.dataValues||invite)

6
api/utils/ldat.ts

@ -87,7 +87,8 @@ function parseLDAT(ldat){
export {
startLDAT, parseLDAT, tokenFromTerms,
urlBase64, urlBase64FromAscii,
urlBase64FromBytes, testLDAT
urlBase64FromBytes, testLDAT,
urlBase64FromHex
}
async function testLDAT(){
@ -152,3 +153,6 @@ function urlBase64FromBytes(buf){
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, '-')
}

37
api/utils/lightning.ts

@ -106,7 +106,6 @@ const getRoute = async (pub_key, amt, callback) => {
const keysend = (opts) => {
return new Promise(async function(resolve, reject) {
let lightning = await loadLightning()
const randoStr = crypto.randomBytes(32).toString('hex');
const preimage = ByteBuffer.fromHex(randoStr)
const options = {
@ -141,9 +140,6 @@ async function keysendMessage(opts) {
if(!opts.data || typeof opts.data!=='string') {
return reject('string plz')
}
// SIGN HERE and append sig
const sig = await signAscii(opts.data)
opts.data = opts.data + sig
if(opts.data.length<MAX_MSG_LENGTH){
try {
@ -160,6 +156,7 @@ async function keysendMessage(opts) {
let fail = false
let res:any = null
const ts = new Date().valueOf()
// WEAVE MESSAGE If TOO LARGE
await asyncForEach(Array.from(Array(n)), async(u,i)=> {
const spliti = Math.ceil(opts.data.length/n)
const m = opts.data.substr(i*spliti, spliti)
@ -197,15 +194,6 @@ async function signAscii(ascii) {
}
}
async function verifyAscii(ascii,sig): Promise<{[k:string]:any}>{
try {
const r = await verifyMessage(ascii_to_hexa(ascii),sig)
return r
} catch(e) {
throw e
}
}
function listInvoices() {
return new Promise(async(resolve, reject)=> {
const lightning = await loadLightning()
@ -336,13 +324,21 @@ const signBuffer = (msg) => {
})
}
async function verifyBytes(msg,sig): Promise<{[k:string]:any}> {
try {
const r = await verifyMessage(msg.toString('hex'),sig)
return r
} catch(e) {
throw e
}
}
function verifyMessage(msg,sig): Promise<{[k:string]:any}> {
return new Promise(async(resolve, reject)=> {
let lightning = await loadLightning()
try {
const options = {
msg:ByteBuffer.fromHex(msg),
signature:sig,
signature:sig, // zbase32 encoded string
}
lightning.verifyMessage(options, function(err,res){
if(err || !res.pubkey) {
@ -356,8 +352,16 @@ function verifyMessage(msg,sig): Promise<{[k:string]:any}> {
}
})
}
async function verifyAscii(ascii,sig): Promise<{[k:string]:any}>{
try {
const r = await verifyMessage(ascii_to_hexa(ascii),sig)
return r
} catch(e) {
throw e
}
}
async function checkConnection(){
async function getInfo(): Promise<{[k:string]:any}>{
return new Promise((resolve,reject)=>{
const lightning = loadLightning()
lightning.getInfo({}, function(err, response) {
@ -391,13 +395,14 @@ export {
signMessage,
verifyMessage,
verifyAscii,
verifyBytes,
signAscii,
signBuffer,
LND_KEYSEND_KEY,
SPHINX_CUSTOM_RECORD_KEY,
listInvoices,
listAllPayments,
checkConnection,
getInfo,
listAllInvoices,
listAllPaymentsFull,
}

8
api/utils/meme.ts

@ -0,0 +1,8 @@
let mediaToken = ''
function setMediaToken(mt) {
mediaToken = mt
}
export {mediaToken, setMediaToken}

108
api/utils/msg.ts

@ -1,10 +1,19 @@
import { tokenFromTerms } from './ldat'
import * as path from 'path'
import * as rsa from '../crypto/rsa'
const constants = require(path.join(__dirname,'../../config/constants.json'))
function addInRemoteText(full:{[k:string]:any}, contactId){
function addInRemoteText(full:{[k:string]:any}, contactId, isTribe:boolean){
const m = full && full.message
if (!(m && m.content)) return full
if (!(typeof m.content==='object')) return full
if(isTribe) {
// if just one, send it (for tribe remote_text_map... is there a better way?)
if(m.content['chat']) {
return fillmsg(full, {content: m.content['chat']})
}
}
return fillmsg(full, {content: m.content[contactId+'']})
}
@ -18,12 +27,51 @@ function removeRecipientFromChatMembers(full:{[k:string]:any}, destkey){
return fillchatmsg(full, {members})
}
function addInMediaKey(full:{[k:string]:any}, contactId){
function removeAllNonAdminMembersIfTribe(full:{[k:string]:any}, destkey){
return full
// const c = full && full.chat
// if (!(c && c.members)) return full
// if (!(typeof c.members==='object')) return full
// const members = {...c.members}
// if(members[destkey]) delete members[destkey]
// return fillchatmsg(full, {members})
}
// THIS IS ONLY FOR TRIBE OWNER
// by this time the content and mediaKey are already in message as string
async function encryptTribeBroadcast(full:{[k:string]:any}, contact, isTribeOwner:boolean){
if(!isTribeOwner) return full
const chat = full && full.chat
const message = full && full.message
if (!message || !(chat && chat.type && chat.uuid)) return full
const obj: {[k:string]:any} = {}
if(isTribeOwner) { // has been previously decrypted
if(message.content) {
const encContent = await rsa.encrypt(contact.contactKey, message.content)
obj.content = encContent
}
if(message.mediaKey) {
const encMediaKey = await rsa.encrypt(contact.contactKey, message.mediaKey)
obj.mediaKey = encMediaKey
}
}
return fillmsg(full, obj)
}
function addInMediaKey(full:{[k:string]:any}, contactId, isTribe:boolean){
const m = full && full.message
if (!(m && m.mediaKey)) return full
if (!(m && m.mediaTerms)) return full
if (!(typeof m.mediaKey==='object')) return full
if(isTribe) {
if(m.mediaKey['chat']) { // "chat" is the key for tribes
const tribeMediaKey = m.mediaTerms.skipSigning?'':m.mediaKey['chat']
return fillmsg(full, {mediaKey:tribeMediaKey})
}
}
const mediaKey = m.mediaTerms.skipSigning ? '' : m.mediaKey[contactId+'']
return fillmsg(full, {mediaKey})
}
@ -49,13 +97,55 @@ async function finishTermsAndReceipt(full:{[k:string]:any}, destkey) {
return fullmsg
}
async function personalizeMessage(m,contactId,destkey){
// this is only for tribes
// DECRYPT EITHER STRING OR FIRST VAL IN OBJ
async function decryptMessage(full:{[k:string]:any}, chat) {
if(!chat.groupPrivateKey) return full
const m = full && full.message
if (!m) return full
const obj: {[k:string]:any} = {}
if(m.content) {
let content = m.content
if(typeof m.content==='object') {
if(m.content['chat']) {
content = m.content['chat']
}
}
const decContent = rsa.decrypt(chat.groupPrivateKey, content)
obj.content = decContent
}
if (m.mediaKey) {
let mediaKey = m.mediaKey
if(typeof m.mediaKey==='object') {
if(m.mediaKey['chat']) {
mediaKey = m.mediaKey['chat']
}
}
const decMediaKey = rsa.decrypt(chat.groupPrivateKey, mediaKey)
obj.mediaKey = decMediaKey
}
// console.log("OBJ FILLED",fillmsg(full, obj))
return fillmsg(full, obj)
}
async function personalizeMessage(m,contact,isTribeOwner:boolean){
const contactId = contact.id
const destkey = contact.publicKey
const cloned = JSON.parse(JSON.stringify(m))
const msg = addInRemoteText(cloned, contactId)
const cleanMsg = removeRecipientFromChatMembers(msg, destkey)
const msgWithMediaKey = addInMediaKey(cleanMsg, contactId)
const finalMsg = await finishTermsAndReceipt(msgWithMediaKey, destkey)
return finalMsg
const chat = cloned && cloned.chat
const isTribe = chat.type&&chat.type===constants.chat_types.tribe
const msgWithRemoteTxt = addInRemoteText(cloned, contactId, isTribe)
const cleanMsg = removeRecipientFromChatMembers(msgWithRemoteTxt, destkey)
const cleanerMsg = removeAllNonAdminMembersIfTribe(cleanMsg, destkey)
const msgWithMediaKey = addInMediaKey(cleanerMsg, contactId, isTribe)
const msgWithMediaToken = await finishTermsAndReceipt(msgWithMediaKey, destkey)
const encMsg = await encryptTribeBroadcast(msgWithMediaToken, contact, isTribeOwner)
return encMsg
}
function fillmsg(full, props){
@ -77,5 +167,5 @@ function fillchatmsg(full, props){
}
export {
personalizeMessage
personalizeMessage, decryptMessage, encryptTribeBroadcast,
}

5
api/utils/rncryptor/index.js

@ -0,0 +1,5 @@
import * as RNCryptor from './rncryptor'
export default RNCryptor

104
api/utils/rncryptor/rncryptor.js

@ -0,0 +1,104 @@
var sjcl = require('./sjcl')
var RNCryptor = {};
/*
Takes password string and salt WordArray
Returns key bitArray
*/
RNCryptor.KeyForPassword = function(password, salt) {
var hmacSHA1 = function (key) {
var hasher = new sjcl.misc.hmac(key, sjcl.hash.sha1);
this.encrypt = function () {
return hasher.encrypt.apply(hasher, arguments);
};
};
return sjcl.misc.pbkdf2(password, salt, 10000, 32 * 8, hmacSHA1);
}
/*
Takes password string and plaintext base64
options:
iv
encryption_salt
html_salt
Returns ciphertext base64
*/
RNCryptor.Encrypt = function(password, plaintextBase64, options) {
var plaintext = sjcl.codec.base64.toBits(plaintextBase64);
options = options || {}
var encryption_salt = options["encryption_salt"] || sjcl.random.randomWords(8 / 4); // FIXME: Need to seed PRNG
var encryption_key = RNCryptor.KeyForPassword(password, encryption_salt);
var hmac_salt = options["hmac_salt"] || sjcl.random.randomWords(8 / 4);
var hmac_key = RNCryptor.KeyForPassword(password, hmac_salt);
var iv = options["iv"] || sjcl.random.randomWords(16 / 4);
var version = sjcl.codec.hex.toBits("03");
var options = sjcl.codec.hex.toBits("01");
var message = sjcl.bitArray.concat(version, options);
message = sjcl.bitArray.concat(message, encryption_salt);
message = sjcl.bitArray.concat(message, hmac_salt);
message = sjcl.bitArray.concat(message, iv);
var aes = new sjcl.cipher.aes(encryption_key);
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
var encrypted = sjcl.mode.cbc.encrypt(aes, plaintext, iv);
message = sjcl.bitArray.concat(message, encrypted);
var hmac = new sjcl.misc.hmac(hmac_key).encrypt(message);
message = sjcl.bitArray.concat(message, hmac);
return sjcl.codec.base64.fromBits(message);
}
/*
Takes password string and message (ciphertext) base64
options:
iv
encryption_salt
html_salt
Returns plaintext base64
*/
RNCryptor.Decrypt = function(password, messageBase64, options) {
var message = sjcl.codec.base64.toBits(messageBase64);
options = options || {}
var version = sjcl.bitArray.extract(message, 0 * 8, 8);
var options = sjcl.bitArray.extract(message, 1 * 8, 8);
var encryption_salt = sjcl.bitArray.bitSlice(message, 2 * 8, 10 * 8);
var encryption_key = RNCryptor.KeyForPassword(password, encryption_salt);
var hmac_salt = sjcl.bitArray.bitSlice(message, 10 * 8, 18 * 8);
var hmac_key = RNCryptor.KeyForPassword(password, hmac_salt);
var iv = sjcl.bitArray.bitSlice(message, 18 * 8, 34 * 8);
var ciphertext_end = sjcl.bitArray.bitLength(message) - (32 * 8);
var ciphertext = sjcl.bitArray.bitSlice(message, 34 * 8, ciphertext_end);
var hmac = sjcl.bitArray.bitSlice(message, ciphertext_end);
var expected_hmac = new sjcl.misc.hmac(hmac_key).encrypt(sjcl.bitArray.bitSlice(message, 0, ciphertext_end));
// .equal is of consistent time
if (! sjcl.bitArray.equal(hmac, expected_hmac)) {
throw new sjcl.exception.corrupt("HMAC mismatch or bad password.");
}
var aes = new sjcl.cipher.aes(encryption_key);
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
var decrypted = sjcl.mode.cbc.decrypt(aes, ciphertext, iv);
return sjcl.codec.base64.fromBits(decrypted);
}
module.exports = RNCryptor

55
api/utils/rncryptor/sjcl.js

@ -0,0 +1,55 @@
"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
sjcl.cipher.aes=function(a){this.u[0][0][0]||this.N();var b,c,d,e,f=this.u[0][4],g=this.u[1];b=a.length;var h=1;if(4!==b&&6!==b&&8!==b)throw new sjcl.exception.invalid("invalid aes key size");this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&
255]]};
sjcl.cipher.aes.prototype={encrypt:function(a){return r(this,a,0)},decrypt:function(a){return r(this,a,1)},u:[[[],[],[],[],[]],[[],[],[],[],[]]],N:function(){var a=this.u[0],b=this.u[1],c=a[4],d=b[4],e,f,g,h=[],k=[],n,l,m,p;for(e=0;0x100>e;e++)k[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=n||1,g=k[g]||1)for(m=g^g<<1^g<<2^g<<3^g<<4,m=m>>8^m&255^99,c[f]=m,d[m]=f,l=h[e=h[n=h[f]]],p=0x1010101*l^0x10001*e^0x101*n^0x1010100*f,l=0x101*h[m]^0x1010100*m,e=0;4>e;e++)a[e][f]=l=l<<24^l>>>8,b[e][m]=p=p<<24^p>>>8;for(e=
0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}};
function r(a,b,c){if(4!==b.length)throw new sjcl.exception.invalid("invalid aes block size");var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,k,n,l=d.length/4-2,m,p=4,q=[0,0,0,0];h=a.u[c];a=h[0];var u=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m<l;m++)h=a[e>>>24]^u[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],k=a[f>>>24]^u[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],n=a[g>>>24]^u[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^u[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=k,g=n;for(m=
0;4>m;m++)q[c?3&-m:m]=x[e>>>24]<<24^x[f>>16&255]<<16^x[g>>8&255]<<8^x[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return q}
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.Y(a.slice(b/32),32-(b&31)).slice(1);return void 0===c?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.Y(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
b?0:32*(b-1)+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(32*a.length<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b=b&31;0<c&&b&&(a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return!1;var c=0,d;for(d=0;d<a.length;d++)c|=a[d]^b[d];return 0===
c},Y:function(a,b,c,d){var e;e=0;for(void 0===d&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},B:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]},byteswapM:function(a){var b,c;for(b=0;b<a.length;++b)c=a[b],a[b]=c>>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}};
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++)0===(d&3)&&(e=a[d/4]),b+=String.fromCharCode(e>>>8>>>8>>>8),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++)d=d<<8|a.charCodeAt(c),3===(c&3)&&(b.push(d),d=0);c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a=a+"00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,4*d)}};
sjcl.codec.base64={S:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.S,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.S,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;d<a.length;d++){h=f.indexOf(a.charAt(d));
if(0>h)throw new sjcl.exception.invalid("this isn't base64!");26<e?(e-=26,c.push(g^h>>>e),g=h<<32-e):(e+=6,g^=h<<32-e)}e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.N();a?(this.g=a.g.slice(0),this.f=a.f.slice(0),this.c=a.c):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.g=this.D.slice(0);this.f=[];this.c=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.f=sjcl.bitArray.concat(this.f,a);b=this.c;a=this.c=b+sjcl.bitArray.bitLength(a);if(0x1fffffffffffff<a)throw new sjcl.exception.invalid("Cannot hash more than 2^53 - 1 bits");if("undefined"!==typeof Uint32Array){var d=new Uint32Array(c),e=0;for(b=512+b-(512+b&0x1ff);b<=a;b+=512)this.l(d.subarray(16*e,
16*(e+1))),e+=1;c.splice(0,16*e)}else for(b=512+b-(512+b&0x1ff);b<=a;b+=512)this.l(c.splice(0,16));return this},finalize:function(){var a,b=this.f,c=this.g,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.c/0x100000000));for(b.push(this.c|0);b.length;)this.l(b.splice(0,16));this.reset();return c},D:[],b:[],N:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}for(var b=0,c=2,d,e;64>b;c++){e=!0;for(d=2;d*d<=c;d++)if(0===c%d){e=
!1;break}e&&(8>b&&(this.D[b]=a(Math.pow(c,.5))),this.b[b]=a(Math.pow(c,1/3)),b++)}},l:function(a){var b,c,d,e=this.g,f=this.b,g=e[0],h=e[1],k=e[2],n=e[3],l=e[4],m=e[5],p=e[6],q=e[7];for(b=0;64>b;b++)16>b?c=a[b]:(c=a[b+1&15],d=a[b+14&15],c=a[b&15]=(c>>>7^c>>>18^c>>>3^c<<25^c<<14)+(d>>>17^d>>>19^d>>>10^d<<15^d<<13)+a[b&15]+a[b+9&15]|0),c=c+q+(l>>>6^l>>>11^l>>>25^l<<26^l<<21^l<<7)+(p^l&(m^p))+f[b],q=p,p=m,m=l,l=n+c|0,n=k,k=h,h=g,g=c+(h&k^n&(h^k))+(h>>>2^h>>>13^h>>>22^h<<30^h<<19^h<<10)|0;e[0]=e[0]+g|
0;e[1]=e[1]+h|0;e[2]=e[2]+k|0;e[3]=e[3]+n|0;e[4]=e[4]+l|0;e[5]=e[5]+m|0;e[6]=e[6]+p|0;e[7]=e[7]+q|0}};sjcl.hash.sha1=function(a){a?(this.g=a.g.slice(0),this.f=a.f.slice(0),this.c=a.c):this.reset()};sjcl.hash.sha1.hash=function(a){return(new sjcl.hash.sha1).update(a).finalize()};
sjcl.hash.sha1.prototype={blockSize:512,reset:function(){this.g=this.D.slice(0);this.f=[];this.c=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.f=sjcl.bitArray.concat(this.f,a);b=this.c;a=this.c=b+sjcl.bitArray.bitLength(a);if(0x1fffffffffffff<a)throw new sjcl.exception.invalid("Cannot hash more than 2^53 - 1 bits");if("undefined"!==typeof Uint32Array){var d=new Uint32Array(c),e=0;for(b=this.blockSize+b-(this.blockSize+b&this.blockSize-1);b<=
a;b+=this.blockSize)this.l(d.subarray(16*e,16*(e+1))),e+=1;c.splice(0,16*e)}else for(b=this.blockSize+b-(this.blockSize+b&this.blockSize-1);b<=a;b+=this.blockSize)this.l(c.splice(0,16));return this},finalize:function(){var a,b=this.f,c=this.g,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.c/0x100000000));for(b.push(this.c|0);b.length;)this.l(b.splice(0,16));this.reset();return c},D:[1732584193,4023233417,2562383102,271733878,3285377520],
b:[1518500249,1859775393,2400959708,3395469782],l:function(a){var b,c,d,e,f,g,h=this.g,k;if("undefined"!==typeof Uint32Array)for(k=Array(80),c=0;16>c;c++)k[c]=a[c];else k=a;c=h[0];d=h[1];e=h[2];f=h[3];g=h[4];for(a=0;79>=a;a++)16<=a&&(b=k[a-3]^k[a-8]^k[a-14]^k[a-16],k[a]=b<<1|b>>>31),b=19>=a?d&e|~d&f:39>=a?d^e^f:59>=a?d&e|d&f|e&f:79>=a?d^e^f:void 0,b=(c<<5|c>>>27)+b+g+k[a]+this.b[Math.floor(a/20)]|0,g=f,f=e,e=d<<30|d>>>2,d=c,c=b;h[0]=h[0]+c|0;h[1]=h[1]+d|0;h[2]=h[2]+e|0;h[3]=h[3]+f|0;h[4]=h[4]+g|0}};
sjcl.mode.ccm={name:"ccm",F:[],listenProgress:function(a){sjcl.mode.ccm.F.push(a)},unListenProgress:function(a){a=sjcl.mode.ccm.F.indexOf(a);-1<a&&sjcl.mode.ccm.F.splice(a,1)},da:function(a){var b=sjcl.mode.ccm.F.slice(),c;for(c=0;c<b.length;c+=1)b[c](a)},encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,k=h.bitLength(c)/8,n=h.bitLength(g)/8;e=e||64;d=d||[];if(7>k)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;4>f&&n>>>8*f;f++);f<15-k&&(f=15-k);c=h.clamp(c,
8*(15-f));b=sjcl.mode.ccm.U(a,b,c,d,e,f);g=sjcl.mode.ccm.V(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),k=f.clamp(b,h-e),n=f.bitSlice(b,h-e),h=(h-e)/8;if(7>g)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));k=sjcl.mode.ccm.V(a,k,c,n,e,b);a=sjcl.mode.ccm.U(a,k.data,c,d,e,b);if(!f.equal(k.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match");
return k.data},ka:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,k=h.B;d=[h.partial(8,(b.length?64:0)|d-2<<2|f-1)];d=h.concat(d,c);d[3]|=e;d=a.encrypt(d);if(b.length)for(c=h.bitLength(b)/8,65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c])),g=h.concat(g,b),b=0;b<g.length;b+=4)d=a.encrypt(k(d,g.slice(b,b+4).concat([0,0,0])));return d},U:function(a,b,c,d,e,f){var g=sjcl.bitArray,h=g.B;e/=8;if(e%2||4>e||16<e)throw new sjcl.exception.invalid("ccm: invalid tag length");
if(0xffffffff<d.length||0xffffffff<b.length)throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");c=sjcl.mode.ccm.ka(a,d,c,e,g.bitLength(b)/8,f);for(d=0;d<b.length;d+=4)c=a.encrypt(h(c,b.slice(d,d+4).concat([0,0,0])));return g.clamp(c,8*e)},V:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.B;var k=b.length,n=h.bitLength(b),l=k/50,m=l;c=h.concat([h.partial(8,f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!k)return{tag:d,data:[]};for(g=0;g<k;g+=4)g>l&&(sjcl.mode.ccm.da(g/
k),l+=m),c[3]++,e=a.encrypt(c),b[g]^=e[0],b[g+1]^=e[1],b[g+2]^=e[2],b[g+3]^=e[3];return{tag:d,data:h.clamp(b,n)}}};void 0===sjcl.beware&&(sjcl.beware={});
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]=function(){sjcl.mode.cbc={name:"cbc",encrypt:function(a,b,c,d){if(d&&d.length)throw new sjcl.exception.invalid("cbc can't authenticate data");if(128!==sjcl.bitArray.bitLength(c))throw new sjcl.exception.invalid("cbc iv must be 128 bits");var e=sjcl.bitArray,f=e.B,g=e.bitLength(b),h=0,k=[];if(g&7)throw new sjcl.exception.invalid("pkcs#5 padding only works for multiples of a byte");for(d=0;h+128<=g;d+=4,h+=128)c=a.encrypt(f(c,
b.slice(d,d+4))),k.splice(d,0,c[0],c[1],c[2],c[3]);g=0x1010101*(16-(g>>3&15));c=a.encrypt(f(c,e.concat(b,[g,g,g,g]).slice(d,d+4)));k.splice(d,0,c[0],c[1],c[2],c[3]);return k},decrypt:function(a,b,c,d){if(d&&d.length)throw new sjcl.exception.invalid("cbc can't authenticate data");if(128!==sjcl.bitArray.bitLength(c))throw new sjcl.exception.invalid("cbc iv must be 128 bits");if(sjcl.bitArray.bitLength(b)&127||!b.length)throw new sjcl.exception.corrupt("cbc ciphertext must be a positive multiple of the block size");
var e=sjcl.bitArray,f=e.B,g,h=[];for(d=0;d<b.length;d+=4)g=b.slice(d,d+4),c=f(c,a.decrypt(g)),h.splice(d,0,c[0],c[1],c[2],c[3]),c=g;g=h[d-1]&255;if(0===g||16<g)throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");c=0x1010101*g;if(!e.equal(e.bitSlice([c,c,c,c],0,8*g),e.bitSlice(h,32*h.length-8*g,32*h.length)))throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");return e.bitSlice(h,0,32*h.length-8*g)}}};
sjcl.misc.hmac=function(a,b){this.W=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.A=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.A[0].update(c[0]);this.A[1].update(c[1]);this.P=new b(this.A[0])};sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){if(this.Z)throw new sjcl.exception.invalid("encrypt on already updated hmac called!");this.update(a);return this.digest(a)};
sjcl.misc.hmac.prototype.reset=function(){this.P=new this.W(this.A[0]);this.Z=!1};sjcl.misc.hmac.prototype.update=function(a){this.Z=!0;this.P.update(a)};sjcl.misc.hmac.prototype.digest=function(){var a=this.P.finalize(),a=(new this.W(this.A[1])).update(a).finalize();this.reset();return a};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E4;if(0>d||0>c)throw new sjcl.exception.invalid("invalid params to pbkdf2");"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,k,n=[],l=sjcl.bitArray;for(k=1;32*n.length<(d||1);k++){e=f=a.encrypt(l.concat(b,[k]));for(g=1;g<c;g++)for(f=a.encrypt(f),h=0;h<f.length;h++)e[h]^=f[h];n=n.concat(e)}d&&(n=l.clamp(n,d));return n};
sjcl.prng=function(a){this.h=[new sjcl.hash.sha256];this.o=[0];this.O=0;this.G={};this.M=0;this.T={};this.X=this.i=this.s=this.fa=0;this.b=[0,0,0,0,0,0,0,0];this.m=[0,0,0,0];this.K=void 0;this.L=a;this.C=!1;this.J={progress:{},seeded:{}};this.w=this.ea=0;this.H=1;this.I=2;this.aa=0x10000;this.R=[0,48,64,96,128,192,0x100,384,512,768,1024];this.ba=3E4;this.$=80};
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;if(d===this.w)throw new sjcl.exception.notReady("generator isn't seeded");if(d&this.I){d=!(d&this.H);e=[];var f=0,g;this.X=e[0]=(new Date).valueOf()+this.ba;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.h.length&&(e=e.concat(this.h[g].finalize()),f+=this.o[g],this.o[g]=0,d||!(this.O&1<<g));g++);this.O>=1<<this.h.length&&(this.h.push(new sjcl.hash.sha256),this.o.push(0));this.i-=f;f>this.s&&(this.s=
f);this.O++;this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.K=new sjcl.cipher.aes(this.b);for(d=0;4>d&&(this.m[d]=this.m[d]+1|0,!this.m[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.aa&&t(this),e=y(this),c.push(e[0],e[1],e[2],e[3]);t(this);return c.slice(0,a)},setDefaultParanoia:function(a,b){if(0===a&&"Setting paranoia=0 will ruin your security; use it only for testing"!==b)throw new sjcl.exception.invalid("Setting paranoia=0 will ruin your security; use it only for testing");this.L=a},addEntropy:function(a,
b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.G[c],h=this.isReady(),k=0;d=this.T[c];void 0===d&&(d=this.T[c]=this.fa++);void 0===g&&(g=this.G[c]=0);this.G[c]=(this.G[c]+1)%this.h.length;switch(typeof a){case "number":void 0===b&&(b=1);this.h[g].update([d,this.M++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else for("[object Array]"!==c&&(k=1),c=0;c<a.length&&!k;c++)"number"!==typeof a[c]&&
(k=1);if(!k){if(void 0===b)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,e=e>>>1;this.h[g].update([d,this.M++,2,b,f,a.length].concat(a))}break;case "string":void 0===b&&(b=a.length);this.h[g].update([d,this.M++,3,b,f,a.length]);this.h[g].update(a);break;default:k=1}if(k)throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");this.o[g]+=b;this.i+=b;h===this.w&&(this.isReady()!==this.w&&z("seeded",Math.max(this.s,this.i)),z("progress",this.getProgress()))},
isReady:function(a){a=this.R[void 0!==a?a:this.L];return this.s&&this.s>=a?this.o[0]>this.$&&(new Date).valueOf()>this.X?this.I|this.H:this.H:this.i>=a?this.I|this.w:this.w},getProgress:function(a){a=this.R[a?a:this.L];return this.s>=a?1:this.i>a?1:this.i/a},startCollectors:function(){if(!this.C){this.a={loadTimeCollector:A(this,this.ja),mouseCollector:A(this,this.la),keyboardCollector:A(this,this.ia),accelerometerCollector:A(this,this.ca),touchCollector:A(this,this.na)};if(window.addEventListener)window.addEventListener("load",
this.a.loadTimeCollector,!1),window.addEventListener("mousemove",this.a.mouseCollector,!1),window.addEventListener("keypress",this.a.keyboardCollector,!1),window.addEventListener("devicemotion",this.a.accelerometerCollector,!1),window.addEventListener("touchmove",this.a.touchCollector,!1);else if(document.attachEvent)document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector);else throw new sjcl.exception.bug("can't attach event");
this.C=!0}},stopCollectors:function(){this.C&&(window.removeEventListener?(window.removeEventListener("load",this.a.loadTimeCollector,!1),window.removeEventListener("mousemove",this.a.mouseCollector,!1),window.removeEventListener("keypress",this.a.keyboardCollector,!1),window.removeEventListener("devicemotion",this.a.accelerometerCollector,!1),window.removeEventListener("touchmove",this.a.touchCollector,!1)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove",
this.a.mouseCollector),document.detachEvent("keypress",this.a.keyboardCollector)),this.C=!1)},addEventListener:function(a,b){this.J[a][this.ea++]=b},removeEventListener:function(a,b){var c,d,e=this.J[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;c<f.length;c++)d=f[c],delete e[d]},ia:function(){B(this,1)},la:function(a){var b,c;try{b=a.x||a.clientX||a.offsetX||0,c=a.y||a.clientY||a.offsetY||0}catch(d){c=b=0}0!=b&&0!=c&&this.addEntropy([b,c],2,"mouse");B(this,0)},na:function(a){a=
a.touches[0]||a.changedTouches[0];this.addEntropy([a.pageX||a.clientX,a.pageY||a.clientY],1,"touch");B(this,0)},ja:function(){B(this,2)},ca:function(a){a=a.accelerationIncludingGravity.x||a.accelerationIncludingGravity.y||a.accelerationIncludingGravity.z;if(window.orientation){var b=window.orientation;"number"===typeof b&&this.addEntropy(b,1,"accelerometer")}a&&this.addEntropy(a,2,"accelerometer");B(this,0)}};
function z(a,b){var c,d=sjcl.random.J[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}function B(a,b){"undefined"!==typeof window&&window.performance&&"function"===typeof window.performance.now?a.addEntropy(window.performance.now(),b,"loadtime"):a.addEntropy((new Date).valueOf(),b,"loadtime")}function t(a){a.b=y(a).concat(y(a));a.K=new sjcl.cipher.aes(a.b)}function y(a){for(var b=0;4>b&&(a.m[b]=a.m[b]+1|0,!a.m[b]);b++);return a.K.encrypt(a.m)}
function A(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6);
a:try{var C,D,E,F;if(F="undefined"!==typeof module&&module.exports){var G;try{G=require("crypto")}catch(a){G=null}F=D=G}if(F&&D.randomBytes)C=D.randomBytes(128),C=new Uint32Array((new Uint8Array(C)).buffer),sjcl.random.addEntropy(C,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){E=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(E);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(E);
else break a;sjcl.random.addEntropy(E,1024,"crypto['getRandomValues']")}}catch(a){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(a))}
sjcl.json={defaults:{v:1,iter:1E4,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},ha:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.j({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.j(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));if(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||
4<f.iv.length)throw new sjcl.exception.invalid("json encrypt: invalid parameters");"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(f.adata=c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.j(d,f);d.key=a;f.ct="ccm"===f.mode&&sjcl.arrayBuffer&&sjcl.arrayBuffer.ccm&&
b instanceof ArrayBuffer?sjcl.arrayBuffer.ccm.encrypt(g,b,f.iv,c,f.ts):sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return f},encrypt:function(a,b,c,d){var e=sjcl.json,f=e.ha.apply(e,arguments);return e.encode(f)},ga:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.j(e.j(e.j({},e.defaults),b),c,!0);var f,g;f=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===
typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)throw new sjcl.exception.invalid("json decrypt: invalid parameters");"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,b),a=g.key.slice(0,b.ks/32),b.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof f&&(f=sjcl.codec.utf8String.toBits(f));g=new sjcl.cipher[b.cipher](a);f="ccm"===
b.mode&&sjcl.arrayBuffer&&sjcl.arrayBuffer.ccm&&b.ct instanceof ArrayBuffer?sjcl.arrayBuffer.ccm.decrypt(g,b.ct,b.iv,b.tag,f,b.ts):sjcl.mode[b.mode].decrypt(g,b.ct,b.iv,f,b.ts);e.j(d,b);d.key=a;return 1===c.raw?f:sjcl.codec.utf8String.fromBits(f)},decrypt:function(a,b,c,d){var e=sjcl.json;return e.ga(a,e.decode(b),c,d)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+'"'+
b+'":';d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:throw new sjcl.exception.bug("json encode: unsupported type");}}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");if(!a.match(/^\{.*\}$/))throw new sjcl.exception.invalid("json decode: this isn't json!");a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++){if(!(d=a[c].match(/^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i)))throw new sjcl.exception.invalid("json decode: this isn't json!");
null!=d[3]?b[d[2]]=parseInt(d[3],10):null!=d[4]?b[d[2]]=d[2].match(/^(ct|adata|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]):null!=d[5]&&(b[d[2]]="true"===d[5])}return b},j:function(a,b,c){void 0===a&&(a={});if(void 0===b)return a;for(var d in b)if(b.hasOwnProperty(d)){if(c&&void 0!==a[d]&&a[d]!==b[d])throw new sjcl.exception.invalid("required parameter overridden");a[d]=b[d]}return a},pa:function(a,b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},oa:function(a,
b){var c={},d;for(d=0;d<b.length;d++)void 0!==a[b[d]]&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.ma={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.ma,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=void 0===b.salt?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);"function"===typeof define&&define([],function(){return sjcl});

28
api/utils/setup.ts

@ -6,7 +6,7 @@ import * as publicIp from 'public-ip'
import password from '../utils/password'
import {checkTag, checkCommitHash} from '../utils/gitinfo'
const USER_VERSION = 1
const USER_VERSION = 2
const setupDatabase = async () => {
console.log('=> [db] starting setup...')
@ -31,8 +31,32 @@ async function setVersion(){
}
async function migrate(){
addTableColumn('sphinx_chats', 'group_key')
addTableColumn('sphinx_chats', 'group_private_key')
addTableColumn('sphinx_chats', 'host')
addTableColumn('sphinx_chats', 'price_to_join', 'BIGINT')
addTableColumn('sphinx_chats', 'price_per_message', 'BIGINT')
addTableColumn('sphinx_chats', 'owner_pubkey')
addTableColumn('sphinx_messages', 'sender_alias')
addTableColumn('sphinx_chat_members', 'alias')
addTableColumn('sphinx_contacts', 'from_group')
try{
await sequelize.query(`
CREATE TABLE sphinx_chat_members (
chat_id INT,
contact_id INT,
alias TEXT,
role INT,
total_spent INT,
total_messages INT,
last_active DATETIME
)`)
} catch(e){}
}
async function addTableColumn(table:string, column:string, type='TEXT') {
try {
await sequelize.query(`alter table sphinx_invites add invoice text`)
await sequelize.query(`alter table ${table} add ${column} ${type}`)
} catch(e) {
//console.log('=> migrate failed',e)
}

118
api/utils/signer.ts

@ -0,0 +1,118 @@
import * as grpc from 'grpc'
import {loadCredentials} from './lightning'
import * as path from 'path'
import * as ByteBuffer from 'bytebuffer'
// var protoLoader = require('@grpc/proto-loader')
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname,'../../config/app.json'))[env]
var signerClient = <any> null;
export const loadSigner = () => {
console.log("LOAD SIGNER RRRRRR",signerClient?true:false)
if (signerClient) {
return signerClient
} else {
console.log("LOAD SIGNER AGAIN!!!!")
try{
var credentials = loadCredentials()
var lnrpcDescriptor = grpc.load("signer.proto");
var signer: any = lnrpcDescriptor.signrpc
signerClient = new signer.Signer(config.node_ip + ':' + config.lnd_port, credentials);
console.log("SIGNER CLIENT",signerClient)
return signerClient
} catch(e) {
throw e
}
}
}
export const signMessage = (msg) => {
return new Promise(async(resolve, reject)=> {
let signer = await loadSigner()
try {
const options = {
msg:ByteBuffer.fromHex(msg),
key_loc:{key_family:6, key_index:0},
}
signer.signMessage(options, function(err,sig){
if(err || !sig.signature) {
reject(err)
} else {
resolve(sig.signature)
}
})
} catch(e) {
reject(e)
}
})
}
export const signBuffer = (msg) => {
return new Promise(async (resolve, reject)=> {
let signer = await loadSigner()
try {
const options = {msg}
signer.signMessage(options, function(err,sig){
if(err || !sig.signature) {
reject(err)
} else {
resolve(sig.signature)
}
})
} catch(e) {
reject(e)
}
})
}
function verifyMessage(msg,sig,pubkey): Promise<{[k:string]:any}> {
return new Promise(async(resolve, reject)=> {
let signer = await loadSigner()
try {
const options = {
msg:ByteBuffer.fromHex(msg),
signature:sig,
pubkey:ByteBuffer.fromHex(pubkey),
}
signer.verifyMessage(options, function(err,res){
if(err) {
reject(err)
} else {
resolve(res)
}
})
} catch(e) {
reject(e)
}
})
}
export async function signAscii(ascii) {
try {
const sig = await signMessage(ascii_to_hexa(ascii))
return sig
} catch(e) {
throw e
}
}
export async function verifyAscii(ascii:string,sig:Buffer,pubkey:string): Promise<{[k:string]:any}>{
try {
const r = await verifyMessage(ascii_to_hexa(ascii),sig,pubkey)
return r
} catch(e) {
throw e
}
}
function ascii_to_hexa(str){
var arr1 = <string[]> [];
for (var n = 0, l = str.length; n < l; n ++) {
var hex = Number(str.charCodeAt(n)).toString(16);
arr1.push(hex);
}
return arr1.join('');
}

101
api/utils/tribes.ts

@ -0,0 +1,101 @@
import * as moment from 'moment'
import * as zbase32 from './zbase32'
import * as LND from './lightning'
import * as path from 'path'
import * as mqtt from 'mqtt'
import * as fetch from 'node-fetch'
const env = process.env.NODE_ENV || 'development'
const config = require(path.join(__dirname,'../../config/app.json'))[env]
let client:any
export async function connect(onMessage) {
try{
const info = await LND.getInfo()
async function reconnect(){
client = null
const pwd = await genSignedTimestamp()
console.log('[tribes] try to connect:',`tls://${config.tribes_host}:8883`)
client = mqtt.connect(`tls://${config.tribes_host}:8883`,{
username:info.identity_pubkey,
password:pwd,
reconnectPeriod:0, // dont auto reconnect
})
client.on('connect', function () {
console.log("[tribes] connected!")
client.subscribe(`${info.identity_pubkey}/#`)
})
client.on('close', function (e) {
setTimeout(()=> reconnect(), 2000)
})
client.on('error', function (e) {
console.log('[tribes] error: ',e.message||e)
})
client.on('message', function(topic, message) {
if(onMessage) onMessage(topic, message)
})
}
reconnect()
} catch(e){
console.log("TRIBES ERROR",e)
}
}
export function subscribe(topic){
if(client) client.subscribe(topic)
}
export function publish(topic,msg){
if(client) client.publish(topic,msg)
}
export async function declare({uuid,name,description,tags,img,groupKey,host,pricePerMessage,priceToJoin,ownerAlias,ownerPubkey}) {
const r = await fetch('https://' + host + '/tribes', {
method: 'POST' ,
body: JSON.stringify({
uuid, groupKey,
name, description, tags, img:img||'',
pricePerMessage:pricePerMessage||0,
priceToJoin:priceToJoin||0,
ownerAlias, ownerPubkey,
}),
headers: { 'Content-Type': 'application/json' }
})
const j = await r.json()
console.log(j)
}
export async function genSignedTimestamp(){
const now = moment().unix()
const tsBytes = Buffer.from(now.toString(16), 'hex')
const sig = await LND.signBuffer(tsBytes)
const sigBytes = zbase32.decode(sig)
const totalLength = tsBytes.length + sigBytes.length
const buf = Buffer.concat([tsBytes, sigBytes], totalLength)
return urlBase64(buf)
}
export async function verifySignedTimestamp(stsBase64){
const stsBuf = Buffer.from(stsBase64, 'base64')
const sig = stsBuf.subarray(4,92)
const sigZbase32 = zbase32.encode(sig)
const r = await LND.verifyBytes(stsBuf.subarray(0,4), sigZbase32) // sig needs to be zbase32 :(
if (r.valid) {
return r.pubkey
} else {
return false
}
}
export function getHost() {
return config.tribes_host || ''
}
function urlBase64(buf){
return buf.toString('base64').replace(/\//g, '_').replace(/\+/g, '-')
}

9
app.ts

@ -10,6 +10,7 @@ import {pingHubInterval, checkInvitesHubInterval} from './api/hub'
import {setupDatabase, setupDone} from './api/utils/setup'
import * as controllers from './api/controllers'
import * as socket from './api/utils/socket'
import * as network from './api/network'
let server: any = null
const port = process.env.PORT || 3001;
@ -27,9 +28,13 @@ async function connectToLND(){
i++
console.log(`=> [lnd] connecting... attempt #${i}`)
try {
await controllers.iniGrpcSubscriptions()
mainSetup()
await network.initGrpcSubscriptions() // LND
await mainSetup() // DB + express
await network.initTribesSubscriptions() // MQTT
} catch(e) {
if(e.details) {
console.log(`=> [lnd] error details: ${e.details}`)
}
setTimeout(async()=>{ // retry each 2 secs
await connectToLND()
},2000)

6
config/app.json

@ -11,7 +11,8 @@
"hub_url": "http://lvh.me/ping",
"hub_invite_url": "http://lvh.me/invites",
"hub_check_invite_url": "http://lvh.me/check_invite",
"media_host": "localhost:5000"
"media_host": "localhost:5000",
"tribes_host": "tribes.sphinx.chat"
},
"production": {
"senza_url": "https://staging.senza.us/api/v2/",
@ -26,6 +27,7 @@
"hub_url": "https://hub.sphinx.chat/ping",
"hub_invite_url": "https://hub.sphinx.chat/invites",
"hub_check_invite_url": "https://hub.sphinx.chat/check_invite",
"media_host": "memes.sphinx.chat"
"media_host": "memes.sphinx.chat",
"tribes_host": "tribes.sphinx.chat"
}
}

11
config/constants.json

@ -47,6 +47,15 @@
},
"chat_types": {
"conversation": 0,
"group": 1
"group": 1,
"tribe": 2
},
"chat_roles": {
"-": 0,
"owner": 1,
"admin": 2,
"mod": 3,
"writer": 4,
"reader": 5
}
}

336
dist/api/controllers/chats.js

@ -13,10 +13,13 @@ const models_1 = require("../models");
const jsonUtils = require("../utils/json");
const res_1 = require("../utils/res");
const helpers = require("../helpers");
const network = require("../network");
const socket = require("../utils/socket");
const hub_1 = require("../hub");
const md5 = require("md5");
const path = require("path");
const rsa = require("../crypto/rsa");
const tribes = require("../utils/tribes");
const constants = require(path.join(__dirname, '../../config/constants.json'));
function getChats(req, res) {
return __awaiter(this, void 0, void 0, function* () {
@ -42,9 +45,12 @@ function mute(req, res) {
});
}
exports.mute = mute;
// just add self here if tribes
// or can u add contacts as members?
function createGroupChat(req, res) {
return __awaiter(this, void 0, void 0, function* () {
const { name, contact_ids, } = req.body;
const { name, is_tribe, is_listed, price_per_message, price_to_join, img, description, tags, } = req.body;
const contact_ids = req.body.contact_ids || [];
const members = {}; //{pubkey:{key,alias}, ...}
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
members[owner.publicKey] = {
@ -57,8 +63,20 @@ function createGroupChat(req, res) {
alias: contact.alias || ''
};
}));
const chatParams = createGroupChatParams(owner, contact_ids, members, name);
helpers.sendMessage({
let chatParams = null;
if (is_tribe) {
chatParams = yield createTribeChatParams(owner, contact_ids, name, img, price_per_message, price_to_join);
if (is_listed && chatParams.uuid) {
// publish to tribe server
tribes.declare(Object.assign(Object.assign({}, chatParams), { pricePerMessage: price_per_message || 0, priceToJoin: price_to_join || 0, description, tags, img, ownerPubkey: owner.publicKey, ownerAlias: owner.alias }));
}
// make me owner when i create
members[owner.publicKey].role = constants.chat_roles.owner;
}
else {
chatParams = createGroupChatParams(owner, contact_ids, members, name);
}
network.sendMessage({
chat: Object.assign(Object.assign({}, chatParams), { members }),
sender: owner,
type: constants.message_types.group_create,
@ -69,6 +87,13 @@ function createGroupChat(req, res) {
success: function () {
return __awaiter(this, void 0, void 0, function* () {
const chat = yield models_1.models.Chat.create(chatParams);
if (chat.type === constants.chat_types.tribe) { // save me as owner when i create
yield models_1.models.ChatMember.create({
contactId: owner.id,
chatId: chat.id,
role: constants.chat_roles.owner,
});
}
res_1.success(res, jsonUtils.chatToJson(chat));
});
}
@ -76,6 +101,7 @@ function createGroupChat(req, res) {
});
}
exports.createGroupChat = createGroupChat;
// only owner can do for tribe?
function addGroupMembers(req, res) {
return __awaiter(this, void 0, void 0, function* () {
const { contact_ids, } = req.body;
@ -86,6 +112,11 @@ function addGroupMembers(req, res) {
const contactIds = JSON.parse(chat.contactIds || '[]');
// for all members (existing and new)
members[owner.publicKey] = { key: owner.contactKey, alias: owner.alias };
if (chat.type === constants.chat_types.tribe) {
const me = yield models_1.models.ChatMember.findOne({ where: { contactId: owner.id, chatId: chat.id } });
if (me)
members[owner.publicKey].role = me.role;
}
const allContactIds = contactIds.concat(contact_ids);
yield asyncForEach(allContactIds, (cid) => __awaiter(this, void 0, void 0, function* () {
const contact = yield models_1.models.Contact.findOne({ where: { id: cid } });
@ -94,10 +125,13 @@ function addGroupMembers(req, res) {
key: contact.contactKey,
alias: contact.alias
};
const member = yield models_1.models.ChatMember.findOne({ where: { contactId: owner.id, chatId: chat.id } });
if (member)
members[contact.publicKey].role = member.role;
}
}));
res_1.success(res, jsonUtils.chatToJson(chat));
helpers.sendMessage({
network.sendMessage({
chat: Object.assign(Object.assign({}, chat.dataValues), { contactIds: contact_ids, members }),
sender: owner,
type: constants.message_types.group_invite,
@ -110,7 +144,14 @@ const deleteChat = (req, res) => __awaiter(void 0, void 0, void 0, function* ()
const { id } = req.params;
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
const chat = yield models_1.models.Chat.findOne({ where: { id } });
helpers.sendMessage({
if (!chat) {
return res_1.failure(res, "you are not in this group");
}
const tribeOwnerPubKey = chat.ownerPubkey;
if (owner.publicKey === tribeOwnerPubKey) {
return res_1.failure(res, "cannot leave your own tribe");
}
network.sendMessage({
chat,
sender: owner,
message: {},
@ -119,6 +160,9 @@ const deleteChat = (req, res) => __awaiter(void 0, void 0, void 0, function* ()
yield chat.update({
deleted: true,
uuid: '',
groupKey: '',
host: '',
photoUrl: '',
contactIds: '[]',
name: ''
});
@ -126,32 +170,126 @@ const deleteChat = (req, res) => __awaiter(void 0, void 0, void 0, function* ()
res_1.success(res, { chat_id: id });
});
exports.deleteChat = deleteChat;
function joinTribe(req, res) {
return __awaiter(this, void 0, void 0, function* () {
console.log('=> joinTribe');
const { uuid, group_key, name, host, amount, img, owner_pubkey, owner_alias } = req.body;
const existing = yield models_1.models.Chat.findOne({ where: { uuid } });
if (existing) {
console.log('[tribes] u are already in this tribe');
return;
}
if (!owner_pubkey || !group_key || !uuid) {
console.log('[tribes] missing required params');
return;
}
const ownerPubKey = owner_pubkey;
// verify signature here?
const tribeOwner = yield models_1.models.Contact.findOne({ where: { publicKey: ownerPubKey } });
let theTribeOwner;
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
const contactIds = [owner.id];
if (tribeOwner) {
theTribeOwner = tribeOwner; // might already include??
if (!contactIds.includes(tribeOwner.id))
contactIds.push(tribeOwner.id);
}
else {
const createdContact = yield models_1.models.Contact.create({
publicKey: ownerPubKey,
contactKey: '',
alias: owner_alias || 'Unknown',
status: 1,
fromGroup: true,
});
theTribeOwner = createdContact;
contactIds.push(createdContact.id);
}
let date = new Date();
date.setMilliseconds(0);
const chatParams = {
uuid: uuid,
contactIds: JSON.stringify(contactIds),
photoUrl: img || '',
createdAt: date,
updatedAt: date,
name: name,
type: constants.chat_types.tribe,
host: host || tribes.getHost(),
groupKey: group_key,
ownerPubkey: owner_pubkey,
};
network.sendMessage({
chat: Object.assign(Object.assign({}, chatParams), { members: {
[owner.publicKey]: {
key: owner.contactKey,
alias: owner.alias || ''
}
} }),
amount: amount || 0,
sender: owner,
message: {},
type: constants.message_types.group_join,
failure: function (e) {
res_1.failure(res, e);
},
success: function () {
return __awaiter(this, void 0, void 0, function* () {
const chat = yield models_1.models.Chat.create(chatParams);
models_1.models.ChatMember.create({
contactId: theTribeOwner.id,
chatId: chat.id,
role: constants.chat_roles.owner,
lastActive: date,
});
res_1.success(res, jsonUtils.chatToJson(chat));
});
}
});
});
}
exports.joinTribe = joinTribe;
function receiveGroupLeave(payload) {
return __awaiter(this, void 0, void 0, function* () {
console.log('=> receiveGroupLeave');
const { sender_pub_key, chat_uuid } = yield helpers.parseReceiveParams(payload);
const { sender_pub_key, chat_uuid, chat_type, sender_alias, isTribeOwner } = yield helpers.parseReceiveParams(payload);
const chat = yield models_1.models.Chat.findOne({ where: { uuid: chat_uuid } });
if (!chat)
return;
const sender = yield models_1.models.Contact.findOne({ where: { publicKey: sender_pub_key } });
if (!sender)
return;
const oldContactIds = JSON.parse(chat.contactIds || '[]');
const contactIds = oldContactIds.filter(cid => cid !== sender.id);
yield chat.update({ contactIds: JSON.stringify(contactIds) });
const isTribe = chat_type === constants.chat_types.tribe;
let sender;
if (!isTribe || isTribeOwner) {
sender = yield models_1.models.Contact.findOne({ where: { publicKey: sender_pub_key } });
if (!sender)
return;
const oldContactIds = JSON.parse(chat.contactIds || '[]');
const contactIds = oldContactIds.filter(cid => cid !== sender.id);
yield chat.update({ contactIds: JSON.stringify(contactIds) });
if (isTribeOwner) {
if (chat_type === constants.chat_types.tribe) {
try {
yield models_1.models.ChatMember.destroy({ where: { chatId: chat.id, contactId: sender.id } });
}
catch (e) { }
}
}
}
var date = new Date();
date.setMilliseconds(0);
const msg = {
chatId: chat.id,
type: constants.message_types.group_leave,
sender: sender.id,
sender: (sender && sender.id) || 0,
date: date,
messageContent: '',
messageContent: `${sender_alias} has left the group`,
remoteMessageContent: '',
status: constants.statuses.confirmed,
createdAt: date,
updatedAt: date
};
if (isTribe) {
msg.senderAlias = sender_alias;
}
const message = yield models_1.models.Message.create(msg);
socket.sendJson({
type: 'group_leave',
@ -167,50 +305,68 @@ exports.receiveGroupLeave = receiveGroupLeave;
function receiveGroupJoin(payload) {
return __awaiter(this, void 0, void 0, function* () {
console.log('=> receiveGroupJoin');
const { sender_pub_key, chat_uuid, chat_members } = yield helpers.parseReceiveParams(payload);
const { sender_pub_key, sender_alias, chat_uuid, chat_members, chat_type, isTribeOwner } = yield helpers.parseReceiveParams(payload);
const chat = yield models_1.models.Chat.findOne({ where: { uuid: chat_uuid } });
if (!chat)
return;
const isTribe = chat_type === constants.chat_types.tribe;
var date = new Date();
date.setMilliseconds(0);
let theSender = null;
const sender = yield models_1.models.Contact.findOne({ where: { publicKey: sender_pub_key } });
const contactIds = JSON.parse(chat.contactIds || '[]');
if (sender) {
theSender = sender; // might already include??
if (!contactIds.includes(sender.id))
contactIds.push(sender.id);
}
else {
const member = chat_members[sender_pub_key];
if (member && member.key) {
const createdContact = yield models_1.models.Contact.create({
publicKey: sender_pub_key,
contactKey: member.key,
alias: member.alias || 'Unknown',
status: 1
const member = chat_members[sender_pub_key];
const senderAlias = sender_alias || (member && member.alias) || 'Unknown';
if (!isTribe || isTribeOwner) { // dont need to create contacts for these
const sender = yield models_1.models.Contact.findOne({ where: { publicKey: sender_pub_key } });
const contactIds = JSON.parse(chat.contactIds || '[]');
if (sender) {
theSender = sender; // might already include??
if (!contactIds.includes(sender.id))
contactIds.push(sender.id);
}
else {
if (member && member.key) {
const createdContact = yield models_1.models.Contact.create({
publicKey: sender_pub_key,
contactKey: member.key,
alias: senderAlias,
status: 1,
fromGroup: true,
});
theSender = createdContact;
contactIds.push(createdContact.id);
}
}
if (!theSender)
return; // fail (no contact key?)
yield chat.update({ contactIds: JSON.stringify(contactIds) });
if (isTribeOwner) { // IF TRIBE, ADD TO XREF
models_1.models.ChatMember.create({
contactId: theSender.id,
chatId: chat.id,
role: constants.chat_roles.reader,
lastActive: date,
});
theSender = createdContact;
contactIds.push(createdContact.id);
}
}
yield chat.update({ contactIds: JSON.stringify(contactIds) });
var date = new Date();
date.setMilliseconds(0);
const msg = {
chatId: chat.id,
type: constants.message_types.group_join,
sender: theSender.id,
sender: (theSender && theSender.id) || 0,
date: date,
messageContent: '',
messageContent: `${senderAlias} has joined the group`,
remoteMessageContent: '',
status: constants.statuses.confirmed,
createdAt: date,
updatedAt: date
};
if (isTribe) {
msg.senderAlias = sender_alias;
}
const message = yield models_1.models.Message.create(msg);
socket.sendJson({
type: 'group_join',
response: {
contact: jsonUtils.contactToJson(theSender),
contact: jsonUtils.contactToJson(theSender || {}),
chat: jsonUtils.chatToJson(chat),
message: jsonUtils.messageToJson(message, null)
}
@ -218,41 +374,73 @@ function receiveGroupJoin(payload) {
});
}
exports.receiveGroupJoin = receiveGroupJoin;
function validateTribeOwner(chat_uuid, pubkey) {
return __awaiter(this, void 0, void 0, function* () {
const verifiedOwnerPubkey = yield tribes.verifySignedTimestamp(chat_uuid);
if (verifiedOwnerPubkey === pubkey) {
return true;
}
return false;
});
}
function receiveGroupCreateOrInvite(payload) {
return __awaiter(this, void 0, void 0, function* () {
const { chat_members, chat_name, chat_uuid } = yield helpers.parseReceiveParams(payload);
const contactIds = [];
const { sender_pub_key, chat_members, chat_name, chat_uuid, chat_type, chat_host, chat_key } = yield helpers.parseReceiveParams(payload);
// maybe this just needs to move to adding tribe owner ChatMember?
const isTribe = chat_type === constants.chat_types.tribe;
if (isTribe) { // must be sent by tribe owner?????
const validOwner = yield validateTribeOwner(chat_uuid, sender_pub_key);
if (!validOwner)
return console.log('[tribes] invalid uuid signature!');
}
const contacts = [];
const newContacts = [];
for (let [pubkey, member] of Object.entries(chat_members)) {
const contact = yield models_1.models.Contact.findOne({ where: { publicKey: pubkey } });
if (!contact && member && member.key) {
const createdContact = yield models_1.models.Contact.create({
publicKey: pubkey,
contactKey: member.key,
alias: member.alias || 'Unknown',
status: 1
});
contactIds.push(createdContact.id);
newContacts.push(createdContact.dataValues);
let addContact = false;
if (chat_type === constants.chat_types.group && member && member.key) {
addContact = true;
}
else {
contactIds.push(contact.id);
else if (isTribe && member && member.role) {
if (member.role === constants.chat_roles.owner || member.role === constants.chat_roles.admin || member.role === constants.chat_roles.mod) {
addContact = true;
}
}
if (addContact) {
if (!contact) {
const createdContact = yield models_1.models.Contact.create({
publicKey: pubkey,
contactKey: member.key,
alias: member.alias || 'Unknown',
status: 1,
fromGroup: true,
});
contacts.push(Object.assign(Object.assign({}, createdContact.dataValues), { role: member.role }));
newContacts.push(createdContact.dataValues);
}
else {
contacts.push(Object.assign(Object.assign({}, contact.dataValues), { role: member.role }));
}
}
}
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
const contactIds = contacts.map(c => c.id);
if (!contactIds.includes(owner.id))
contactIds.push(owner.id);
// make chat
let date = new Date();
date.setMilliseconds(0);
const chat = yield models_1.models.Chat.create({
uuid: chat_uuid,
contactIds: JSON.stringify(contactIds),
createdAt: date,
updatedAt: date,
name: chat_name,
type: constants.chat_types.group
});
const chat = yield models_1.models.Chat.create(Object.assign(Object.assign({ uuid: chat_uuid, contactIds: JSON.stringify(contactIds), createdAt: date, updatedAt: date, name: chat_name, type: chat_type || constants.chat_types.group }, chat_host && { host: chat_host }), chat_key && { groupKey: chat_key }));
if (isTribe) { // IF TRIBE, ADD TO XREF
contacts.forEach(c => {
models_1.models.ChatMember.create({
contactId: c.id,
chatId: chat.id,
role: c.role || constants.chat_roles.reader,
lastActive: date,
});
});
}
socket.sendJson({
type: 'group_create',
response: jsonUtils.messageToJson({ newContacts }, chat)
@ -260,7 +448,7 @@ function receiveGroupCreateOrInvite(payload) {
hub_1.sendNotification(chat, chat_name, 'group');
if (payload.type === constants.message_types.group_invite) {
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
helpers.sendMessage({
network.sendMessage({
chat: Object.assign(Object.assign({}, chat.dataValues), { members: {
[owner.publicKey]: {
key: owner.contactKey,
@ -299,6 +487,34 @@ function createGroupChatParams(owner, contactIds, members, name) {
type: constants.chat_types.group
};
}
function createTribeChatParams(owner, contactIds, name, img, price_per_message, price_to_join) {
return __awaiter(this, void 0, void 0, function* () {
let date = new Date();
date.setMilliseconds(0);
if (!(owner && contactIds && Array.isArray(contactIds))) {
return {};
}
// make ts sig here w LNd pubkey - that is UUID
const keys = yield rsa.genKeys();
const groupUUID = yield tribes.genSignedTimestamp();
const theContactIds = contactIds.includes(owner.id) ? contactIds : [owner.id].concat(contactIds);
return {
uuid: groupUUID,
ownerPubkey: owner.publicKey,
contactIds: JSON.stringify(theContactIds),
photoUrl: img || '',
createdAt: date,
updatedAt: date,
name: name,
type: constants.chat_types.tribe,
groupKey: keys.public,
groupPrivateKey: keys.private,
host: tribes.getHost(),
pricePerMessage: price_per_message || 0,
priceToJoin: price_to_join || 0,
};
});
}
function asyncForEach(array, callback) {
return __awaiter(this, void 0, void 0, function* () {
for (let index = 0; index < array.length; index++) {

2
dist/api/controllers/chats.js.map

File diff suppressed because one or more lines are too long

6
dist/api/controllers/confirmations.js

@ -13,11 +13,11 @@ const lock_1 = require("../utils/lock");
const models_1 = require("../models");
const socket = require("../utils/socket");
const jsonUtils = require("../utils/json");
const helpers = require("../helpers");
const network = require("../network");
const path = require("path");
const constants = require(path.join(__dirname, '../../config/constants.json'));
function sendConfirmation({ chat, sender, msg_id }) {
helpers.sendMessage({
network.sendMessage({
chat,
sender,
message: { id: msg_id },
@ -27,7 +27,7 @@ function sendConfirmation({ chat, sender, msg_id }) {
exports.sendConfirmation = sendConfirmation;
function receiveConfirmation(payload) {
return __awaiter(this, void 0, void 0, function* () {
console.log('received confirmation', { payload });
console.log('=> received confirmation');
const dat = payload.content || payload;
const chat_uuid = dat.chat.uuid;
const msg_id = dat.message.id;

2
dist/api/controllers/confirmations.js.map

@ -1 +1 @@
{"version":3,"file":"confirmations.js","sourceRoot":"","sources":["../../../api/controllers/confirmations.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"confirmations.js","sourceRoot":"","sources":["../../../api/controllers/confirmations.ts"],"names":[],"mappings":"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"}

41
dist/api/controllers/contacts.js

@ -17,6 +17,7 @@ const jsonUtils = require("../utils/json");
const res_1 = require("../utils/res");
const password_1 = require("../utils/password");
const path = require("path");
const sequelize_1 = require("sequelize");
const constants = require(path.join(__dirname, '../../config/constants.json'));
const getContacts = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const contacts = yield models_1.models.Contact.findAll({ where: { deleted: false }, raw: true });
@ -100,6 +101,14 @@ const createContact = (req, res) => __awaiter(void 0, void 0, void 0, function*
console.log('=> createContact called', { body: req.body, params: req.params, query: req.query });
let attrs = extractAttrs(req.body);
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
const existing = attrs['public_key'] && (yield models_1.models.Contact.findOne({ where: { publicKey: attrs['public_key'] } }));
if (existing) {
const updateObj = { fromGroup: false };
if (attrs['alias'])
updateObj.alias = attrs['alias'];
yield existing.update(updateObj);
return res_1.success(res, jsonUtils.contactToJson(existing));
}
const createdContact = yield models_1.models.Contact.create(attrs);
const contact = yield createdContact.update(jsonUtils.jsonToContact(attrs));
res_1.success(res, jsonUtils.contactToJson(contact));
@ -117,13 +126,29 @@ const deleteContact = (req, res) => __awaiter(void 0, void 0, void 0, function*
return;
}
const contact = yield models_1.models.Contact.findOne({ where: { id } });
yield contact.update({
deleted: true,
publicKey: '',
photoUrl: '',
alias: 'Unknown',
contactKey: '',
});
if (!contact)
return;
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
const tribesImAdminOf = yield models_1.models.Chat.findAll({ where: { ownerPubkey: owner.publicKey } });
const tribesIdArray = tribesImAdminOf && tribesImAdminOf.length && tribesImAdminOf.map(t => t.id);
let okToDelete = true;
if (tribesIdArray && tribesIdArray.length) {
const thisContactMembers = yield models_1.models.ChatMember.findAll({ where: { contactId: id, chatId: { [sequelize_1.Op.in]: tribesIdArray } } });
if (thisContactMembers && thisContactMembers.length) {
// IS A MEMBER! dont delete, instead just set from_group=true
okToDelete = false;
yield contact.update({ fromGroup: true });
}
}
if (okToDelete) {
yield contact.update({
deleted: true,
publicKey: '',
photoUrl: '',
alias: 'Unknown',
contactKey: '',
});
}
// find and destroy chat & messages
const chats = yield models_1.models.Chat.findAll({ where: { deleted: false } });
chats.map((chat) => __awaiter(void 0, void 0, void 0, function* () {
@ -202,7 +227,7 @@ const receiveContactKey = (payload) => __awaiter(void 0, void 0, void 0, functio
});
exports.receiveContactKey = receiveContactKey;
const extractAttrs = body => {
let fields_to_update = ["public_key", "node_alias", "alias", "photo_url", "device_id", "status", "contact_key"];
let fields_to_update = ["public_key", "node_alias", "alias", "photo_url", "device_id", "status", "contact_key", "from_group"];
let attrs = {};
Object.keys(body).forEach(key => {
if (fields_to_update.includes(key)) {

2
dist/api/controllers/contacts.js.map

File diff suppressed because one or more lines are too long

34
dist/api/controllers/index.js

@ -10,11 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
};
Object.defineProperty(exports, "__esModule", { value: true });
const models_1 = require("../models");
const lndService = require("../grpc");
const gitinfo_1 = require("../utils/gitinfo");
const lightning_1 = require("../utils/lightning");
const path = require("path");
const constants = require(path.join(__dirname, '../../config/constants.json'));
const env = process.env.NODE_ENV || 'development';
console.log("=> env:", env);
let controllers = {
@ -30,34 +26,7 @@ let controllers = {
media: require('./media'),
confirmations: require('./confirmations')
};
function iniGrpcSubscriptions() {
return __awaiter(this, void 0, void 0, function* () {
try {
yield lightning_1.checkConnection();
const types = constants.message_types;
yield lndService.subscribeInvoices({
[types.contact_key]: controllers.contacts.receiveContactKey,
[types.contact_key_confirmation]: controllers.contacts.receiveConfirmContactKey,
[types.message]: controllers.messages.receiveMessage,
[types.invoice]: controllers.invoices.receiveInvoice,
[types.direct_payment]: controllers.payments.receivePayment,
[types.confirmation]: controllers.confirmations.receiveConfirmation,
[types.attachment]: controllers.media.receiveAttachment,
[types.purchase]: controllers.media.receivePurchase,
[types.purchase_accept]: controllers.media.receivePurchaseAccept,
[types.purchase_deny]: controllers.media.receivePurchaseDeny,
[types.group_create]: controllers.chats.receiveGroupCreateOrInvite,
[types.group_invite]: controllers.chats.receiveGroupCreateOrInvite,
[types.group_join]: controllers.chats.receiveGroupJoin,
[types.group_leave]: controllers.chats.receiveGroupLeave,
});
}
catch (e) {
throw e;
}
});
}
exports.iniGrpcSubscriptions = iniGrpcSubscriptions;
exports.controllers = controllers;
function set(app) {
return __awaiter(this, void 0, void 0, function* () {
if (models_1.models && models_1.models.Subscription) {
@ -74,6 +43,7 @@ function set(app) {
app.post('/chats/:chat_id/:mute_unmute', controllers.chats.mute);
app.delete('/chat/:id', controllers.chats.deleteChat);
app.put('/chat/:id', controllers.chats.addGroupMembers);
app.post('/tribe', controllers.chats.joinTribe);
app.post('/contacts/tokens', controllers.contacts.generateToken);
app.post('/upload', controllers.uploads.avatarUpload.single('file'), controllers.uploads.uploadFile);
app.post('/invites', controllers.invites.createInvite);

2
dist/api/controllers/index.js.map

File diff suppressed because one or more lines are too long

16
dist/api/controllers/invoices.js

@ -19,6 +19,7 @@ const hub_1 = require("../hub");
const res_1 = require("../utils/res");
const confirmations_1 = require("./confirmations");
const path = require("path");
const network = require("../network");
const constants = require(path.join(__dirname, '../../config/constants.json'));
const payInvoice = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const lightning = yield lightning_1.loadLightning();
@ -125,7 +126,7 @@ const createInvoice = (req, res) => __awaiter(void 0, void 0, void 0, function*
updatedAt: new Date(timestamp)
});
res_1.success(res, jsonUtils.messageToJson(message, chat));
helpers.sendMessage({
network.sendMessage({
chat: chat,
sender: owner,
type: constants.message_types.invoice,
@ -173,12 +174,12 @@ const receiveInvoice = (payload) => __awaiter(void 0, void 0, void 0, function*
const payment_request = dat.message.invoice;
var date = new Date();
date.setMilliseconds(0);
const { owner, sender, chat, msg_id } = yield helpers.parseReceiveParams(payload);
const { owner, sender, chat, msg_id, chat_type, sender_alias } = yield helpers.parseReceiveParams(payload);
if (!owner || !sender || !chat) {
return console.log('=> no group chat!');
}
const { memo, sat, msat, paymentHash, invoiceDate, expirationSeconds } = decodePaymentRequest(payment_request);
const message = yield models_1.models.Message.create({
const msg = {
chatId: chat.id,
type: constants.message_types.invoice,
sender: sender.id,
@ -193,13 +194,18 @@ const receiveInvoice = (payload) => __awaiter(void 0, void 0, void 0, function*
status: constants.statuses.pending,
createdAt: date,
updatedAt: date
});
};
const isTribe = chat_type === constants.chat_types.tribe;
if (isTribe) {
msg.senderAlias = sender_alias;
}
const message = yield models_1.models.Message.create(msg);
console.log('received keysend invoice message', message.id);
socket.sendJson({
type: 'invoice',
response: jsonUtils.messageToJson(message, chat, sender)
});
hub_1.sendNotification(chat, sender.alias, 'message');
hub_1.sendNotification(chat, msg.senderAlias || sender.alias, 'message');
const theChat = Object.assign(Object.assign({}, chat.dataValues), { contactIds: [sender.id] });
confirmations_1.sendConfirmation({ chat: theChat, sender: owner, msg_id });
});

2
dist/api/controllers/invoices.js.map

File diff suppressed because one or more lines are too long

31
dist/api/controllers/media.js

@ -24,6 +24,8 @@ const zbase32 = require("../utils/zbase32");
const schemas = require("./schemas");
const confirmations_1 = require("./confirmations");
const path = require("path");
const network = require("../network");
const meme = require("../utils/meme");
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname, '../../config/app.json'))[env];
const constants = require(path.join(__dirname, '../../config/constants.json'));
@ -103,7 +105,7 @@ const sendAttachmentMessage = (req, res) => __awaiter(void 0, void 0, void 0, fu
mediaKey: media_key_map,
mediaType: mediaType,
};
helpers.sendMessage({
network.sendMessage({
chat: chat,
sender: owner,
type: constants.message_types.attachment,
@ -162,7 +164,7 @@ const purchase = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const msg = {
amount, mediaToken: media_token, id: message.id,
};
helpers.sendMessage({
network.sendMessage({
chat: Object.assign(Object.assign({}, chat.dataValues), { contactIds: [contact_id] }),
sender: owner,
type: constants.message_types.purchase,
@ -230,7 +232,7 @@ const receivePurchase = (payload) => __awaiter(void 0, void 0, void 0, function*
price = 0;
}
if (amount < price) { // didnt pay enough
return helpers.sendMessage({
return network.sendMessage({
chat: Object.assign(Object.assign({}, chat.dataValues), { contactIds: [sender.id] }),
sender: owner,
amount: amount,
@ -258,7 +260,7 @@ const receivePurchase = (payload) => __awaiter(void 0, void 0, void 0, function*
meta: { amt: amount },
pubkey: sender.publicKey,
});
helpers.sendMessage({
network.sendMessage({
chat: Object.assign(Object.assign({}, chat.dataValues), { contactIds: [sender.id] }),
sender: owner,
type: constants.message_types.purchase_accept,
@ -357,7 +359,7 @@ const receiveAttachment = (payload) => __awaiter(void 0, void 0, void 0, functio
console.log('received attachment', { payload });
var date = new Date();
date.setMilliseconds(0);
const { owner, sender, chat, mediaToken, mediaKey, mediaType, content, msg_id } = yield helpers.parseReceiveParams(payload);
const { owner, sender, chat, mediaToken, mediaKey, mediaType, content, msg_id, chat_type, sender_alias } = yield helpers.parseReceiveParams(payload);
if (!owner || !sender || !chat) {
return console.log('=> no group chat!');
}
@ -377,13 +379,17 @@ const receiveAttachment = (payload) => __awaiter(void 0, void 0, void 0, functio
msg.mediaKey = mediaKey;
if (mediaType)
msg.mediaType = mediaType;
const isTribe = chat_type === constants.chat_types.tribe;
if (isTribe) {
msg.senderAlias = sender_alias;
}
const message = yield models_1.models.Message.create(msg);
console.log('saved attachment', message.dataValues);
// console.log('saved attachment', message.dataValues)
socket.sendJson({
type: 'attachment',
response: jsonUtils.messageToJson(message, chat, sender)
});
hub_1.sendNotification(chat, sender.alias, 'message');
hub_1.sendNotification(chat, msg.senderAlias || sender.alias, 'message');
const theChat = Object.assign(Object.assign({}, chat.dataValues), { contactIds: [sender.id] });
confirmations_1.sendConfirmation({ chat: theChat, sender: owner, msg_id });
});
@ -440,8 +446,10 @@ function cycleMediaToken() {
if (process.env.TEST_LDAT)
ldat_1.testLDAT();
const mt = yield getMediaToken(null);
if (mt)
if (mt) {
console.log('=> [meme] authed!');
meme.setMediaToken(mt);
}
new cron_1.CronJob('1 * * * *', function () {
getMediaToken(true);
});
@ -453,11 +461,10 @@ function cycleMediaToken() {
}
exports.cycleMediaToken = cycleMediaToken;
const mediaURL = 'http://' + config.media_host + '/';
let mediaToken;
function getMediaToken(force) {
return __awaiter(this, void 0, void 0, function* () {
if (!force && mediaToken)
return mediaToken;
if (!force && meme.mediaToken)
return meme.mediaToken;
yield helpers.sleep(3000);
try {
const res = yield rp.get(mediaURL + 'ask');
@ -481,7 +488,7 @@ function getMediaToken(force) {
if (!(body && body.token)) {
throw new Error('no token');
}
mediaToken = body.token;
meme.setMediaToken(body.token);
return body.token;
}
catch (e) {

2
dist/api/controllers/media.js.map

File diff suppressed because one or more lines are too long

23
dist/api/controllers/messages.js

@ -19,6 +19,7 @@ const helpers = require("../helpers");
const res_1 = require("../utils/res");
const confirmations_1 = require("./confirmations");
const path = require("path");
const network = require("../network");
const constants = require(path.join(__dirname, '../../config/constants.json'));
const getMessages = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const dateToReturn = req.query.date;
@ -96,8 +97,8 @@ const sendMessage = (req, res) => __awaiter(void 0, void 0, void 0, function* ()
// } catch(e) {
// return failure(res, e.message)
// }
const { contact_id, text, remote_text, chat_id, remote_text_map, } = req.body;
console.log('[sendMessage]');
const { contact_id, text, remote_text, chat_id, remote_text_map, amount, } = req.body;
console.log('[sendMessage]', remote_text_map);
var date = new Date();
date.setMilliseconds(0);
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
@ -111,19 +112,21 @@ const sendMessage = (req, res) => __awaiter(void 0, void 0, void 0, function* ()
chatId: chat.id,
type: constants.message_types.message,
sender: owner.id,
amount: amount || 0,
date: date,
messageContent: text,
remoteMessageContent,
status: constants.statuses.pending,
createdAt: date,
updatedAt: date
updatedAt: date,
};
// console.log(msg)
const message = yield models_1.models.Message.create(msg);
res_1.success(res, jsonUtils.messageToJson(message, chat));
helpers.sendMessage({
network.sendMessage({
chat: chat,
sender: owner,
amount: amount || 0,
type: constants.message_types.message,
message: {
id: message.id,
@ -137,12 +140,12 @@ const receiveMessage = (payload) => __awaiter(void 0, void 0, void 0, function*
var date = new Date();
date.setMilliseconds(0);
const total_spent = 1;
const { owner, sender, chat, content, msg_id } = yield helpers.parseReceiveParams(payload);
const { owner, sender, chat, content, msg_id, chat_type, sender_alias } = yield helpers.parseReceiveParams(payload);
if (!owner || !sender || !chat) {
return console.log('=> no group chat!');
}
const text = content;
const message = yield models_1.models.Message.create({
const msg = {
chatId: chat.id,
type: constants.message_types.message,
asciiEncodedTotal: total_spent,
@ -152,13 +155,17 @@ const receiveMessage = (payload) => __awaiter(void 0, void 0, void 0, function*
createdAt: date,
updatedAt: date,
status: constants.statuses.received
});
};
if (chat_type === constants.chat_types.tribe) {
msg.senderAlias = sender_alias;
}
const message = yield models_1.models.Message.create(msg);
console.log('saved message', message.dataValues);
socket.sendJson({
type: 'message',
response: jsonUtils.messageToJson(message, chat, sender)
});
hub_1.sendNotification(chat, sender.alias, 'message');
hub_1.sendNotification(chat, msg.senderAlias || sender.alias, 'message');
const theChat = Object.assign(Object.assign({}, chat.dataValues), { contactIds: [sender.id] });
confirmations_1.sendConfirmation({ chat: theChat, sender: owner, msg_id });
});

2
dist/api/controllers/messages.js.map

File diff suppressed because one or more lines are too long

15
dist/api/controllers/payment.js

@ -18,14 +18,17 @@ const res_1 = require("../utils/res");
const lightning = require("../utils/lightning");
const ldat_1 = require("../utils/ldat");
const constants = require("../../config/constants.json");
const network = require("../network");
const sendPayment = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { amount, chat_id, contact_id, destination_key, media_type, muid, text, remote_text, dimensions, remote_text_map, contact_ids, } = req.body;
console.log('[send payment]', req.body);
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
if (destination_key && !contact_id && !chat_id) {
return helpers.performKeysendMessage({
sender: owner,
destination_key,
amount,
msg: '{}',
msg: {},
success: () => {
console.log('payment sent!');
res_1.success(res, { destination_key, amount });
@ -37,7 +40,6 @@ const sendPayment = (req, res) => __awaiter(void 0, void 0, void 0, function* ()
}
});
}
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
const chat = yield helpers.findOrCreateChat({
chat_id,
owner_id: owner.id,
@ -87,7 +89,7 @@ const sendPayment = (req, res) => __awaiter(void 0, void 0, void 0, function* ()
if (remote_text_map)
msgToSend.content = remote_text_map;
}
helpers.sendMessage({
network.sendMessage({
chat: theChat,
sender: owner,
type: constants.message_types.direct_payment,
@ -113,7 +115,7 @@ const receivePayment = (payload) => __awaiter(void 0, void 0, void 0, function*
console.log('received payment', { payload });
var date = new Date();
date.setMilliseconds(0);
const { owner, sender, chat, amount, content, mediaType, mediaToken } = yield helpers.parseReceiveParams(payload);
const { owner, sender, chat, amount, content, mediaType, mediaToken, chat_type, sender_alias } = yield helpers.parseReceiveParams(payload);
if (!owner || !sender || !chat) {
return console.log('=> no group chat!');
}
@ -133,13 +135,16 @@ const receivePayment = (payload) => __awaiter(void 0, void 0, void 0, function*
msg.mediaType = mediaType;
if (mediaToken)
msg.mediaToken = mediaToken;
if (chat_type === constants.chat_types.tribe) {
msg.senderAlias = sender_alias;
}
const message = yield models_1.models.Message.create(msg);
console.log('saved message', message.dataValues);
socket.sendJson({
type: 'direct_payment',
response: jsonUtils.messageToJson(message, chat, sender)
});
hub_1.sendNotification(chat, sender.alias, 'message');
hub_1.sendNotification(chat, msg.senderAlias || sender.alias, 'message');
});
exports.receivePayment = receivePayment;
const listPayments = (req, res) => __awaiter(void 0, void 0, void 0, function* () {

2
dist/api/controllers/payment.js.map

File diff suppressed because one or more lines are too long

3
dist/api/controllers/subscriptions.js

@ -20,6 +20,7 @@ const helpers = require("../helpers");
const rsa = require("../crypto/rsa");
const moment = require("moment");
const path = require("path");
const network = require("../network");
const constants = require(path.join(__dirname, '../../config/constants.json'));
// store all current running jobs in memory
let jobs = {};
@ -126,7 +127,7 @@ function sendSubscriptionPayment(sub, isFirstMessage) {
const text = msgForSubPayment(owner, sub, isFirstMessage, forMe);
const contact = yield models_1.models.Contact.findByPk(sub.contactId);
const enc = rsa.encrypt(contact.contactKey, text);
helpers.sendMessage({
network.sendMessage({
chat: chat,
sender: owner,
type: constants.message_types.direct_payment,

2
dist/api/controllers/subscriptions.js.map

File diff suppressed because one or more lines are too long

27
dist/api/crypto/rsa.js

@ -29,6 +29,27 @@ function decrypt(privateKey, enc) {
}
}
exports.decrypt = decrypt;
function genKeys() {
return new Promise((resolve, reject) => {
crypto.generateKeyPair('rsa', {
modulusLength: 2048
}, (err, publicKey, privKey) => {
const pubPEM = publicKey.export({
type: 'pkcs1', format: 'pem'
});
const pubBase64 = cert.unpub(pubPEM);
const privPEM = privKey.export({
type: 'pkcs1', format: 'pem'
});
const privBase64 = cert.unpriv(privPEM);
resolve({
public: pubBase64,
private: privBase64,
});
});
});
}
exports.genKeys = genKeys;
function testRSA() {
crypto.generateKeyPair('rsa', {
modulusLength: 2048
@ -51,6 +72,12 @@ const cert = {
s = s.replace('-----END RSA PUBLIC KEY-----', '');
return s.replace(/[\r\n]+/gm, '');
},
unpriv: function (key) {
let s = key;
s = s.replace('-----BEGIN RSA PRIVATE KEY-----', '');
s = s.replace('-----END RSA PRIVATE KEY-----', '');
return s.replace(/[\r\n]+/gm, '');
},
pub: function (key) {
return '-----BEGIN RSA PUBLIC KEY-----\n' +
key + '\n' +

2
dist/api/crypto/rsa.js.map

@ -1 +1 @@
{"version":3,"file":"rsa.js","sourceRoot":"","sources":["../../../api/crypto/rsa.ts"],"names":[],"mappings":";;AAAA,iCAAiC;AAEjC,SAAgB,OAAO,CAAC,GAAG,EAAE,GAAG;IAC9B,IAAG;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC1B,MAAM,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC;YAC/B,GAAG,EAAC,IAAI;YACR,OAAO,EAAC,MAAM,CAAC,SAAS,CAAC,iBAAiB;SAC3C,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAC,OAAO,CAAC,CAAC,CAAA;QAC5B,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;KAC9B;IAAC,OAAM,CAAC,EAAE;QACT,OAAO,EAAE,CAAA;KACV;AACH,CAAC;AAXD,0BAWC;AAED,SAAgB,OAAO,CAAC,UAAU,EAAE,GAAG;IACrC,IAAG;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACnC,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,CAAC;YAChC,GAAG,EAAC,KAAK;YACT,OAAO,EAAC,MAAM,CAAC,SAAS,CAAC,iBAAiB;SAC3C,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAC,QAAQ,CAAC,CAAC,CAAA;QAC7B,OAAO,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;KAC7B;IAAC,OAAM,CAAC,EAAE;QACT,OAAO,EAAE,CAAA;KACV;AACH,CAAC;AAXD,0BAWC;AAED,SAAgB,OAAO;IACrB,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE;QAC5B,aAAa,EAAE,IAAI;KACpB,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAC,EAAE;QACzB,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;YAC9B,IAAI,EAAC,OAAO,EAAC,MAAM,EAAC,KAAK;SAC1B,CAAC,CAAA;QACF,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QAE9B,MAAM,GAAG,GAAG,IAAI,CAAA;QAChB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;QAE7B,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;QAC9B,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAC,GAAG,CAAC,CAAA;IAC3B,CAAC,CAAC,CAAA;AACJ,CAAC;AAfD,0BAeC;AAED,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,UAAS,GAAG;QACjB,IAAI,CAAC,GAAG,GAAG,CAAA;QACX,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,gCAAgC,EAAC,EAAE,CAAC,CAAA;QAClD,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,8BAA8B,EAAC,EAAE,CAAC,CAAA;QAChD,OAAO,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;IACnC,CAAC;IACD,GAAG,EAAC,UAAS,GAAG;QACd,OAAO,kCAAkC;YACvC,GAAG,GAAG,IAAI;YACV,8BAA8B,CAAA;IAClC,CAAC;IACD,IAAI,EAAC,UAAS,GAAG;QACf,OAAO,mCAAmC;YACxC,GAAG,GAAG,IAAI;YACV,+BAA+B,CAAA;IACnC,CAAC;CACF,CAAA"}
{"version":3,"file":"rsa.js","sourceRoot":"","sources":["../../../api/crypto/rsa.ts"],"names":[],"mappings":"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"}

105
dist/api/grpc/index.js

@ -15,121 +15,24 @@ const hub_1 = require("../hub");
const jsonUtils = require("../utils/json");
const decodeUtils = require("../utils/decode");
const lightning_1 = require("../utils/lightning");
const controllers = require("../controllers");
const network = require("../network");
const moment = require("moment");
const path = require("path");
const constants = require(path.join(__dirname, '../../config/constants.json'));
const ERR_CODE_UNAVAILABLE = 14;
const ERR_CODE_STREAM_REMOVED = 2;
// VERIFY PUBKEY OF SENDER
function parseAndVerifyPayload(data) {
return __awaiter(this, void 0, void 0, function* () {
let payload;
const li = data.lastIndexOf('}');
const msg = data.substring(0, li + 1);
const sig = data.substring(li + 1);
try {
payload = JSON.parse(msg);
if (payload) {
const v = yield lightning_1.verifyAscii(msg, sig);
if (v && v.valid && v.pubkey) {
payload.sender = payload.sender || {};
payload.sender.pub_key = v.pubkey;
return payload;
}
else {
console.error('[GRPC] invalid payload signature');
}
}
}
catch (e) {
console.error('[GRPC] failed to parse msg');
return null;
}
});
}
function parseKeysendInvoice(i, actions) {
return __awaiter(this, void 0, void 0, function* () {
const recs = i.htlcs && i.htlcs[0] && i.htlcs[0].custom_records;
const buf = recs && recs[lightning_1.SPHINX_CUSTOM_RECORD_KEY];
const data = buf && buf.toString();
const value = i && i.value && parseInt(i.value);
if (!data) {
console.error('[GRPC] no keysend data received');
return;
}
let payload;
if (data[0] === '{') {
try {
payload = yield parseAndVerifyPayload(data);
}
catch (e) {
console.error('[GRPC] failed to parse and verify payload');
}
}
else {
const threads = weave(data);
if (threads) {
try {
payload = yield parseAndVerifyPayload(threads);
}
catch (e) {
console.error('[GRPC] failed to parse and verify payload II');
}
}
}
if (payload) {
const dat = payload.content || payload;
if (value && dat && dat.message) {
dat.message.amount = value; // ADD IN TRUE VALUE
}
if (actions[payload.type]) {
actions[payload.type](payload);
}
else {
console.log('Incorrect payload type:', payload.type);
}
}
else {
console.error('[GRPC] no payload');
}
});
}
const chunks = {};
function weave(p) {
const pa = p.split('_');
if (pa.length < 4)
return;
const ts = pa[0];
const i = pa[1];
const n = pa[2];
const m = pa.filter((u, i) => i > 2).join('_');
chunks[ts] = chunks[ts] ? [...chunks[ts], { i, n, m }] : [{ i, n, m }];
if (chunks[ts].length === parseInt(n)) {
// got em all!
const all = chunks[ts];
let payload = '';
all.slice().sort((a, b) => a.i - b.i).forEach(obj => {
payload += obj.m;
});
delete chunks[ts];
return payload;
}
}
function subscribeInvoices(actions) {
function subscribeInvoices(parseKeysendInvoice) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
const lightning = yield lightning_1.loadLightning();
var call = lightning.subscribeInvoices();
call.on('data', function (response) {
return __awaiter(this, void 0, void 0, function* () {
// console.log('subscribed invoices', { response })
console.log('[GRPC] subscribeInvoices received');
if (response['state'] !== 'SETTLED') {
return;
}
// console.log("IS KEYSEND", response.is_keysend)
if (response.is_keysend) {
parseKeysendInvoice(response, actions);
parseKeysendInvoice(response);
}
else {
const invoice = yield models_1.models.Message.findOne({ where: { type: constants.message_types.invoice, payment_request: response['payment_request'] } });
@ -224,7 +127,7 @@ function reconnectToLND(innerCtx) {
i++;
console.log(`=> [lnd] reconnecting... attempt #${i}`);
try {
yield controllers.iniGrpcSubscriptions();
yield network.initGrpcSubscriptions();
const now = moment().format('YYYY-MM-DD HH:mm:ss').trim();
console.log(`=> [lnd] reconnected! ${now}`);
}

2
dist/api/grpc/index.js.map

File diff suppressed because one or more lines are too long

83
dist/api/helpers.js

@ -11,8 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
Object.defineProperty(exports, "__esModule", { value: true });
const models_1 = require("./models");
const md5 = require("md5");
const lightning_1 = require("./utils/lightning");
const msg_1 = require("./utils/msg");
const network = require("./network");
const constants = require('../config/constants.json');
const findOrCreateChat = (params) => __awaiter(void 0, void 0, void 0, function* () {
const { chat_id, owner_id, recipient_id } = params;
@ -64,9 +63,10 @@ const sendContactKeys = (args) => __awaiter(void 0, void 0, void 0, function* ()
destination_key = contact.publicKey;
}
performKeysendMessage({
sender,
destination_key,
amount: 3,
msg: JSON.stringify(msg),
msg,
success: (data) => {
yes = data;
},
@ -83,53 +83,14 @@ const sendContactKeys = (args) => __awaiter(void 0, void 0, void 0, function* ()
}
});
exports.sendContactKeys = sendContactKeys;
const sendMessage = (params) => __awaiter(void 0, void 0, void 0, function* () {
const { type, chat, message, sender, amount, success, failure } = params;
const m = newmsg(type, chat, sender, message);
const contactIds = typeof chat.contactIds === 'string' ? JSON.parse(chat.contactIds) : chat.contactIds;
let yes = null;
let no = null;
console.log('all contactIds', contactIds);
yield asyncForEach(contactIds, (contactId) => __awaiter(void 0, void 0, void 0, function* () {
if (contactId == sender.id) {
return;
}
console.log('-> sending to contact #', contactId);
const contact = yield models_1.models.Contact.findOne({ where: { id: contactId } });
const destkey = contact.publicKey;
const finalMsg = yield msg_1.personalizeMessage(m, contactId, destkey);
const opts = {
dest: destkey,
data: JSON.stringify(finalMsg),
amt: amount || 3,
};
try {
const r = yield lightning_1.keysendMessage(opts);
yes = r;
}
catch (e) {
console.log("KEYSEND ERROR", e);
no = e;
}
}));
if (yes) {
if (success)
success(yes);
}
else {
if (failure)
failure(no);
}
});
exports.sendMessage = sendMessage;
const performKeysendMessage = ({ destination_key, amount, msg, success, failure }) => __awaiter(void 0, void 0, void 0, function* () {
const performKeysendMessage = ({ destination_key, amount, msg, success, failure, sender }) => __awaiter(void 0, void 0, void 0, function* () {
const opts = {
dest: destination_key,
data: msg || JSON.stringify({}),
data: msg || {},
amt: Math.max(amount, 3)
};
try {
const r = yield lightning_1.keysendMessage(opts);
const r = yield network.signAndSend(opts, sender.publicKey);
console.log("=> external keysend");
if (success)
success(r);
@ -189,29 +150,40 @@ function parseReceiveParams(payload) {
return __awaiter(this, void 0, void 0, function* () {
const dat = payload.content || payload;
const sender_pub_key = dat.sender.pub_key;
const sender_alias = dat.sender.alias;
const chat_uuid = dat.chat.uuid;
const chat_type = dat.chat.type;
const chat_members = dat.chat.members || {};
const chat_name = dat.chat.name;
const chat_key = dat.chat.groupKey;
const chat_host = dat.chat.host;
const amount = dat.message.amount;
const content = dat.message.content;
const mediaToken = dat.message.mediaToken;
const msg_id = dat.message.id || 0;
const mediaKey = dat.message.mediaKey;
const mediaType = dat.message.mediaType;
const isGroup = chat_type && chat_type == constants.chat_types.group;
const isTribeOwner = dat.isTribeOwner ? true : false;
const isConversation = !chat_type || (chat_type && chat_type == constants.chat_types.conversation);
let sender;
let chat;
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
if (isGroup) {
sender = yield models_1.models.Contact.findOne({ where: { publicKey: sender_pub_key } });
chat = yield models_1.models.Chat.findOne({ where: { uuid: chat_uuid } });
}
else {
if (isConversation) {
sender = yield findOrCreateContactByPubkey(sender_pub_key);
chat = yield findOrCreateChatByUUID(chat_uuid, [parseInt(owner.id), parseInt(sender.id)]);
if (sender.fromGroup) { // if a private msg received, update the contact
yield sender.update({ fromGroup: false });
}
}
else { // group
sender = yield models_1.models.Contact.findOne({ where: { publicKey: sender_pub_key } });
// inject a "sender" with an alias
if (!sender && chat_type == constants.chat_types.tribe) {
sender = { id: 0, alias: sender_alias };
}
chat = yield models_1.models.Chat.findOne({ where: { uuid: chat_uuid } });
}
return { owner, sender, chat, sender_pub_key, chat_uuid, amount, content, mediaToken, mediaKey, mediaType, chat_type, msg_id, chat_members, chat_name };
return { owner, sender, chat, sender_pub_key, sender_alias, isTribeOwner, chat_uuid, amount, content, mediaToken, mediaKey, mediaType, chat_type, msg_id, chat_members, chat_name, chat_host, chat_key };
});
}
exports.parseReceiveParams = parseReceiveParams;
@ -222,13 +194,6 @@ function asyncForEach(array, callback) {
}
});
}
function newmsg(type, chat, sender, message) {
return {
type: type,
chat: Object.assign(Object.assign(Object.assign({ uuid: chat.uuid }, chat.name && { name: chat.name }), chat.type && { type: chat.type }), chat.members && { members: chat.members }),
message: message,
};
}
function newkeyexchangemsg(type, sender) {
return {
type: type,

2
dist/api/helpers.js.map

File diff suppressed because one or more lines are too long

3
dist/api/models/index.js

@ -8,9 +8,10 @@ const invite_1 = require("./ts/invite");
const message_1 = require("./ts/message");
const subscription_1 = require("./ts/subscription");
const mediaKey_1 = require("./ts/mediaKey");
const chatMember_1 = require("./ts/chatMember");
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname, '../../config/config.json'))[env];
const sequelize = new sequelize_typescript_1.Sequelize(Object.assign(Object.assign({}, config), { logging: process.env.SQL_LOG === 'true' ? console.log : false, models: [chat_1.default, contact_1.default, invite_1.default, message_1.default, subscription_1.default, mediaKey_1.default] }));
const sequelize = new sequelize_typescript_1.Sequelize(Object.assign(Object.assign({}, config), { logging: process.env.SQL_LOG === 'true' ? console.log : false, models: [chat_1.default, contact_1.default, invite_1.default, message_1.default, subscription_1.default, mediaKey_1.default, chatMember_1.default] }));
exports.sequelize = sequelize;
const models = sequelize.models;
exports.models = models;

2
dist/api/models/index.js.map

@ -1 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../api/models/index.ts"],"names":[],"mappings":";;AAAA,+DAA+C;AAC/C,6BAA4B;AAC5B,oCAA4B;AAC5B,0CAAkC;AAClC,wCAAgC;AAChC,0CAAkC;AAClC,oDAA4C;AAC5C,4CAAoC;AAEpC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa,CAAC;AAClD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAC,0BAA0B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAE5E,MAAM,SAAS,GAAG,IAAI,gCAAS,iCAC1B,MAAM,KACT,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,KAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAC3D,MAAM,EAAE,CAAC,cAAI,EAAC,iBAAO,EAAC,gBAAM,EAAC,iBAAO,EAAC,sBAAY,EAAC,kBAAQ,CAAC,IAC3D,CAAA;AAIA,8BAAS;AAHX,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAA;AAI7B,wBAAM"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../api/models/index.ts"],"names":[],"mappings":";;AAAA,+DAA+C;AAC/C,6BAA4B;AAC5B,oCAA4B;AAC5B,0CAAkC;AAClC,wCAAgC;AAChC,0CAAkC;AAClC,oDAA4C;AAC5C,4CAAoC;AACpC,gDAAwC;AAExC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa,CAAC;AAClD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAC,0BAA0B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAE5E,MAAM,SAAS,GAAG,IAAI,gCAAS,iCAC1B,MAAM,KACT,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,KAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAC3D,MAAM,EAAE,CAAC,cAAI,EAAC,iBAAO,EAAC,gBAAM,EAAC,iBAAO,EAAC,sBAAY,EAAC,kBAAQ,EAAC,oBAAU,CAAC,IACtE,CAAA;AAIA,8BAAS;AAHX,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAA;AAI7B,wBAAM"}

24
dist/api/models/ts/chat.js

@ -65,6 +65,30 @@ __decorate([
}),
__metadata("design:type", Boolean)
], Chat.prototype, "deleted", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", String)
], Chat.prototype, "groupKey", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", String)
], Chat.prototype, "groupPrivateKey", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", String)
], Chat.prototype, "host", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Number)
], Chat.prototype, "priceToJoin", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Number)
], Chat.prototype, "pricePerMessage", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", String)
], Chat.prototype, "ownerPubkey", void 0);
Chat = __decorate([
sequelize_typescript_1.Table({ tableName: 'sphinx_chats', underscored: true })
], Chat);

2
dist/api/models/ts/chat.js.map

@ -1 +1 @@
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../../api/models/ts/chat.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+DAAsE;AAGtE,IAAqB,IAAI,GAAzB,MAAqB,IAAK,SAAQ,4BAAW;CA4C5C,CAAA;AApCC;IANC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,MAAM;QACrB,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;KACpB,CAAC;;gCACQ;AAGV;IADC,6BAAM;;kCACK;AAGZ;IADC,6BAAM;;kCACK;AAGZ;IADC,6BAAM;;sCACS;AAGhB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;kCACZ;AAGZ;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;oCACV;AAGd;IADC,6BAAM;;wCACW;AAGlB;IADC,6BAAM;;qCACS;AAGhB;IADC,6BAAM;8BACI,IAAI;uCAAA;AAGf;IADC,6BAAM;8BACI,IAAI;uCAAA;AAOf;IALC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,OAAO;QACtB,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,KAAK;KACjB,CAAC;;qCACc;AA1CG,IAAI;IADxB,4BAAK,CAAC,EAAC,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAC,CAAC;GACjC,IAAI,CA4CxB;kBA5CoB,IAAI"}
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../../api/models/ts/chat.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+DAAsE;AAGtE,IAAqB,IAAI,GAAzB,MAAqB,IAAK,SAAQ,4BAAW;CA8D5C,CAAA;AAtDC;IANC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,MAAM;QACrB,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;KACpB,CAAC;;gCACQ;AAGV;IADC,6BAAM;;kCACK;AAGZ;IADC,6BAAM;;kCACK;AAGZ;IADC,6BAAM;;sCACS;AAGhB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;kCACZ;AAGZ;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;oCACV;AAGd;IADC,6BAAM;;wCACW;AAGlB;IADC,6BAAM;;qCACS;AAGhB;IADC,6BAAM;8BACI,IAAI;uCAAA;AAGf;IADC,6BAAM;8BACI,IAAI;uCAAA;AAOf;IALC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,OAAO;QACtB,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,KAAK;KACjB,CAAC;;qCACc;AAGhB;IADC,6BAAM;;sCACS;AAGhB;IADC,6BAAM;;6CACgB;AAGvB;IADC,6BAAM;;kCACK;AAGZ;IADC,6BAAM;;yCACY;AAGnB;IADC,6BAAM;;6CACgB;AAGvB;IADC,6BAAM;;yCACY;AA5DA,IAAI;IADxB,4BAAK,CAAC,EAAC,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAC,CAAC;GACjC,IAAI,CA8DxB;kBA9DoB,IAAI"}

43
dist/api/models/ts/chatMember.js

@ -0,0 +1,43 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
const sequelize_typescript_1 = require("sequelize-typescript");
let ChatMember = class ChatMember extends sequelize_typescript_1.Model {
};
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Number)
], ChatMember.prototype, "chatId", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Number)
], ChatMember.prototype, "contactId", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Number)
], ChatMember.prototype, "role", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Number)
], ChatMember.prototype, "totalSpent", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Number)
], ChatMember.prototype, "totalMessages", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Date)
], ChatMember.prototype, "lastActive", void 0);
ChatMember = __decorate([
sequelize_typescript_1.Table({ tableName: 'sphinx_chat_members', underscored: true })
], ChatMember);
exports.default = ChatMember;
//# sourceMappingURL=chatMember.js.map

1
dist/api/models/ts/chatMember.js.map

@ -0,0 +1 @@
{"version":3,"file":"chatMember.js","sourceRoot":"","sources":["../../../../api/models/ts/chatMember.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+DAA4D;AAG5D,IAAqB,UAAU,GAA/B,MAAqB,UAAW,SAAQ,4BAAiB;CAoBxD,CAAA;AAjBC;IADC,6BAAM;;0CACO;AAGd;IADC,6BAAM;;6CACU;AAGjB;IADC,6BAAM;;wCACK;AAGZ;IADC,6BAAM;;8CACW;AAGlB;IADC,6BAAM;;iDACc;AAGrB;IADC,6BAAM;8BACK,IAAI;8CAAA;AAlBG,UAAU;IAD9B,4BAAK,CAAC,EAAC,SAAS,EAAE,qBAAqB,EAAE,WAAW,EAAE,IAAI,EAAC,CAAC;GACxC,UAAU,CAoB9B;kBApBoB,UAAU"}

4
dist/api/models/ts/contact.js

@ -77,6 +77,10 @@ __decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Date)
], Contact.prototype, "updatedAt", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Boolean)
], Contact.prototype, "fromGroup", void 0);
Contact = __decorate([
sequelize_typescript_1.Table({ tableName: 'sphinx_contacts', underscored: true })
], Contact);

2
dist/api/models/ts/contact.js.map

@ -1 +1 @@
{"version":3,"file":"contact.js","sourceRoot":"","sources":["../../../../api/models/ts/contact.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+DAAsE;AAGtE,IAAqB,OAAO,GAA5B,MAAqB,OAAQ,SAAQ,4BAAc;CAqDlD,CAAA;AA7CC;IANC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,MAAM;QACrB,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;KACpB,CAAC;;mCACQ;AAGV;IADC,6BAAM;;0CACU;AAGjB;IADC,6BAAM;;0CACU;AAGjB;IADC,6BAAM;;sCACM;AAGb;IADC,6BAAM;;yCACS;AAGhB;IADC,6BAAM;;wCACS;AAOhB;IALC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,OAAO;QACtB,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,KAAK;KACjB,CAAC;;wCACc;AAGhB;IADC,6BAAM;;0CACU;AAGjB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;yCACR;AAGhB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;uCACV;AAGd;IADC,6BAAM,CAAC,+BAAQ,CAAC,IAAI,CAAC;;2CACJ;AAGlB;IADC,6BAAM;;yCACS;AAGhB;IADC,6BAAM;8BACI,IAAI;0CAAA;AAGf;IADC,6BAAM;8BACI,IAAI;0CAAA;AAnDI,OAAO;IAD3B,4BAAK,CAAC,EAAC,SAAS,EAAE,iBAAiB,EAAE,WAAW,EAAE,IAAI,EAAC,CAAC;GACpC,OAAO,CAqD3B;kBArDoB,OAAO"}
{"version":3,"file":"contact.js","sourceRoot":"","sources":["../../../../api/models/ts/contact.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+DAAsE;AAGtE,IAAqB,OAAO,GAA5B,MAAqB,OAAQ,SAAQ,4BAAc;CAwDlD,CAAA;AAhDC;IANC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,MAAM;QACrB,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;KACpB,CAAC;;mCACQ;AAGV;IADC,6BAAM;;0CACU;AAGjB;IADC,6BAAM;;0CACU;AAGjB;IADC,6BAAM;;sCACM;AAGb;IADC,6BAAM;;yCACS;AAGhB;IADC,6BAAM;;wCACS;AAOhB;IALC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,OAAO;QACtB,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,KAAK;KACjB,CAAC;;wCACc;AAGhB;IADC,6BAAM;;0CACU;AAGjB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;yCACR;AAGhB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;uCACV;AAGd;IADC,6BAAM,CAAC,+BAAQ,CAAC,IAAI,CAAC;;2CACJ;AAGlB;IADC,6BAAM;;yCACS;AAGhB;IADC,6BAAM;8BACI,IAAI;0CAAA;AAGf;IADC,6BAAM;8BACI,IAAI;0CAAA;AAGf;IADC,6BAAM;;0CACW;AAtDC,OAAO;IAD3B,4BAAK,CAAC,EAAC,SAAS,EAAE,iBAAiB,EAAE,WAAW,EAAE,IAAI,EAAC,CAAC;GACpC,OAAO,CAwD3B;kBAxDoB,OAAO"}

4
dist/api/models/ts/message.js

@ -113,6 +113,10 @@ __decorate([
sequelize_typescript_1.Column,
__metadata("design:type", Date)
], Message.prototype, "updatedAt", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", String)
], Message.prototype, "senderAlias", void 0);
Message = __decorate([
sequelize_typescript_1.Table({ tableName: 'sphinx_messages', underscored: true })
], Message);

2
dist/api/models/ts/message.js.map

@ -1 +1 @@
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../../../api/models/ts/message.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+DAAsE;AAGtE,IAAqB,OAAO,GAA5B,MAAqB,OAAQ,SAAQ,4BAAc;CA+ElD,CAAA;AAvEC;IANC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,MAAM;QACrB,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;KACpB,CAAC;;mCACQ;AAGV;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;uCACV;AAGd;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;qCACZ;AAGZ;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;uCACV;AAGd;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;yCACR;AAGhB;IADC,6BAAM,CAAC,+BAAQ,CAAC,OAAO,CAAC;;uCACX;AAGd;IADC,6BAAM,CAAC,+BAAQ,CAAC,OAAO,CAAC;;2CACP;AAGlB;IADC,6BAAM;;4CACY;AAGnB;IADC,6BAAM,CAAC,+BAAQ,CAAC,IAAI,CAAC;;+CACA;AAGtB;IADC,6BAAM;8BACD,IAAI;qCAAA;AAGV;IADC,6BAAM;8BACS,IAAI;+CAAA;AAGpB;IADC,6BAAM,CAAC,+BAAQ,CAAC,IAAI,CAAC;;+CACA;AAGtB;IADC,6BAAM,CAAC,+BAAQ,CAAC,IAAI,CAAC;;qDACM;AAG5B;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;uCACV;AAGd;IADC,6BAAM,CAAC,+BAAQ,CAAC,IAAI,CAAC;;0CACL;AAGjB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;yCACR;AAGhB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;+CACF;AAGtB;IADC,6BAAM;;yCACS;AAGhB;IADC,6BAAM;;0CACU;AAGjB;IADC,6BAAM;;2CACW;AAOlB;IALC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,OAAO;QACtB,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,KAAK;KACjB,CAAC;;qCACW;AAGb;IADC,6BAAM;8BACI,IAAI;0CAAA;AAGf;IADC,6BAAM;8BACI,IAAI;0CAAA;AA9EI,OAAO;IAD3B,4BAAK,CAAC,EAAC,SAAS,EAAE,iBAAiB,EAAE,WAAW,EAAE,IAAI,EAAC,CAAC;GACpC,OAAO,CA+E3B;kBA/EoB,OAAO"}
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../../../api/models/ts/message.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+DAAsE;AAGtE,IAAqB,OAAO,GAA5B,MAAqB,OAAQ,SAAQ,4BAAc;CAkFlD,CAAA;AA1EC;IANC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,MAAM;QACrB,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;KACpB,CAAC;;mCACQ;AAGV;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;uCACV;AAGd;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;qCACZ;AAGZ;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;uCACV;AAGd;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;yCACR;AAGhB;IADC,6BAAM,CAAC,+BAAQ,CAAC,OAAO,CAAC;;uCACX;AAGd;IADC,6BAAM,CAAC,+BAAQ,CAAC,OAAO,CAAC;;2CACP;AAGlB;IADC,6BAAM;;4CACY;AAGnB;IADC,6BAAM,CAAC,+BAAQ,CAAC,IAAI,CAAC;;+CACA;AAGtB;IADC,6BAAM;8BACD,IAAI;qCAAA;AAGV;IADC,6BAAM;8BACS,IAAI;+CAAA;AAGpB;IADC,6BAAM,CAAC,+BAAQ,CAAC,IAAI,CAAC;;+CACA;AAGtB;IADC,6BAAM,CAAC,+BAAQ,CAAC,IAAI,CAAC;;qDACM;AAG5B;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;uCACV;AAGd;IADC,6BAAM,CAAC,+BAAQ,CAAC,IAAI,CAAC;;0CACL;AAGjB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;yCACR;AAGhB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;+CACF;AAGtB;IADC,6BAAM;;yCACS;AAGhB;IADC,6BAAM;;0CACU;AAGjB;IADC,6BAAM;;2CACW;AAOlB;IALC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,OAAO;QACtB,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,KAAK;KACjB,CAAC;;qCACW;AAGb;IADC,6BAAM;8BACI,IAAI;0CAAA;AAGf;IADC,6BAAM;8BACI,IAAI;0CAAA;AAGf;IADC,6BAAM;;4CACY;AAjFA,OAAO;IAD3B,4BAAK,CAAC,EAAC,SAAS,EAAE,iBAAiB,EAAE,WAAW,EAAE,IAAI,EAAC,CAAC;GACpC,OAAO,CAkF3B;kBAlFoB,OAAO"}

10
dist/api/network/index.js

@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const send_1 = require("./send");
exports.sendMessage = send_1.sendMessage;
exports.signAndSend = send_1.signAndSend;
const receive_1 = require("./receive");
exports.initGrpcSubscriptions = receive_1.initGrpcSubscriptions;
exports.initTribesSubscriptions = receive_1.initTribesSubscriptions;
exports.parseKeysendInvoice = receive_1.parseKeysendInvoice;
//# sourceMappingURL=index.js.map

1
dist/api/network/index.js.map

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../api/network/index.ts"],"names":[],"mappings":";;AAAA,iCAA8C;AAQ1C,sBARI,kBAAW,CAQJ;AAAC,sBARI,kBAAW,CAQJ;AAP3B,uCAA2F;AAQvF,gCARI,+BAAqB,CAQJ;AACrB,kCAT0B,iCAAuB,CAS1B;AACvB,8BAVkD,6BAAmB,CAUlD"}

84
dist/api/network/modify.js

@ -0,0 +1,84 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path");
const rncryptor_1 = require("../utils/rncryptor");
const fetch = require("node-fetch");
const ldat_1 = require("../utils/ldat");
const rsa = require("../crypto/rsa");
const crypto = require("crypto");
const meme = require("../utils/meme");
const FormData = require("form-data");
const constants = require(path.join(__dirname, '../../config/constants.json'));
const msgtypes = constants.message_types;
function modifyPayload(payload, chat) {
return __awaiter(this, void 0, void 0, function* () {
if (payload.type === msgtypes.attachment) {
const mt = payload.message && payload.message.mediaToken;
const key = payload.message && payload.message.mediaKey;
const typ = payload.message && payload.message.mediaType;
if (!mt || !key)
return payload;
const terms = ldat_1.parseLDAT(mt);
if (!terms.host)
return payload;
try {
const r = yield fetch(`https://${terms.host}/file/${mt}`, {
headers: { 'Authorization': `Bearer ${meme.mediaToken}` }
});
const buf = yield r.buffer();
const decMediaKey = rsa.decrypt(chat.groupPrivateKey, key);
const imgBase64 = rncryptor_1.default.Decrypt(decMediaKey, buf.toString('base64'));
const newKey = crypto.randomBytes(20).toString('hex');
const encImg = rncryptor_1.default.Encrypt(newKey, imgBase64);
var encImgBuffer = Buffer.from(encImg, 'base64');
const form = new FormData();
form.append('file', encImgBuffer, {
contentType: typ || 'image/jpg',
filename: 'Image.jpg',
knownLength: encImgBuffer.length,
});
const formHeaders = form.getHeaders();
const resp = yield fetch(`https://${terms.host}/file`, {
method: 'POST',
headers: Object.assign(Object.assign({}, formHeaders), { 'Authorization': `Bearer ${meme.mediaToken}` }),
body: form
});
let json = yield resp.json();
if (!json.muid)
return payload;
// PUT NEW TERMS, to finish in personalizeMessage
const amt = terms.meta && terms.meta.amt;
const ttl = terms.meta && terms.meta.ttl;
const mediaTerms = {
muid: json.muid, ttl: ttl || 31536000,
meta: Object.assign({}, amt && { amt }),
skipSigning: amt ? true : false // only sign if its free
};
const encKey = rsa.encrypt(chat.groupKey, newKey);
return fillmsg(payload, { mediaTerms, mediaKey: encKey }); // key is re-encrypted later
}
catch (e) {
console.log("[modify] error", e);
return payload;
}
// how to link w og msg? ogMediaToken?
}
else {
return payload;
}
});
}
exports.modifyPayload = modifyPayload;
function fillmsg(full, props) {
return Object.assign(Object.assign({}, full), { message: Object.assign(Object.assign({}, full.message), props) });
}
//# sourceMappingURL=modify.js.map

1
dist/api/network/modify.js.map

@ -0,0 +1 @@
{"version":3,"file":"modify.js","sourceRoot":"","sources":["../../../api/network/modify.ts"],"names":[],"mappings":"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"}

240
dist/api/network/receive.js

@ -0,0 +1,240 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path");
const lndService = require("../grpc");
const lightning_1 = require("../utils/lightning");
const controllers_1 = require("../controllers");
const tribes = require("../utils/tribes");
const lightning_2 = require("../utils/lightning");
const models_1 = require("../models");
const send_1 = require("./send");
const modify_1 = require("./modify");
const msg_1 = require("../utils/msg");
const constants = require(path.join(__dirname, '../../config/constants.json'));
const msgtypes = constants.message_types;
const typesToForward = [
msgtypes.message, msgtypes.group_join, msgtypes.group_leave, msgtypes.attachment
];
const typesToModify = [
msgtypes.attachment
];
const typesThatNeedPricePerMessage = [
msgtypes.message, msgtypes.attachment
];
function onReceive(payload) {
return __awaiter(this, void 0, void 0, function* () {
console.log("=>>> onReceive", payload);
// if tribe, owner must forward to MQTT
let doAction = true;
const toAddIn = {};
const isTribe = payload.chat && payload.chat.type === constants.chat_types.tribe;
if (isTribe && typesToForward.includes(payload.type)) {
const needsPricePerJoin = typesThatNeedPricePerMessage.includes(payload.type);
const chat = yield models_1.models.Chat.findOne({ where: { uuid: payload.chat.uuid } });
const tribeOwnerPubKey = chat && chat.ownerPubkey;
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
if (owner.publicKey === tribeOwnerPubKey) {
toAddIn.isTribeOwner = true;
// CHECK THEY ARE IN THE GROUP if message
if (needsPricePerJoin) {
const senderContact = yield models_1.models.Contact.findOne({ where: { publicKey: payload.sender.pub_key } });
const senderMember = senderContact && (yield models_1.models.ChatMember.findOne({ where: { contactId: senderContact.id, chatId: chat.id } }));
if (!senderMember)
doAction = false;
}
// CHECK PRICES
if (needsPricePerJoin) {
if (payload.message.amount < chat.pricePerMessage)
doAction = false;
}
// check price to join
if (payload.type === msgtypes.group_join) {
if (payload.message.amount < chat.priceToJoin)
doAction = false;
}
if (doAction)
forwardMessageToTribe(payload);
else
console.log('=> insufficient payment for this action');
}
}
if (doAction)
doTheAction(Object.assign(Object.assign({}, payload), toAddIn));
});
}
function doTheAction(data) {
return __awaiter(this, void 0, void 0, function* () {
let payload = data;
if (payload.isTribeOwner) {
// decrypt and re-encrypt with self pubkey
const chat = yield models_1.models.Chat.findOne({ where: { uuid: payload.chat.uuid } });
const pld = yield msg_1.decryptMessage(data, chat);
const me = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
payload = yield msg_1.encryptTribeBroadcast(pld, me, true); // true=isTribeOwner
}
if (ACTIONS[payload.type]) {
ACTIONS[payload.type](payload);
}
else {
console.log('Incorrect payload type:', payload.type);
}
});
}
function forwardMessageToTribe(ogpayload) {
return __awaiter(this, void 0, void 0, function* () {
const chat = yield models_1.models.Chat.findOne({ where: { uuid: ogpayload.chat.uuid } });
let payload;
if (typesToModify.includes(ogpayload.type)) {
payload = yield modify_1.modifyPayload(ogpayload, chat);
}
else {
payload = ogpayload;
}
//console.log("FORWARD TO TRIBE",payload) // filter out the sender?
//const sender = await models.Contact.findOne({where:{publicKey:payload.sender.pub_key}})
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
const type = payload.type;
const message = payload.message;
// HERE: NEED TO MAKE SURE ALIAS IS UNIQUE
// ASK xref TABLE and put alias there too?
send_1.sendMessage({
sender: Object.assign(Object.assign({}, owner.dataValues), payload.sender && payload.sender.alias && { alias: payload.sender.alias }),
chat, type, message,
skipPubKey: payload.sender.pub_key,
success: () => { },
receive: () => { }
});
});
}
const ACTIONS = {
[msgtypes.contact_key]: controllers_1.controllers.contacts.receiveContactKey,
[msgtypes.contact_key_confirmation]: controllers_1.controllers.contacts.receiveConfirmContactKey,
[msgtypes.message]: controllers_1.controllers.messages.receiveMessage,
[msgtypes.invoice]: controllers_1.controllers.invoices.receiveInvoice,
[msgtypes.direct_payment]: controllers_1.controllers.payments.receivePayment,
[msgtypes.confirmation]: controllers_1.controllers.confirmations.receiveConfirmation,
[msgtypes.attachment]: controllers_1.controllers.media.receiveAttachment,
[msgtypes.purchase]: controllers_1.controllers.media.receivePurchase,
[msgtypes.purchase_accept]: controllers_1.controllers.media.receivePurchaseAccept,
[msgtypes.purchase_deny]: controllers_1.controllers.media.receivePurchaseDeny,
[msgtypes.group_create]: controllers_1.controllers.chats.receiveGroupCreateOrInvite,
[msgtypes.group_invite]: controllers_1.controllers.chats.receiveGroupCreateOrInvite,
[msgtypes.group_join]: controllers_1.controllers.chats.receiveGroupJoin,
[msgtypes.group_leave]: controllers_1.controllers.chats.receiveGroupLeave,
};
function initGrpcSubscriptions() {
return __awaiter(this, void 0, void 0, function* () {
try {
yield lightning_1.getInfo();
yield lndService.subscribeInvoices(parseKeysendInvoice);
}
catch (e) {
throw e;
}
});
}
exports.initGrpcSubscriptions = initGrpcSubscriptions;
function initTribesSubscriptions() {
return __awaiter(this, void 0, void 0, function* () {
tribes.connect((topic, message) => __awaiter(this, void 0, void 0, function* () {
try {
const msg = message.toString();
console.log("=====> msg received! TOPIC", topic, "MESSAGE", msg);
// check topic is signed by sender?
const payload = yield parseAndVerifyPayload(msg);
onReceive(payload);
}
catch (e) { }
}));
});
}
exports.initTribesSubscriptions = initTribesSubscriptions;
// VERIFY PUBKEY OF SENDER from sig
function parseAndVerifyPayload(data) {
return __awaiter(this, void 0, void 0, function* () {
let payload;
const li = data.lastIndexOf('}');
const msg = data.substring(0, li + 1);
const sig = data.substring(li + 1);
try {
payload = JSON.parse(msg);
if (payload) {
const v = yield lightning_2.verifyAscii(msg, sig);
if (v && v.valid && v.pubkey) {
payload.sender = payload.sender || {};
payload.sender.pub_key = v.pubkey;
return payload;
}
else {
return payload; // => RM THIS
}
}
}
catch (e) {
if (payload)
return payload; // => RM THIS
return null;
}
});
}
function parseKeysendInvoice(i) {
return __awaiter(this, void 0, void 0, function* () {
const recs = i.htlcs && i.htlcs[0] && i.htlcs[0].custom_records;
const buf = recs && recs[lightning_2.SPHINX_CUSTOM_RECORD_KEY];
const data = buf && buf.toString();
const value = i && i.value && parseInt(i.value);
if (!data)
return;
let payload;
if (data[0] === '{') {
try {
payload = yield parseAndVerifyPayload(data);
}
catch (e) { }
}
else {
const threads = weave(data);
if (threads)
payload = yield parseAndVerifyPayload(threads);
}
if (payload) {
const dat = payload;
if (value && dat && dat.message) {
dat.message.amount = value; // ADD IN TRUE VALUE
}
onReceive(dat);
}
});
}
exports.parseKeysendInvoice = parseKeysendInvoice;
const chunks = {};
function weave(p) {
const pa = p.split('_');
if (pa.length < 4)
return;
const ts = pa[0];
const i = pa[1];
const n = pa[2];
const m = pa.filter((u, i) => i > 2).join('_');
chunks[ts] = chunks[ts] ? [...chunks[ts], { i, n, m }] : [{ i, n, m }];
if (chunks[ts].length === parseInt(n)) {
// got em all!
const all = chunks[ts];
let payload = '';
all.slice().sort((a, b) => a.i - b.i).forEach(obj => {
payload += obj.m;
});
delete chunks[ts];
return payload;
}
}
//# sourceMappingURL=receive.js.map

1
dist/api/network/receive.js.map

File diff suppressed because one or more lines are too long

155
dist/api/network/send.js

@ -0,0 +1,155 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const models_1 = require("../models");
const LND = require("../utils/lightning");
const msg_1 = require("../utils/msg");
const path = require("path");
const tribes = require("../utils/tribes");
const constants = require(path.join(__dirname, '../../config/constants.json'));
function sendMessage(params) {
return __awaiter(this, void 0, void 0, function* () {
const { type, chat, message, sender, amount, success, failure, skipPubKey } = params;
const m = newmsg(type, chat, sender, message);
let msg = m;
// console.log(type,message)
if (!(sender && sender.publicKey)) {
console.log("NO SENDER?????");
return;
}
let contactIds = (typeof chat.contactIds === 'string' ? JSON.parse(chat.contactIds) : chat.contactIds) || [];
if (contactIds.length === 1) {
if (contactIds[0] === 1) {
if (success)
success(true);
return; // if no contacts thats fine (like create public tribe)
}
}
let networkType = undefined;
const isTribe = chat.type === constants.chat_types.tribe;
let isTribeOwner = false;
const chatUUID = chat.uuid;
if (isTribe) {
if (type === constants.message_types.confirmation) {
return; // dont send confs for tribe
}
console.log("is tribe!");
const tribeOwnerPubKey = chat.ownerPubkey;
if (sender.publicKey === tribeOwnerPubKey) {
console.log('im owner! mqtt!');
isTribeOwner = true;
networkType = 'mqtt'; // broadcast to all
// decrypt message.content and message.mediaKey w groupKey
msg = yield msg_1.decryptMessage(msg, chat);
}
else {
// if tribe, send to owner only
const tribeOwner = yield models_1.models.Contact.findOne({ where: { publicKey: tribeOwnerPubKey } });
contactIds = tribeOwner ? [tribeOwner.id] : [];
}
}
let yes = null;
let no = null;
console.log('all contactIds', contactIds);
yield asyncForEach(contactIds, (contactId) => __awaiter(this, void 0, void 0, function* () {
if (contactId == 1) { // dont send to self
return;
}
const contact = yield models_1.models.Contact.findOne({ where: { id: contactId } });
const destkey = contact.publicKey;
if (destkey === skipPubKey) {
return; // skip (for tribe owner broadcasting, not back to the sender)
}
console.log('-> sending to ', contact.id, destkey);
const m = yield msg_1.personalizeMessage(msg, contact, isTribeOwner);
const opts = {
dest: destkey,
data: m,
amt: Math.max((amount || 0), 3)
};
try {
const mqttTopic = networkType === 'mqtt' ? `${destkey}/${chatUUID}` : '';
const r = yield signAndSend(opts, sender.publicKey, mqttTopic);
yes = r;
}
catch (e) {
console.log("KEYSEND ERROR", e);
no = e;
}
yield sleep(2);
}));
if (yes) {
if (success)
success(yes);
}
else {
if (failure)
failure(no);
}
});
}
exports.sendMessage = sendMessage;
function signAndSend(opts, pubkey, mqttTopic) {
// console.log('sign and send!!!!',opts.data)
return new Promise(function (resolve, reject) {
return __awaiter(this, void 0, void 0, function* () {
if (!opts.data || typeof opts.data !== 'object') {
return reject('object plz');
}
let data = JSON.stringify(opts.data);
const sig = yield LND.signAscii(data);
data = data + sig;
// console.log("ACTUALLY SEND", mqttTopic)
try {
if (mqttTopic) {
yield tribes.publish(mqttTopic, data);
}
else {
yield LND.keysendMessage(Object.assign(Object.assign({}, opts), { data }));
}
resolve(true);
}
catch (e) {
reject(e);
}
});
});
}
exports.signAndSend = signAndSend;
function newmsg(type, chat, sender, message) {
const includeGroupKey = type === constants.message_types.group_create || type === constants.message_types.group_invite;
const includeAlias = sender && sender.alias && chat.type === constants.chat_types.tribe;
return {
type: type,
chat: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ uuid: chat.uuid }, chat.name && { name: chat.name }), (chat.type || chat.type === 0) && { type: chat.type }), chat.members && { members: chat.members }), (includeGroupKey && chat.groupKey) && { groupKey: chat.groupKey }), (includeGroupKey && chat.host) && { host: chat.host }),
message: message,
sender: Object.assign(Object.assign({}, includeAlias && { alias: sender.alias }), { pub_key: sender.publicKey })
};
}
function asyncForEach(array, callback) {
return __awaiter(this, void 0, void 0, function* () {
for (let index = 0; index < array.length; index++) {
yield callback(array[index], index, array);
}
});
}
function sleep(ms) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(resolve => setTimeout(resolve, ms));
});
}
// function urlBase64FromHex(ascii){
// return Buffer.from(ascii,'hex').toString('base64').replace(/\//g, '_').replace(/\+/g, '-')
// }
// function urlBase64FromBytes(buf){
// return Buffer.from(buf).toString('base64').replace(/\//g, '_').replace(/\+/g, '-')
// }
//# sourceMappingURL=send.js.map

1
dist/api/network/send.js.map

File diff suppressed because one or more lines are too long

14
dist/api/utils/json.js

@ -3,15 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
const case_1 = require("../utils/case");
const cronUtils = require("./cron");
function chatToJson(c) {
const chat = c.dataValues || c;
if (!c)
return {};
const ch = c.dataValues || c;
const chat = JSON.parse(JSON.stringify(ch));
let contactIds = chat.contactIds || null;
if (chat.contactIds && typeof chat.contactIds === 'string') {
contactIds = JSON.parse(chat.contactIds);
}
delete chat.groupPrivateKey;
return case_1.toSnake(Object.assign(Object.assign({}, chat), { contactIds }));
}
exports.chatToJson = chatToJson;
function messageToJson(msg, chat, contact) {
if (!msg)
return {};
const message = msg.dataValues || msg;
let statusMap = message.statusMap || null;
if (message.statusMap && typeof message.statusMap === 'string') {
@ -20,7 +26,11 @@ function messageToJson(msg, chat, contact) {
return case_1.toSnake(Object.assign(Object.assign({}, message), { statusMap, chat: chat ? chatToJson(chat) : null, contact: contact ? contactToJson(contact) : null }));
}
exports.messageToJson = messageToJson;
const contactToJson = (contact) => case_1.toSnake(contact.dataValues || contact);
function contactToJson(contact) {
if (!contact)
return {};
return case_1.toSnake(contact.dataValues || contact);
}
exports.contactToJson = contactToJson;
const inviteToJson = (invite) => case_1.toSnake(invite.dataValues || invite);
exports.inviteToJson = inviteToJson;

2
dist/api/utils/json.js.map

@ -1 +1 @@
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../../api/utils/json.ts"],"names":[],"mappings":";;AAAA,wCAA6C;AAC7C,oCAAmC;AAEnC,SAAS,UAAU,CAAC,CAAC;IACnB,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,IAAE,CAAC,CAAA;IAC5B,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAA;IACxC,IAAG,IAAI,CAAC,UAAU,IAAI,OAAO,IAAI,CAAC,UAAU,KAAG,QAAQ,EAAC;QACtD,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;KACzC;IACD,OAAO,cAAO,iCACT,IAAI,KACP,UAAU,IACV,CAAA;AACJ,CAAC;AAsCC,gCAAU;AApCZ,SAAS,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,OAAQ;IACxC,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,IAAE,GAAG,CAAA;IACnC,IAAI,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAA;IACzC,IAAG,OAAO,CAAC,SAAS,IAAI,OAAO,OAAO,CAAC,SAAS,KAAG,QAAQ,EAAC;QAC1D,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;KAC1C;IACD,OAAO,cAAO,iCACT,OAAO,KACV,SAAS,EACT,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EACpC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,IAChD,CAAA;AACJ,CAAC;AAoBC,sCAAa;AAlBf,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,cAAO,CAAC,OAAO,CAAC,UAAU,IAAE,OAAO,CAAC,CAAA;AAmBrE,sCAAa;AAjBf,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,cAAO,CAAC,MAAM,CAAC,UAAU,IAAE,MAAM,CAAC,CAAA;AAkBjE,oCAAY;AAhBd,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,cAAO,CAAC,IAAI,CAAC,CAAA;AAiB3C,sCAAa;AAff,SAAS,kBAAkB,CAAC,YAAY,EAAE,IAAI;IAC5C,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,IAAI,YAAY,CAAA;IACnD,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACpD,OAAO,cAAO,iCACT,GAAG,KACN,QAAQ;QACR,IAAI,EACJ,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IACpC,CAAA;AACJ,CAAC;AAQC,gDAAkB"}
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../../api/utils/json.ts"],"names":[],"mappings":";;AAAA,wCAA6C;AAC7C,oCAAmC;AAEnC,SAAS,UAAU,CAAC,CAAC;IACnB,IAAG,CAAC,CAAC;QAAE,OAAO,EAAE,CAAA;IAChB,MAAM,EAAE,GAAG,CAAC,CAAC,UAAU,IAAE,CAAC,CAAA;IAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAA;IAC3C,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAA;IACxC,IAAG,IAAI,CAAC,UAAU,IAAI,OAAO,IAAI,CAAC,UAAU,KAAG,QAAQ,EAAC;QACtD,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;KACzC;IACD,OAAO,IAAI,CAAC,eAAe,CAAA;IAC3B,OAAO,cAAO,iCACT,IAAI,KACP,UAAU,IACV,CAAA;AACJ,CAAC;AA0CC,gCAAU;AAxCZ,SAAS,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,OAAQ;IACxC,IAAG,CAAC,GAAG;QAAE,OAAO,EAAE,CAAA;IAClB,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,IAAE,GAAG,CAAA;IACnC,IAAI,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAA;IACzC,IAAG,OAAO,CAAC,SAAS,IAAI,OAAO,OAAO,CAAC,SAAS,KAAG,QAAQ,EAAC;QAC1D,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;KAC1C;IACD,OAAO,cAAO,iCACT,OAAO,KACV,SAAS,EACT,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EACpC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,IAChD,CAAA;AACJ,CAAC;AAuBC,sCAAa;AArBf,SAAS,aAAa,CAAC,OAAO;IAC5B,IAAG,CAAC,OAAO;QAAE,OAAO,EAAE,CAAA;IACtB,OAAO,cAAO,CAAC,OAAO,CAAC,UAAU,IAAE,OAAO,CAAC,CAAA;AAC7C,CAAC;AAmBC,sCAAa;AAjBf,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,cAAO,CAAC,MAAM,CAAC,UAAU,IAAE,MAAM,CAAC,CAAA;AAkBjE,oCAAY;AAhBd,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,cAAO,CAAC,IAAI,CAAC,CAAA;AAiB3C,sCAAa;AAff,SAAS,kBAAkB,CAAC,YAAY,EAAE,IAAI;IAC5C,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,IAAI,YAAY,CAAA;IACnD,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACpD,OAAO,cAAO,iCACT,GAAG,KACN,QAAQ;QACR,IAAI,EACJ,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IACpC,CAAA;AACJ,CAAC;AAQC,gDAAkB"}

4
dist/api/utils/ldat.js

@ -154,4 +154,8 @@ function urlBase64FromAscii(ascii) {
return Buffer.from(ascii, 'ascii').toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
}
exports.urlBase64FromAscii = urlBase64FromAscii;
function urlBase64FromHex(ascii) {
return Buffer.from(ascii, 'hex').toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
}
exports.urlBase64FromHex = urlBase64FromHex;
//# sourceMappingURL=ldat.js.map

2
dist/api/utils/ldat.js.map

File diff suppressed because one or more lines are too long

44
dist/api/utils/lightning.js

@ -154,9 +154,6 @@ function keysendMessage(opts) {
if (!opts.data || typeof opts.data !== 'string') {
return reject('string plz');
}
// SIGN HERE and append sig
const sig = yield signAscii(opts.data);
opts.data = opts.data + sig;
if (opts.data.length < MAX_MSG_LENGTH) {
try {
const res = yield keysend(opts);
@ -173,6 +170,7 @@ function keysendMessage(opts) {
let fail = false;
let res = null;
const ts = new Date().valueOf();
// WEAVE MESSAGE If TOO LARGE
yield asyncForEach(Array.from(Array(n)), (u, i) => __awaiter(this, void 0, void 0, function* () {
const spliti = Math.ceil(opts.data.length / n);
const m = opts.data.substr(i * spliti, spliti);
@ -216,18 +214,6 @@ function signAscii(ascii) {
});
}
exports.signAscii = signAscii;
function verifyAscii(ascii, sig) {
return __awaiter(this, void 0, void 0, function* () {
try {
const r = yield verifyMessage(ascii_to_hexa(ascii), sig);
return r;
}
catch (e) {
throw e;
}
});
}
exports.verifyAscii = verifyAscii;
function listInvoices() {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
const lightning = yield loadLightning();
@ -378,6 +364,18 @@ const signBuffer = (msg) => {
}));
};
exports.signBuffer = signBuffer;
function verifyBytes(msg, sig) {
return __awaiter(this, void 0, void 0, function* () {
try {
const r = yield verifyMessage(msg.toString('hex'), sig);
return r;
}
catch (e) {
throw e;
}
});
}
exports.verifyBytes = verifyBytes;
function verifyMessage(msg, sig) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
let lightning = yield loadLightning();
@ -401,7 +399,19 @@ function verifyMessage(msg, sig) {
}));
}
exports.verifyMessage = verifyMessage;
function checkConnection() {
function verifyAscii(ascii, sig) {
return __awaiter(this, void 0, void 0, function* () {
try {
const r = yield verifyMessage(ascii_to_hexa(ascii), sig);
return r;
}
catch (e) {
throw e;
}
});
}
exports.verifyAscii = verifyAscii;
function getInfo() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
const lightning = loadLightning();
@ -416,7 +426,7 @@ function checkConnection() {
});
});
}
exports.checkConnection = checkConnection;
exports.getInfo = getInfo;
function ascii_to_hexa(str) {
var arr1 = [];
for (var n = 0, l = str.length; n < l; n++) {

2
dist/api/utils/lightning.js.map

File diff suppressed because one or more lines are too long

9
dist/api/utils/meme.js

@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
let mediaToken = '';
exports.mediaToken = mediaToken;
function setMediaToken(mt) {
exports.mediaToken = mediaToken = mt;
}
exports.setMediaToken = setMediaToken;
//# sourceMappingURL=meme.js.map

1
dist/api/utils/meme.js.map

@ -0,0 +1 @@
{"version":3,"file":"meme.js","sourceRoot":"","sources":["../../../api/utils/meme.ts"],"names":[],"mappings":";;AACA,IAAI,UAAU,GAAG,EAAE,CAAA;AAMX,gCAAU;AAJlB,SAAS,aAAa,CAAC,EAAE;IACrB,qBAAA,UAAU,GAAG,EAAE,CAAA;AACnB,CAAC;AAEmB,sCAAa"}

106
dist/api/utils/msg.js

@ -10,12 +10,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
};
Object.defineProperty(exports, "__esModule", { value: true });
const ldat_1 = require("./ldat");
function addInRemoteText(full, contactId) {
const path = require("path");
const rsa = require("../crypto/rsa");
const constants = require(path.join(__dirname, '../../config/constants.json'));
function addInRemoteText(full, contactId, isTribe) {
const m = full && full.message;
if (!(m && m.content))
return full;
if (!(typeof m.content === 'object'))
return full;
if (isTribe) {
// if just one, send it (for tribe remote_text_map... is there a better way?)
if (m.content['chat']) {
return fillmsg(full, { content: m.content['chat'] });
}
}
return fillmsg(full, { content: m.content[contactId + ''] });
}
function removeRecipientFromChatMembers(full, destkey) {
@ -29,7 +38,41 @@ function removeRecipientFromChatMembers(full, destkey) {
delete members[destkey];
return fillchatmsg(full, { members });
}
function addInMediaKey(full, contactId) {
function removeAllNonAdminMembersIfTribe(full, destkey) {
return full;
// const c = full && full.chat
// if (!(c && c.members)) return full
// if (!(typeof c.members==='object')) return full
// const members = {...c.members}
// if(members[destkey]) delete members[destkey]
// return fillchatmsg(full, {members})
}
// THIS IS ONLY FOR TRIBE OWNER
// by this time the content and mediaKey are already in message as string
function encryptTribeBroadcast(full, contact, isTribeOwner) {
return __awaiter(this, void 0, void 0, function* () {
if (!isTribeOwner)
return full;
const chat = full && full.chat;
const message = full && full.message;
if (!message || !(chat && chat.type && chat.uuid))
return full;
const obj = {};
if (isTribeOwner) { // has been previously decrypted
if (message.content) {
const encContent = yield rsa.encrypt(contact.contactKey, message.content);
obj.content = encContent;
}
if (message.mediaKey) {
const encMediaKey = yield rsa.encrypt(contact.contactKey, message.mediaKey);
obj.mediaKey = encMediaKey;
}
}
return fillmsg(full, obj);
});
}
exports.encryptTribeBroadcast = encryptTribeBroadcast;
function addInMediaKey(full, contactId, isTribe) {
const m = full && full.message;
if (!(m && m.mediaKey))
return full;
@ -37,6 +80,12 @@ function addInMediaKey(full, contactId) {
return full;
if (!(typeof m.mediaKey === 'object'))
return full;
if (isTribe) {
if (m.mediaKey['chat']) { // "chat" is the key for tribes
const tribeMediaKey = m.mediaTerms.skipSigning ? '' : m.mediaKey['chat'];
return fillmsg(full, { mediaKey: tribeMediaKey });
}
}
const mediaKey = m.mediaTerms.skipSigning ? '' : m.mediaKey[contactId + ''];
return fillmsg(full, { mediaKey });
}
@ -62,14 +111,55 @@ function finishTermsAndReceipt(full, destkey) {
return fullmsg;
});
}
function personalizeMessage(m, contactId, destkey) {
// this is only for tribes
// DECRYPT EITHER STRING OR FIRST VAL IN OBJ
function decryptMessage(full, chat) {
return __awaiter(this, void 0, void 0, function* () {
if (!chat.groupPrivateKey)
return full;
const m = full && full.message;
if (!m)
return full;
const obj = {};
if (m.content) {
let content = m.content;
if (typeof m.content === 'object') {
if (m.content['chat']) {
content = m.content['chat'];
}
}
const decContent = rsa.decrypt(chat.groupPrivateKey, content);
obj.content = decContent;
}
if (m.mediaKey) {
let mediaKey = m.mediaKey;
if (typeof m.mediaKey === 'object') {
if (m.mediaKey['chat']) {
mediaKey = m.mediaKey['chat'];
}
}
const decMediaKey = rsa.decrypt(chat.groupPrivateKey, mediaKey);
obj.mediaKey = decMediaKey;
}
// console.log("OBJ FILLED",fillmsg(full, obj))
return fillmsg(full, obj);
});
}
exports.decryptMessage = decryptMessage;
function personalizeMessage(m, contact, isTribeOwner) {
return __awaiter(this, void 0, void 0, function* () {
const contactId = contact.id;
const destkey = contact.publicKey;
const cloned = JSON.parse(JSON.stringify(m));
const msg = addInRemoteText(cloned, contactId);
const cleanMsg = removeRecipientFromChatMembers(msg, destkey);
const msgWithMediaKey = addInMediaKey(cleanMsg, contactId);
const finalMsg = yield finishTermsAndReceipt(msgWithMediaKey, destkey);
return finalMsg;
const chat = cloned && cloned.chat;
const isTribe = chat.type && chat.type === constants.chat_types.tribe;
const msgWithRemoteTxt = addInRemoteText(cloned, contactId, isTribe);
const cleanMsg = removeRecipientFromChatMembers(msgWithRemoteTxt, destkey);
const cleanerMsg = removeAllNonAdminMembersIfTribe(cleanMsg, destkey);
const msgWithMediaKey = addInMediaKey(cleanerMsg, contactId, isTribe);
const msgWithMediaToken = yield finishTermsAndReceipt(msgWithMediaKey, destkey);
const encMsg = yield encryptTribeBroadcast(msgWithMediaToken, contact, isTribeOwner);
return encMsg;
});
}
exports.personalizeMessage = personalizeMessage;

2
dist/api/utils/msg.js.map

File diff suppressed because one or more lines are too long

5
dist/api/utils/rncryptor/index.js

@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const RNCryptor = require("./rncryptor");
exports.default = RNCryptor;
//# sourceMappingURL=index.js.map

1
dist/api/utils/rncryptor/index.js.map

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../api/utils/rncryptor/index.js"],"names":[],"mappings":";;AAEA,yCAAwC;AAExC,kBAAe,SAAS,CAAA"}

78
dist/api/utils/rncryptor/rncryptor.js

@ -0,0 +1,78 @@
var sjcl = require('./sjcl');
var RNCryptor = {};
/*
Takes password string and salt WordArray
Returns key bitArray
*/
RNCryptor.KeyForPassword = function (password, salt) {
var hmacSHA1 = function (key) {
var hasher = new sjcl.misc.hmac(key, sjcl.hash.sha1);
this.encrypt = function () {
return hasher.encrypt.apply(hasher, arguments);
};
};
return sjcl.misc.pbkdf2(password, salt, 10000, 32 * 8, hmacSHA1);
};
/*
Takes password string and plaintext base64
options:
iv
encryption_salt
html_salt
Returns ciphertext base64
*/
RNCryptor.Encrypt = function (password, plaintextBase64, options) {
var plaintext = sjcl.codec.base64.toBits(plaintextBase64);
options = options || {};
var encryption_salt = options["encryption_salt"] || sjcl.random.randomWords(8 / 4); // FIXME: Need to seed PRNG
var encryption_key = RNCryptor.KeyForPassword(password, encryption_salt);
var hmac_salt = options["hmac_salt"] || sjcl.random.randomWords(8 / 4);
var hmac_key = RNCryptor.KeyForPassword(password, hmac_salt);
var iv = options["iv"] || sjcl.random.randomWords(16 / 4);
var version = sjcl.codec.hex.toBits("03");
var options = sjcl.codec.hex.toBits("01");
var message = sjcl.bitArray.concat(version, options);
message = sjcl.bitArray.concat(message, encryption_salt);
message = sjcl.bitArray.concat(message, hmac_salt);
message = sjcl.bitArray.concat(message, iv);
var aes = new sjcl.cipher.aes(encryption_key);
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
var encrypted = sjcl.mode.cbc.encrypt(aes, plaintext, iv);
message = sjcl.bitArray.concat(message, encrypted);
var hmac = new sjcl.misc.hmac(hmac_key).encrypt(message);
message = sjcl.bitArray.concat(message, hmac);
return sjcl.codec.base64.fromBits(message);
};
/*
Takes password string and message (ciphertext) base64
options:
iv
encryption_salt
html_salt
Returns plaintext base64
*/
RNCryptor.Decrypt = function (password, messageBase64, options) {
var message = sjcl.codec.base64.toBits(messageBase64);
options = options || {};
var version = sjcl.bitArray.extract(message, 0 * 8, 8);
var options = sjcl.bitArray.extract(message, 1 * 8, 8);
var encryption_salt = sjcl.bitArray.bitSlice(message, 2 * 8, 10 * 8);
var encryption_key = RNCryptor.KeyForPassword(password, encryption_salt);
var hmac_salt = sjcl.bitArray.bitSlice(message, 10 * 8, 18 * 8);
var hmac_key = RNCryptor.KeyForPassword(password, hmac_salt);
var iv = sjcl.bitArray.bitSlice(message, 18 * 8, 34 * 8);
var ciphertext_end = sjcl.bitArray.bitLength(message) - (32 * 8);
var ciphertext = sjcl.bitArray.bitSlice(message, 34 * 8, ciphertext_end);
var hmac = sjcl.bitArray.bitSlice(message, ciphertext_end);
var expected_hmac = new sjcl.misc.hmac(hmac_key).encrypt(sjcl.bitArray.bitSlice(message, 0, ciphertext_end));
// .equal is of consistent time
if (!sjcl.bitArray.equal(hmac, expected_hmac)) {
throw new sjcl.exception.corrupt("HMAC mismatch or bad password.");
}
var aes = new sjcl.cipher.aes(encryption_key);
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
var decrypted = sjcl.mode.cbc.decrypt(aes, ciphertext, iv);
return sjcl.codec.base64.fromBits(decrypted);
};
module.exports = RNCryptor;
//# sourceMappingURL=rncryptor.js.map

1
dist/api/utils/rncryptor/rncryptor.js.map

@ -0,0 +1 @@
{"version":3,"file":"rncryptor.js","sourceRoot":"","sources":["../../../../api/utils/rncryptor/rncryptor.js"],"names":[],"mappings":"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"}

503
dist/api/utils/rncryptor/sjcl.js

@ -0,0 +1,503 @@
"use strict";
var sjcl = { cipher: {}, hash: {}, keyexchange: {}, mode: {}, misc: {}, codec: {}, exception: { corrupt: function (a) { this.toString = function () { return "CORRUPT: " + this.message; }; this.message = a; }, invalid: function (a) { this.toString = function () { return "INVALID: " + this.message; }; this.message = a; }, bug: function (a) { this.toString = function () { return "BUG: " + this.message; }; this.message = a; }, notReady: function (a) { this.toString = function () { return "NOT READY: " + this.message; }; this.message = a; } } };
sjcl.cipher.aes = function (a) {
this.u[0][0][0] || this.N();
var b, c, d, e, f = this.u[0][4], g = this.u[1];
b = a.length;
var h = 1;
if (4 !== b && 6 !== b && 8 !== b)
throw new sjcl.exception.invalid("invalid aes key size");
this.b = [d = a.slice(0), e = []];
for (a = b; a < 4 * b + 28; a++) {
c = d[a - 1];
if (0 === a % b || 8 === b && 4 === a % b)
c = f[c >>> 24] << 24 ^ f[c >> 16 & 255] << 16 ^ f[c >> 8 & 255] << 8 ^ f[c & 255], 0 === a % b && (c = c << 8 ^ c >>> 24 ^ h << 24, h = h << 1 ^ 283 * (h >> 7));
d[a] = d[a - b] ^ c;
}
for (b = 0; a; b++, a--)
c = d[b & 3 ? a : a - 4], e[b] = 4 >= a || 4 > b ? c : g[0][f[c >>> 24]] ^ g[1][f[c >> 16 & 255]] ^ g[2][f[c >> 8 & 255]] ^ g[3][f[c &
255]];
};
sjcl.cipher.aes.prototype = { encrypt: function (a) { return r(this, a, 0); }, decrypt: function (a) { return r(this, a, 1); }, u: [[[], [], [], [], []], [[], [], [], [], []]], N: function () {
var a = this.u[0], b = this.u[1], c = a[4], d = b[4], e, f, g, h = [], k = [], n, l, m, p;
for (e = 0; 0x100 > e; e++)
k[(h[e] = e << 1 ^ 283 * (e >> 7)) ^ e] = e;
for (f = g = 0; !c[f]; f ^= n || 1, g = k[g] || 1)
for (m = g ^ g << 1 ^ g << 2 ^ g << 3 ^ g << 4, m = m >> 8 ^ m & 255 ^ 99, c[f] = m, d[m] = f, l = h[e = h[n = h[f]]], p = 0x1010101 * l ^ 0x10001 * e ^ 0x101 * n ^ 0x1010100 * f, l = 0x101 * h[m] ^ 0x1010100 * m, e = 0; 4 > e; e++)
a[e][f] = l = l << 24 ^ l >>> 8, b[e][m] = p = p << 24 ^ p >>> 8;
for (e =
0; 5 > e; e++)
a[e] = a[e].slice(0), b[e] = b[e].slice(0);
} };
function r(a, b, c) {
if (4 !== b.length)
throw new sjcl.exception.invalid("invalid aes block size");
var d = a.b[c], e = b[0] ^ d[0], f = b[c ? 3 : 1] ^ d[1], g = b[2] ^ d[2];
b = b[c ? 1 : 3] ^ d[3];
var h, k, n, l = d.length / 4 - 2, m, p = 4, q = [0, 0, 0, 0];
h = a.u[c];
a = h[0];
var u = h[1], v = h[2], w = h[3], x = h[4];
for (m = 0; m < l; m++)
h = a[e >>> 24] ^ u[f >> 16 & 255] ^ v[g >> 8 & 255] ^ w[b & 255] ^ d[p], k = a[f >>> 24] ^ u[g >> 16 & 255] ^ v[b >> 8 & 255] ^ w[e & 255] ^ d[p + 1], n = a[g >>> 24] ^ u[b >> 16 & 255] ^ v[e >> 8 & 255] ^ w[f & 255] ^ d[p + 2], b = a[b >>> 24] ^ u[e >> 16 & 255] ^ v[f >> 8 & 255] ^ w[g & 255] ^ d[p + 3], p += 4, e = h, f = k, g = n;
for (m =
0; 4 > m; m++)
q[c ? 3 & -m : m] = x[e >>> 24] << 24 ^ x[f >> 16 & 255] << 16 ^ x[g >> 8 & 255] << 8 ^ x[b & 255] ^ d[p++], h = e, e = f, f = g, g = b, b = h;
return q;
}
sjcl.bitArray = { bitSlice: function (a, b, c) { a = sjcl.bitArray.Y(a.slice(b / 32), 32 - (b & 31)).slice(1); return void 0 === c ? a : sjcl.bitArray.clamp(a, c - b); }, extract: function (a, b, c) { var d = Math.floor(-b - c & 31); return ((b + c - 1 ^ b) & -32 ? a[b / 32 | 0] << 32 - d ^ a[b / 32 + 1 | 0] >>> d : a[b / 32 | 0] >>> d) & (1 << c) - 1; }, concat: function (a, b) { if (0 === a.length || 0 === b.length)
return a.concat(b); var c = a[a.length - 1], d = sjcl.bitArray.getPartial(c); return 32 === d ? a.concat(b) : sjcl.bitArray.Y(b, d, c | 0, a.slice(0, a.length - 1)); }, bitLength: function (a) {
var b = a.length;
return 0 ===
b ? 0 : 32 * (b - 1) + sjcl.bitArray.getPartial(a[b - 1]);
}, clamp: function (a, b) { if (32 * a.length < b)
return a; a = a.slice(0, Math.ceil(b / 32)); var c = a.length; b = b & 31; 0 < c && b && (a[c - 1] = sjcl.bitArray.partial(b, a[c - 1] & 2147483648 >> b - 1, 1)); return a; }, partial: function (a, b, c) { return 32 === a ? b : (c ? b | 0 : b << 32 - a) + 0x10000000000 * a; }, getPartial: function (a) { return Math.round(a / 0x10000000000) || 32; }, equal: function (a, b) {
if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b))
return !1;
var c = 0, d;
for (d = 0; d < a.length; d++)
c |= a[d] ^ b[d];
return 0 ===
c;
}, Y: function (a, b, c, d) { var e; e = 0; for (void 0 === d && (d = []); 32 <= b; b -= 32)
d.push(c), c = 0; if (0 === b)
return d.concat(a); for (e = 0; e < a.length; e++)
d.push(c | a[e] >>> b), c = a[e] << 32 - b; e = a.length ? a[a.length - 1] : 0; a = sjcl.bitArray.getPartial(e); d.push(sjcl.bitArray.partial(b + a & 31, 32 < b + a ? c : d.pop(), 1)); return d; }, B: function (a, b) { return [a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3]]; }, byteswapM: function (a) { var b, c; for (b = 0; b < a.length; ++b)
c = a[b], a[b] = c >>> 24 | c >>> 8 & 0xff00 | (c & 0xff00) << 8 | c << 24; return a; } };
sjcl.codec.utf8String = { fromBits: function (a) { var b = "", c = sjcl.bitArray.bitLength(a), d, e; for (d = 0; d < c / 8; d++)
0 === (d & 3) && (e = a[d / 4]), b += String.fromCharCode(e >>> 8 >>> 8 >>> 8), e <<= 8; return decodeURIComponent(escape(b)); }, toBits: function (a) { a = unescape(encodeURIComponent(a)); var b = [], c, d = 0; for (c = 0; c < a.length; c++)
d = d << 8 | a.charCodeAt(c), 3 === (c & 3) && (b.push(d), d = 0); c & 3 && b.push(sjcl.bitArray.partial(8 * (c & 3), d)); return b; } };
sjcl.codec.hex = { fromBits: function (a) { var b = "", c; for (c = 0; c < a.length; c++)
b += ((a[c] | 0) + 0xf00000000000).toString(16).substr(4); return b.substr(0, sjcl.bitArray.bitLength(a) / 4); }, toBits: function (a) { var b, c = [], d; a = a.replace(/\s|0x/g, ""); d = a.length; a = a + "00000000"; for (b = 0; b < a.length; b += 8)
c.push(parseInt(a.substr(b, 8), 16) ^ 0); return sjcl.bitArray.clamp(c, 4 * d); } };
sjcl.codec.base64 = { S: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", fromBits: function (a, b, c) { var d = "", e = 0, f = sjcl.codec.base64.S, g = 0, h = sjcl.bitArray.bitLength(a); c && (f = f.substr(0, 62) + "-_"); for (c = 0; 6 * d.length < h;)
d += f.charAt((g ^ a[c] >>> e) >>> 26), 6 > e ? (g = a[c] << 6 - e, e += 26, c++) : (g <<= 6, e -= 6); for (; d.length & 3 && !b;)
d += "="; return d; }, toBits: function (a, b) {
a = a.replace(/\s|=/g, "");
var c = [], d, e = 0, f = sjcl.codec.base64.S, g = 0, h;
b && (f = f.substr(0, 62) + "-_");
for (d = 0; d < a.length; d++) {
h = f.indexOf(a.charAt(d));
if (0 > h)
throw new sjcl.exception.invalid("this isn't base64!");
26 < e ? (e -= 26, c.push(g ^ h >>> e), g = h << 32 - e) : (e += 6, g ^= h << 32 - e);
}
e & 56 && c.push(sjcl.bitArray.partial(e & 56, g, 1));
return c;
} };
sjcl.codec.base64url = { fromBits: function (a) { return sjcl.codec.base64.fromBits(a, 1, 1); }, toBits: function (a) { return sjcl.codec.base64.toBits(a, 1); } };
sjcl.hash.sha256 = function (a) { this.b[0] || this.N(); a ? (this.g = a.g.slice(0), this.f = a.f.slice(0), this.c = a.c) : this.reset(); };
sjcl.hash.sha256.hash = function (a) { return (new sjcl.hash.sha256).update(a).finalize(); };
sjcl.hash.sha256.prototype = { blockSize: 512, reset: function () { this.g = this.D.slice(0); this.f = []; this.c = 0; return this; }, update: function (a) {
"string" === typeof a && (a = sjcl.codec.utf8String.toBits(a));
var b, c = this.f = sjcl.bitArray.concat(this.f, a);
b = this.c;
a = this.c = b + sjcl.bitArray.bitLength(a);
if (0x1fffffffffffff < a)
throw new sjcl.exception.invalid("Cannot hash more than 2^53 - 1 bits");
if ("undefined" !== typeof Uint32Array) {
var d = new Uint32Array(c), e = 0;
for (b = 512 + b - (512 + b & 0x1ff); b <= a; b += 512)
this.l(d.subarray(16 * e, 16 * (e + 1))), e += 1;
c.splice(0, 16 * e);
}
else
for (b = 512 + b - (512 + b & 0x1ff); b <= a; b += 512)
this.l(c.splice(0, 16));
return this;
}, finalize: function () { var a, b = this.f, c = this.g, b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1, 1)]); for (a = b.length + 2; a & 15; a++)
b.push(0); b.push(Math.floor(this.c / 0x100000000)); for (b.push(this.c | 0); b.length;)
this.l(b.splice(0, 16)); this.reset(); return c; }, D: [], b: [], N: function () {
function a(a) { return 0x100000000 * (a - Math.floor(a)) | 0; }
for (var b = 0, c = 2, d, e; 64 > b; c++) {
e = !0;
for (d = 2; d * d <= c; d++)
if (0 === c % d) {
e =
!1;
break;
}
e && (8 > b && (this.D[b] = a(Math.pow(c, .5))), this.b[b] = a(Math.pow(c, 1 / 3)), b++);
}
}, l: function (a) {
var b, c, d, e = this.g, f = this.b, g = e[0], h = e[1], k = e[2], n = e[3], l = e[4], m = e[5], p = e[6], q = e[7];
for (b = 0; 64 > b; b++)
16 > b ? c = a[b] : (c = a[b + 1 & 15], d = a[b + 14 & 15], c = a[b & 15] = (c >>> 7 ^ c >>> 18 ^ c >>> 3 ^ c << 25 ^ c << 14) + (d >>> 17 ^ d >>> 19 ^ d >>> 10 ^ d << 15 ^ d << 13) + a[b & 15] + a[b + 9 & 15] | 0), c = c + q + (l >>> 6 ^ l >>> 11 ^ l >>> 25 ^ l << 26 ^ l << 21 ^ l << 7) + (p ^ l & (m ^ p)) + f[b], q = p, p = m, m = l, l = n + c | 0, n = k, k = h, h = g, g = c + (h & k ^ n & (h ^ k)) + (h >>> 2 ^ h >>> 13 ^ h >>> 22 ^ h << 30 ^ h << 19 ^ h << 10) | 0;
e[0] = e[0] + g |
0;
e[1] = e[1] + h | 0;
e[2] = e[2] + k | 0;
e[3] = e[3] + n | 0;
e[4] = e[4] + l | 0;
e[5] = e[5] + m | 0;
e[6] = e[6] + p | 0;
e[7] = e[7] + q | 0;
} };
sjcl.hash.sha1 = function (a) { a ? (this.g = a.g.slice(0), this.f = a.f.slice(0), this.c = a.c) : this.reset(); };
sjcl.hash.sha1.hash = function (a) { return (new sjcl.hash.sha1).update(a).finalize(); };
sjcl.hash.sha1.prototype = { blockSize: 512, reset: function () { this.g = this.D.slice(0); this.f = []; this.c = 0; return this; }, update: function (a) {
"string" === typeof a && (a = sjcl.codec.utf8String.toBits(a));
var b, c = this.f = sjcl.bitArray.concat(this.f, a);
b = this.c;
a = this.c = b + sjcl.bitArray.bitLength(a);
if (0x1fffffffffffff < a)
throw new sjcl.exception.invalid("Cannot hash more than 2^53 - 1 bits");
if ("undefined" !== typeof Uint32Array) {
var d = new Uint32Array(c), e = 0;
for (b = this.blockSize + b - (this.blockSize + b & this.blockSize - 1); b <=
a; b += this.blockSize)
this.l(d.subarray(16 * e, 16 * (e + 1))), e += 1;
c.splice(0, 16 * e);
}
else
for (b = this.blockSize + b - (this.blockSize + b & this.blockSize - 1); b <= a; b += this.blockSize)
this.l(c.splice(0, 16));
return this;
}, finalize: function () { var a, b = this.f, c = this.g, b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1, 1)]); for (a = b.length + 2; a & 15; a++)
b.push(0); b.push(Math.floor(this.c / 0x100000000)); for (b.push(this.c | 0); b.length;)
this.l(b.splice(0, 16)); this.reset(); return c; }, D: [1732584193, 4023233417, 2562383102, 271733878, 3285377520],
b: [1518500249, 1859775393, 2400959708, 3395469782], l: function (a) { var b, c, d, e, f, g, h = this.g, k; if ("undefined" !== typeof Uint32Array)
for (k = Array(80), c = 0; 16 > c; c++)
k[c] = a[c];
else
k = a; c = h[0]; d = h[1]; e = h[2]; f = h[3]; g = h[4]; for (a = 0; 79 >= a; a++)
16 <= a && (b = k[a - 3] ^ k[a - 8] ^ k[a - 14] ^ k[a - 16], k[a] = b << 1 | b >>> 31), b = 19 >= a ? d & e | ~d & f : 39 >= a ? d ^ e ^ f : 59 >= a ? d & e | d & f | e & f : 79 >= a ? d ^ e ^ f : void 0, b = (c << 5 | c >>> 27) + b + g + k[a] + this.b[Math.floor(a / 20)] | 0, g = f, f = e, e = d << 30 | d >>> 2, d = c, c = b; h[0] = h[0] + c | 0; h[1] = h[1] + d | 0; h[2] = h[2] + e | 0; h[3] = h[3] + f | 0; h[4] = h[4] + g | 0; } };
sjcl.mode.ccm = { name: "ccm", F: [], listenProgress: function (a) { sjcl.mode.ccm.F.push(a); }, unListenProgress: function (a) { a = sjcl.mode.ccm.F.indexOf(a); -1 < a && sjcl.mode.ccm.F.splice(a, 1); }, da: function (a) { var b = sjcl.mode.ccm.F.slice(), c; for (c = 0; c < b.length; c += 1)
b[c](a); }, encrypt: function (a, b, c, d, e) {
var f, g = b.slice(0), h = sjcl.bitArray, k = h.bitLength(c) / 8, n = h.bitLength(g) / 8;
e = e || 64;
d = d || [];
if (7 > k)
throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");
for (f = 2; 4 > f && n >>> 8 * f; f++)
;
f < 15 - k && (f = 15 - k);
c = h.clamp(c, 8 * (15 - f));
b = sjcl.mode.ccm.U(a, b, c, d, e, f);
g = sjcl.mode.ccm.V(a, g, c, b, e, f);
return h.concat(g.data, g.tag);
}, decrypt: function (a, b, c, d, e) {
e = e || 64;
d = d || [];
var f = sjcl.bitArray, g = f.bitLength(c) / 8, h = f.bitLength(b), k = f.clamp(b, h - e), n = f.bitSlice(b, h - e), h = (h - e) / 8;
if (7 > g)
throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");
for (b = 2; 4 > b && h >>> 8 * b; b++)
;
b < 15 - g && (b = 15 - g);
c = f.clamp(c, 8 * (15 - b));
k = sjcl.mode.ccm.V(a, k, c, n, e, b);
a = sjcl.mode.ccm.U(a, k.data, c, d, e, b);
if (!f.equal(k.tag, a))
throw new sjcl.exception.corrupt("ccm: tag doesn't match");
return k.data;
}, ka: function (a, b, c, d, e, f) { var g = [], h = sjcl.bitArray, k = h.B; d = [h.partial(8, (b.length ? 64 : 0) | d - 2 << 2 | f - 1)]; d = h.concat(d, c); d[3] |= e; d = a.encrypt(d); if (b.length)
for (c = h.bitLength(b) / 8, 65279 >= c ? g = [h.partial(16, c)] : 0xffffffff >= c && (g = h.concat([h.partial(16, 65534)], [c])), g = h.concat(g, b), b = 0; b < g.length; b += 4)
d = a.encrypt(k(d, g.slice(b, b + 4).concat([0, 0, 0]))); return d; }, U: function (a, b, c, d, e, f) {
var g = sjcl.bitArray, h = g.B;
e /= 8;
if (e % 2 || 4 > e || 16 < e)
throw new sjcl.exception.invalid("ccm: invalid tag length");
if (0xffffffff < d.length || 0xffffffff < b.length)
throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
c = sjcl.mode.ccm.ka(a, d, c, e, g.bitLength(b) / 8, f);
for (d = 0; d < b.length; d += 4)
c = a.encrypt(h(c, b.slice(d, d + 4).concat([0, 0, 0])));
return g.clamp(c, 8 * e);
}, V: function (a, b, c, d, e, f) {
var g, h = sjcl.bitArray;
g = h.B;
var k = b.length, n = h.bitLength(b), l = k / 50, m = l;
c = h.concat([h.partial(8, f - 1)], c).concat([0, 0, 0]).slice(0, 4);
d = h.bitSlice(g(d, a.encrypt(c)), 0, e);
if (!k)
return { tag: d, data: [] };
for (g = 0; g < k; g += 4)
g > l && (sjcl.mode.ccm.da(g /
k), l += m), c[3]++, e = a.encrypt(c), b[g] ^= e[0], b[g + 1] ^= e[1], b[g + 2] ^= e[2], b[g + 3] ^= e[3];
return { tag: d, data: h.clamp(b, n) };
} };
void 0 === sjcl.beware && (sjcl.beware = {});
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."] = function () {
sjcl.mode.cbc = { name: "cbc", encrypt: function (a, b, c, d) {
if (d && d.length)
throw new sjcl.exception.invalid("cbc can't authenticate data");
if (128 !== sjcl.bitArray.bitLength(c))
throw new sjcl.exception.invalid("cbc iv must be 128 bits");
var e = sjcl.bitArray, f = e.B, g = e.bitLength(b), h = 0, k = [];
if (g & 7)
throw new sjcl.exception.invalid("pkcs#5 padding only works for multiples of a byte");
for (d = 0; h + 128 <= g; d += 4, h += 128)
c = a.encrypt(f(c, b.slice(d, d + 4))), k.splice(d, 0, c[0], c[1], c[2], c[3]);
g = 0x1010101 * (16 - (g >> 3 & 15));
c = a.encrypt(f(c, e.concat(b, [g, g, g, g]).slice(d, d + 4)));
k.splice(d, 0, c[0], c[1], c[2], c[3]);
return k;
}, decrypt: function (a, b, c, d) {
if (d && d.length)
throw new sjcl.exception.invalid("cbc can't authenticate data");
if (128 !== sjcl.bitArray.bitLength(c))
throw new sjcl.exception.invalid("cbc iv must be 128 bits");
if (sjcl.bitArray.bitLength(b) & 127 || !b.length)
throw new sjcl.exception.corrupt("cbc ciphertext must be a positive multiple of the block size");
var e = sjcl.bitArray, f = e.B, g, h = [];
for (d = 0; d < b.length; d += 4)
g = b.slice(d, d + 4), c = f(c, a.decrypt(g)), h.splice(d, 0, c[0], c[1], c[2], c[3]), c = g;
g = h[d - 1] & 255;
if (0 === g || 16 < g)
throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");
c = 0x1010101 * g;
if (!e.equal(e.bitSlice([c, c, c, c], 0, 8 * g), e.bitSlice(h, 32 * h.length - 8 * g, 32 * h.length)))
throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");
return e.bitSlice(h, 0, 32 * h.length - 8 * g);
} };
};
sjcl.misc.hmac = function (a, b) { this.W = b = b || sjcl.hash.sha256; var c = [[], []], d, e = b.prototype.blockSize / 32; this.A = [new b, new b]; a.length > e && (a = b.hash(a)); for (d = 0; d < e; d++)
c[0][d] = a[d] ^ 909522486, c[1][d] = a[d] ^ 1549556828; this.A[0].update(c[0]); this.A[1].update(c[1]); this.P = new b(this.A[0]); };
sjcl.misc.hmac.prototype.encrypt = sjcl.misc.hmac.prototype.mac = function (a) { if (this.Z)
throw new sjcl.exception.invalid("encrypt on already updated hmac called!"); this.update(a); return this.digest(a); };
sjcl.misc.hmac.prototype.reset = function () { this.P = new this.W(this.A[0]); this.Z = !1; };
sjcl.misc.hmac.prototype.update = function (a) { this.Z = !0; this.P.update(a); };
sjcl.misc.hmac.prototype.digest = function () { var a = this.P.finalize(), a = (new this.W(this.A[1])).update(a).finalize(); this.reset(); return a; };
sjcl.misc.pbkdf2 = function (a, b, c, d, e) { c = c || 1E4; if (0 > d || 0 > c)
throw new sjcl.exception.invalid("invalid params to pbkdf2"); "string" === typeof a && (a = sjcl.codec.utf8String.toBits(a)); "string" === typeof b && (b = sjcl.codec.utf8String.toBits(b)); e = e || sjcl.misc.hmac; a = new e(a); var f, g, h, k, n = [], l = sjcl.bitArray; for (k = 1; 32 * n.length < (d || 1); k++) {
e = f = a.encrypt(l.concat(b, [k]));
for (g = 1; g < c; g++)
for (f = a.encrypt(f), h = 0; h < f.length; h++)
e[h] ^= f[h];
n = n.concat(e);
} d && (n = l.clamp(n, d)); return n; };
sjcl.prng = function (a) { this.h = [new sjcl.hash.sha256]; this.o = [0]; this.O = 0; this.G = {}; this.M = 0; this.T = {}; this.X = this.i = this.s = this.fa = 0; this.b = [0, 0, 0, 0, 0, 0, 0, 0]; this.m = [0, 0, 0, 0]; this.K = void 0; this.L = a; this.C = !1; this.J = { progress: {}, seeded: {} }; this.w = this.ea = 0; this.H = 1; this.I = 2; this.aa = 0x10000; this.R = [0, 48, 64, 96, 128, 192, 0x100, 384, 512, 768, 1024]; this.ba = 3E4; this.$ = 80; };
sjcl.prng.prototype = { randomWords: function (a, b) {
var c = [], d;
d = this.isReady(b);
var e;
if (d === this.w)
throw new sjcl.exception.notReady("generator isn't seeded");
if (d & this.I) {
d = !(d & this.H);
e = [];
var f = 0, g;
this.X = e[0] = (new Date).valueOf() + this.ba;
for (g = 0; 16 > g; g++)
e.push(0x100000000 * Math.random() | 0);
for (g = 0; g < this.h.length && (e = e.concat(this.h[g].finalize()), f += this.o[g], this.o[g] = 0, d || !(this.O & 1 << g)); g++)
;
this.O >= 1 << this.h.length && (this.h.push(new sjcl.hash.sha256), this.o.push(0));
this.i -= f;
f > this.s && (this.s =
f);
this.O++;
this.b = sjcl.hash.sha256.hash(this.b.concat(e));
this.K = new sjcl.cipher.aes(this.b);
for (d = 0; 4 > d && (this.m[d] = this.m[d] + 1 | 0, !this.m[d]); d++)
;
}
for (d = 0; d < a; d += 4)
0 === (d + 1) % this.aa && t(this), e = y(this), c.push(e[0], e[1], e[2], e[3]);
t(this);
return c.slice(0, a);
}, setDefaultParanoia: function (a, b) { if (0 === a && "Setting paranoia=0 will ruin your security; use it only for testing" !== b)
throw new sjcl.exception.invalid("Setting paranoia=0 will ruin your security; use it only for testing"); this.L = a; }, addEntropy: function (a, b, c) {
c = c || "user";
var d, e, f = (new Date).valueOf(), g = this.G[c], h = this.isReady(), k = 0;
d = this.T[c];
void 0 === d && (d = this.T[c] = this.fa++);
void 0 === g && (g = this.G[c] = 0);
this.G[c] = (this.G[c] + 1) % this.h.length;
switch (typeof a) {
case "number":
void 0 === b && (b = 1);
this.h[g].update([d, this.M++, 1, b, f, 1, a | 0]);
break;
case "object":
c = Object.prototype.toString.call(a);
if ("[object Uint32Array]" === c) {
e = [];
for (c = 0; c < a.length; c++)
e.push(a[c]);
a = e;
}
else
for ("[object Array]" !== c && (k = 1), c = 0; c < a.length && !k; c++)
"number" !== typeof a[c] &&
(k = 1);
if (!k) {
if (void 0 === b)
for (c = b = 0; c < a.length; c++)
for (e = a[c]; 0 < e;)
b++, e = e >>> 1;
this.h[g].update([d, this.M++, 2, b, f, a.length].concat(a));
}
break;
case "string":
void 0 === b && (b = a.length);
this.h[g].update([d, this.M++, 3, b, f, a.length]);
this.h[g].update(a);
break;
default: k = 1;
}
if (k)
throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");
this.o[g] += b;
this.i += b;
h === this.w && (this.isReady() !== this.w && z("seeded", Math.max(this.s, this.i)), z("progress", this.getProgress()));
},
isReady: function (a) { a = this.R[void 0 !== a ? a : this.L]; return this.s && this.s >= a ? this.o[0] > this.$ && (new Date).valueOf() > this.X ? this.I | this.H : this.H : this.i >= a ? this.I | this.w : this.w; }, getProgress: function (a) { a = this.R[a ? a : this.L]; return this.s >= a ? 1 : this.i > a ? 1 : this.i / a; }, startCollectors: function () {
if (!this.C) {
this.a = { loadTimeCollector: A(this, this.ja), mouseCollector: A(this, this.la), keyboardCollector: A(this, this.ia), accelerometerCollector: A(this, this.ca), touchCollector: A(this, this.na) };
if (window.addEventListener)
window.addEventListener("load", this.a.loadTimeCollector, !1), window.addEventListener("mousemove", this.a.mouseCollector, !1), window.addEventListener("keypress", this.a.keyboardCollector, !1), window.addEventListener("devicemotion", this.a.accelerometerCollector, !1), window.addEventListener("touchmove", this.a.touchCollector, !1);
else if (document.attachEvent)
document.attachEvent("onload", this.a.loadTimeCollector), document.attachEvent("onmousemove", this.a.mouseCollector), document.attachEvent("keypress", this.a.keyboardCollector);
else
throw new sjcl.exception.bug("can't attach event");
this.C = !0;
}
}, stopCollectors: function () {
this.C && (window.removeEventListener ? (window.removeEventListener("load", this.a.loadTimeCollector, !1), window.removeEventListener("mousemove", this.a.mouseCollector, !1), window.removeEventListener("keypress", this.a.keyboardCollector, !1), window.removeEventListener("devicemotion", this.a.accelerometerCollector, !1), window.removeEventListener("touchmove", this.a.touchCollector, !1)) : document.detachEvent && (document.detachEvent("onload", this.a.loadTimeCollector), document.detachEvent("onmousemove", this.a.mouseCollector), document.detachEvent("keypress", this.a.keyboardCollector)), this.C = !1);
}, addEventListener: function (a, b) { this.J[a][this.ea++] = b; }, removeEventListener: function (a, b) { var c, d, e = this.J[a], f = []; for (d in e)
e.hasOwnProperty(d) && e[d] === b && f.push(d); for (c = 0; c < f.length; c++)
d = f[c], delete e[d]; }, ia: function () { B(this, 1); }, la: function (a) { var b, c; try {
b = a.x || a.clientX || a.offsetX || 0, c = a.y || a.clientY || a.offsetY || 0;
}
catch (d) {
c = b = 0;
} 0 != b && 0 != c && this.addEntropy([b, c], 2, "mouse"); B(this, 0); }, na: function (a) {
a =
a.touches[0] || a.changedTouches[0];
this.addEntropy([a.pageX || a.clientX, a.pageY || a.clientY], 1, "touch");
B(this, 0);
}, ja: function () { B(this, 2); }, ca: function (a) { a = a.accelerationIncludingGravity.x || a.accelerationIncludingGravity.y || a.accelerationIncludingGravity.z; if (window.orientation) {
var b = window.orientation;
"number" === typeof b && this.addEntropy(b, 1, "accelerometer");
} a && this.addEntropy(a, 2, "accelerometer"); B(this, 0); } };
function z(a, b) { var c, d = sjcl.random.J[a], e = []; for (c in d)
d.hasOwnProperty(c) && e.push(d[c]); for (c = 0; c < e.length; c++)
e[c](b); }
function B(a, b) { "undefined" !== typeof window && window.performance && "function" === typeof window.performance.now ? a.addEntropy(window.performance.now(), b, "loadtime") : a.addEntropy((new Date).valueOf(), b, "loadtime"); }
function t(a) { a.b = y(a).concat(y(a)); a.K = new sjcl.cipher.aes(a.b); }
function y(a) { for (var b = 0; 4 > b && (a.m[b] = a.m[b] + 1 | 0, !a.m[b]); b++)
; return a.K.encrypt(a.m); }
function A(a, b) { return function () { b.apply(a, arguments); }; }
sjcl.random = new sjcl.prng(6);
a: try {
var C, D, E, F;
if (F = "undefined" !== typeof module && module.exports) {
var G;
try {
G = require("crypto");
}
catch (a) {
G = null;
}
F = D = G;
}
if (F && D.randomBytes)
C = D.randomBytes(128), C = new Uint32Array((new Uint8Array(C)).buffer), sjcl.random.addEntropy(C, 1024, "crypto['randomBytes']");
else if ("undefined" !== typeof window && "undefined" !== typeof Uint32Array) {
E = new Uint32Array(32);
if (window.crypto && window.crypto.getRandomValues)
window.crypto.getRandomValues(E);
else if (window.msCrypto && window.msCrypto.getRandomValues)
window.msCrypto.getRandomValues(E);
else
break a;
sjcl.random.addEntropy(E, 1024, "crypto['getRandomValues']");
}
}
catch (a) {
"undefined" !== typeof window && window.console && (console.log("There was an error collecting entropy from the browser:"), console.log(a));
}
sjcl.json = { defaults: { v: 1, iter: 1E4, ks: 128, ts: 64, mode: "ccm", adata: "", cipher: "aes" }, ha: function (a, b, c, d) {
c = c || {};
d = d || {};
var e = sjcl.json, f = e.j({ iv: sjcl.random.randomWords(4, 0) }, e.defaults), g;
e.j(f, c);
c = f.adata;
"string" === typeof f.salt && (f.salt = sjcl.codec.base64.toBits(f.salt));
"string" === typeof f.iv && (f.iv = sjcl.codec.base64.toBits(f.iv));
if (!sjcl.mode[f.mode] || !sjcl.cipher[f.cipher] || "string" === typeof a && 100 >= f.iter || 64 !== f.ts && 96 !== f.ts && 128 !== f.ts || 128 !== f.ks && 192 !== f.ks && 0x100 !== f.ks || 2 > f.iv.length ||
4 < f.iv.length)
throw new sjcl.exception.invalid("json encrypt: invalid parameters");
"string" === typeof a ? (g = sjcl.misc.cachedPbkdf2(a, f), a = g.key.slice(0, f.ks / 32), f.salt = g.salt) : sjcl.ecc && a instanceof sjcl.ecc.elGamal.publicKey && (g = a.kem(), f.kemtag = g.tag, a = g.key.slice(0, f.ks / 32));
"string" === typeof b && (b = sjcl.codec.utf8String.toBits(b));
"string" === typeof c && (f.adata = c = sjcl.codec.utf8String.toBits(c));
g = new sjcl.cipher[f.cipher](a);
e.j(d, f);
d.key = a;
f.ct = "ccm" === f.mode && sjcl.arrayBuffer && sjcl.arrayBuffer.ccm &&
b instanceof ArrayBuffer ? sjcl.arrayBuffer.ccm.encrypt(g, b, f.iv, c, f.ts) : sjcl.mode[f.mode].encrypt(g, b, f.iv, c, f.ts);
return f;
}, encrypt: function (a, b, c, d) { var e = sjcl.json, f = e.ha.apply(e, arguments); return e.encode(f); }, ga: function (a, b, c, d) {
c = c || {};
d = d || {};
var e = sjcl.json;
b = e.j(e.j(e.j({}, e.defaults), b), c, !0);
var f, g;
f = b.adata;
"string" === typeof b.salt && (b.salt = sjcl.codec.base64.toBits(b.salt));
"string" === typeof b.iv && (b.iv = sjcl.codec.base64.toBits(b.iv));
if (!sjcl.mode[b.mode] || !sjcl.cipher[b.cipher] || "string" ===
typeof a && 100 >= b.iter || 64 !== b.ts && 96 !== b.ts && 128 !== b.ts || 128 !== b.ks && 192 !== b.ks && 0x100 !== b.ks || !b.iv || 2 > b.iv.length || 4 < b.iv.length)
throw new sjcl.exception.invalid("json decrypt: invalid parameters");
"string" === typeof a ? (g = sjcl.misc.cachedPbkdf2(a, b), a = g.key.slice(0, b.ks / 32), b.salt = g.salt) : sjcl.ecc && a instanceof sjcl.ecc.elGamal.secretKey && (a = a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0, b.ks / 32));
"string" === typeof f && (f = sjcl.codec.utf8String.toBits(f));
g = new sjcl.cipher[b.cipher](a);
f = "ccm" ===
b.mode && sjcl.arrayBuffer && sjcl.arrayBuffer.ccm && b.ct instanceof ArrayBuffer ? sjcl.arrayBuffer.ccm.decrypt(g, b.ct, b.iv, b.tag, f, b.ts) : sjcl.mode[b.mode].decrypt(g, b.ct, b.iv, f, b.ts);
e.j(d, b);
d.key = a;
return 1 === c.raw ? f : sjcl.codec.utf8String.fromBits(f);
}, decrypt: function (a, b, c, d) { var e = sjcl.json; return e.ga(a, e.decode(b), c, d); }, encode: function (a) {
var b, c = "{", d = "";
for (b in a)
if (a.hasOwnProperty(b)) {
if (!b.match(/^[a-z0-9]+$/i))
throw new sjcl.exception.invalid("json encode: invalid property name");
c += d + '"' +
b + '":';
d = ",";
switch (typeof a[b]) {
case "number":
case "boolean":
c += a[b];
break;
case "string":
c += '"' + escape(a[b]) + '"';
break;
case "object":
c += '"' + sjcl.codec.base64.fromBits(a[b], 0) + '"';
break;
default: throw new sjcl.exception.bug("json encode: unsupported type");
}
}
return c + "}";
}, decode: function (a) {
a = a.replace(/\s/g, "");
if (!a.match(/^\{.*\}$/))
throw new sjcl.exception.invalid("json decode: this isn't json!");
a = a.replace(/^\{|\}$/g, "").split(/,/);
var b = {}, c, d;
for (c = 0; c < a.length; c++) {
if (!(d = a[c].match(/^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i)))
throw new sjcl.exception.invalid("json decode: this isn't json!");
null != d[3] ? b[d[2]] = parseInt(d[3], 10) : null != d[4] ? b[d[2]] = d[2].match(/^(ct|adata|salt|iv)$/) ? sjcl.codec.base64.toBits(d[4]) : unescape(d[4]) : null != d[5] && (b[d[2]] = "true" === d[5]);
}
return b;
}, j: function (a, b, c) { void 0 === a && (a = {}); if (void 0 === b)
return a; for (var d in b)
if (b.hasOwnProperty(d)) {
if (c && void 0 !== a[d] && a[d] !== b[d])
throw new sjcl.exception.invalid("required parameter overridden");
a[d] = b[d];
} return a; }, pa: function (a, b) { var c = {}, d; for (d in a)
a.hasOwnProperty(d) && a[d] !== b[d] && (c[d] = a[d]); return c; }, oa: function (a, b) { var c = {}, d; for (d = 0; d < b.length; d++)
void 0 !== a[b[d]] && (c[b[d]] = a[b[d]]); return c; } };
sjcl.encrypt = sjcl.json.encrypt;
sjcl.decrypt = sjcl.json.decrypt;
sjcl.misc.ma = {};
sjcl.misc.cachedPbkdf2 = function (a, b) { var c = sjcl.misc.ma, d; b = b || {}; d = b.iter || 1E3; c = c[a] = c[a] || {}; d = c[d] = c[d] || { firstSalt: b.salt && b.salt.length ? b.salt.slice(0) : sjcl.random.randomWords(2, 0) }; c = void 0 === b.salt ? d.firstSalt : b.salt; d[c] = d[c] || sjcl.misc.pbkdf2(a, c, b.iter); return { key: d[c].slice(0), salt: c.slice(0) }; };
"undefined" !== typeof module && module.exports && (module.exports = sjcl);
"function" === typeof define && define([], function () { return sjcl; });
//# sourceMappingURL=sjcl.js.map

1
dist/api/utils/rncryptor/sjcl.js.map

File diff suppressed because one or more lines are too long

30
dist/api/utils/setup.js

@ -16,7 +16,7 @@ const QRCode = require("qrcode");
const publicIp = require("public-ip");
const password_1 = require("../utils/password");
const gitinfo_1 = require("../utils/gitinfo");
const USER_VERSION = 1;
const USER_VERSION = 2;
const setupDatabase = () => __awaiter(void 0, void 0, void 0, function* () {
console.log('=> [db] starting setup...');
yield setVersion();
@ -44,8 +44,34 @@ function setVersion() {
}
function migrate() {
return __awaiter(this, void 0, void 0, function* () {
addTableColumn('sphinx_chats', 'group_key');
addTableColumn('sphinx_chats', 'group_private_key');
addTableColumn('sphinx_chats', 'host');
addTableColumn('sphinx_chats', 'price_to_join', 'BIGINT');
addTableColumn('sphinx_chats', 'price_per_message', 'BIGINT');
addTableColumn('sphinx_chats', 'owner_pubkey');
addTableColumn('sphinx_messages', 'sender_alias');
addTableColumn('sphinx_chat_members', 'alias');
addTableColumn('sphinx_contacts', 'from_group');
try {
yield models_1.sequelize.query(`alter table sphinx_invites add invoice text`);
yield models_1.sequelize.query(`
CREATE TABLE sphinx_chat_members (
chat_id INT,
contact_id INT,
alias TEXT,
role INT,
total_spent INT,
total_messages INT,
last_active DATETIME
)`);
}
catch (e) { }
});
}
function addTableColumn(table, column, type = 'TEXT') {
return __awaiter(this, void 0, void 0, function* () {
try {
yield models_1.sequelize.query(`alter table ${table} add ${column} ${type}`);
}
catch (e) {
//console.log('=> migrate failed',e)

2
dist/api/utils/setup.js.map

@ -1 +1 @@
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../api/utils/setup.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../api/utils/setup.ts"],"names":[],"mappings":"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"}

136
dist/api/utils/signer.js

@ -0,0 +1,136 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const grpc = require("grpc");
const lightning_1 = require("./lightning");
const path = require("path");
const ByteBuffer = require("bytebuffer");
// var protoLoader = require('@grpc/proto-loader')
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname, '../../config/app.json'))[env];
var signerClient = null;
exports.loadSigner = () => {
console.log("LOAD SIGNER RRRRRR", signerClient ? true : false);
if (signerClient) {
return signerClient;
}
else {
console.log("LOAD SIGNER AGAIN!!!!");
try {
var credentials = lightning_1.loadCredentials();
var lnrpcDescriptor = grpc.load("signer.proto");
var signer = lnrpcDescriptor.signrpc;
signerClient = new signer.Signer(config.node_ip + ':' + config.lnd_port, credentials);
console.log("SIGNER CLIENT", signerClient);
return signerClient;
}
catch (e) {
throw e;
}
}
};
exports.signMessage = (msg) => {
return new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () {
let signer = yield exports.loadSigner();
try {
const options = {
msg: ByteBuffer.fromHex(msg),
key_loc: { key_family: 6, key_index: 0 },
};
signer.signMessage(options, function (err, sig) {
if (err || !sig.signature) {
reject(err);
}
else {
resolve(sig.signature);
}
});
}
catch (e) {
reject(e);
}
}));
};
exports.signBuffer = (msg) => {
return new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () {
let signer = yield exports.loadSigner();
try {
const options = { msg };
signer.signMessage(options, function (err, sig) {
if (err || !sig.signature) {
reject(err);
}
else {
resolve(sig.signature);
}
});
}
catch (e) {
reject(e);
}
}));
};
function verifyMessage(msg, sig, pubkey) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
let signer = yield exports.loadSigner();
try {
const options = {
msg: ByteBuffer.fromHex(msg),
signature: sig,
pubkey: ByteBuffer.fromHex(pubkey),
};
signer.verifyMessage(options, function (err, res) {
if (err) {
reject(err);
}
else {
resolve(res);
}
});
}
catch (e) {
reject(e);
}
}));
}
function signAscii(ascii) {
return __awaiter(this, void 0, void 0, function* () {
try {
const sig = yield exports.signMessage(ascii_to_hexa(ascii));
return sig;
}
catch (e) {
throw e;
}
});
}
exports.signAscii = signAscii;
function verifyAscii(ascii, sig, pubkey) {
return __awaiter(this, void 0, void 0, function* () {
try {
const r = yield verifyMessage(ascii_to_hexa(ascii), sig, pubkey);
return r;
}
catch (e) {
throw e;
}
});
}
exports.verifyAscii = verifyAscii;
function ascii_to_hexa(str) {
var arr1 = [];
for (var n = 0, l = str.length; n < l; n++) {
var hex = Number(str.charCodeAt(n)).toString(16);
arr1.push(hex);
}
return arr1.join('');
}
//# sourceMappingURL=signer.js.map

1
dist/api/utils/signer.js.map

@ -0,0 +1 @@
{"version":3,"file":"signer.js","sourceRoot":"","sources":["../../../api/utils/signer.ts"],"names":[],"mappings":"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"}

121
dist/api/utils/tribes.js

@ -0,0 +1,121 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const moment = require("moment");
const zbase32 = require("./zbase32");
const LND = require("./lightning");
const path = require("path");
const mqtt = require("mqtt");
const fetch = require("node-fetch");
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname, '../../config/app.json'))[env];
let client;
function connect(onMessage) {
return __awaiter(this, void 0, void 0, function* () {
try {
const info = yield LND.getInfo();
function reconnect() {
return __awaiter(this, void 0, void 0, function* () {
client = null;
const pwd = yield genSignedTimestamp();
console.log('[tribes] try to connect:', `tls://${config.tribes_host}:8883`);
client = mqtt.connect(`tls://${config.tribes_host}:8883`, {
username: info.identity_pubkey,
password: pwd,
reconnectPeriod: 0,
});
client.on('connect', function () {
console.log("[tribes] connected!");
client.subscribe(`${info.identity_pubkey}/#`);
});
client.on('close', function (e) {
setTimeout(() => reconnect(), 2000);
});
client.on('error', function (e) {
console.log('[tribes] error: ', e.message || e);
});
client.on('message', function (topic, message) {
if (onMessage)
onMessage(topic, message);
});
});
}
reconnect();
}
catch (e) {
console.log("TRIBES ERROR", e);
}
});
}
exports.connect = connect;
function subscribe(topic) {
if (client)
client.subscribe(topic);
}
exports.subscribe = subscribe;
function publish(topic, msg) {
if (client)
client.publish(topic, msg);
}
exports.publish = publish;
function declare({ uuid, name, description, tags, img, groupKey, host, pricePerMessage, priceToJoin, ownerAlias, ownerPubkey }) {
return __awaiter(this, void 0, void 0, function* () {
const r = yield fetch('https://' + host + '/tribes', {
method: 'POST',
body: JSON.stringify({
uuid, groupKey,
name, description, tags, img: img || '',
pricePerMessage: pricePerMessage || 0,
priceToJoin: priceToJoin || 0,
ownerAlias, ownerPubkey,
}),
headers: { 'Content-Type': 'application/json' }
});
const j = yield r.json();
console.log(j);
});
}
exports.declare = declare;
function genSignedTimestamp() {
return __awaiter(this, void 0, void 0, function* () {
const now = moment().unix();
const tsBytes = Buffer.from(now.toString(16), 'hex');
const sig = yield LND.signBuffer(tsBytes);
const sigBytes = zbase32.decode(sig);
const totalLength = tsBytes.length + sigBytes.length;
const buf = Buffer.concat([tsBytes, sigBytes], totalLength);
return urlBase64(buf);
});
}
exports.genSignedTimestamp = genSignedTimestamp;
function verifySignedTimestamp(stsBase64) {
return __awaiter(this, void 0, void 0, function* () {
const stsBuf = Buffer.from(stsBase64, 'base64');
const sig = stsBuf.subarray(4, 92);
const sigZbase32 = zbase32.encode(sig);
const r = yield LND.verifyBytes(stsBuf.subarray(0, 4), sigZbase32); // sig needs to be zbase32 :(
if (r.valid) {
return r.pubkey;
}
else {
return false;
}
});
}
exports.verifySignedTimestamp = verifySignedTimestamp;
function getHost() {
return config.tribes_host || '';
}
exports.getHost = getHost;
function urlBase64(buf) {
return buf.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
}
//# sourceMappingURL=tribes.js.map

1
dist/api/utils/tribes.js.map

@ -0,0 +1 @@
{"version":3,"file":"tribes.js","sourceRoot":"","sources":["../../../api/utils/tribes.ts"],"names":[],"mappings":"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"}

9
dist/app.js

@ -21,6 +21,7 @@ const hub_1 = require("./api/hub");
const setup_1 = require("./api/utils/setup");
const controllers = require("./api/controllers");
const socket = require("./api/utils/socket");
const network = require("./api/network");
let server = null;
const port = process.env.PORT || 3001;
const env = process.env.NODE_ENV || 'development';
@ -34,10 +35,14 @@ function connectToLND() {
i++;
console.log(`=> [lnd] connecting... attempt #${i}`);
try {
yield controllers.iniGrpcSubscriptions();
mainSetup();
yield network.initGrpcSubscriptions(); // LND
yield mainSetup(); // DB + express
yield network.initTribesSubscriptions(); // MQTT
}
catch (e) {
if (e.details) {
console.log(`=> [lnd] error details: ${e.details}`);
}
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
yield connectToLND();
}), 2000);

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save