Last active
March 31, 2022 19:54
-
-
Save abramsymons/c7c8ef37d392706bbe8cb9b913dee5f5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const CryptoJS = require("crypto-js"); | |
const nacl = require("tweetnacl"); | |
const B64 = require("base64-js"); | |
const { create } = require("apisauce"); | |
const assert = require("assert"); | |
const qrcode = require("qrcode-terminal"); | |
const baseURL = "http://test.brightid.org/profile"; | |
const api = create({ | |
baseURL, | |
headers: { "Cache-Control": "no-cache" }, | |
}); | |
const decryptData = (data, aesKey) => { | |
const decrypted = CryptoJS.AES.decrypt(data, aesKey).toString( | |
CryptoJS.enc.Utf8 | |
); | |
return JSON.parse(decrypted); | |
}; | |
const b64ToUrlSafeB64 = (s) => { | |
const alts = { | |
"/": "_", | |
"+": "-", | |
"=": "", | |
}; | |
return s.replace(/[/+=]/g, (c) => alts[c]); | |
}; | |
const hash = (data) => { | |
const h = CryptoJS.SHA256(data); | |
const b = h.toString(CryptoJS.enc.Base64); | |
return b64ToUrlSafeB64(b); | |
}; | |
const createImportQR = async () => { | |
const array = CryptoJS.lib.WordArray.random(16); | |
const aesKey = b64ToUrlSafeB64(CryptoJS.enc.Base64.stringify(array)); | |
console.log(`aesKey: ${aesKey}`); | |
const channelId = hash(aesKey); | |
console.log(`channelId: ${channelId}`); | |
const { publicKey, secretKey } = await nacl.sign.keyPair(); | |
const b64PublicKey = B64.fromByteArray(publicKey); | |
console.log(`b64PublicKey: ${b64PublicKey}`); | |
const b64SecretKey = B64.fromByteArray(secretKey); | |
console.log(`b64SecretKey: ${b64SecretKey}`); | |
const dataObj = { | |
signingKey: b64PublicKey, | |
timestamp: Date.now(), | |
}; | |
const data = JSON.stringify(dataObj); | |
const body = JSON.stringify({ data, uuid: "data" }); | |
const res = await api.post(`/upload/${channelId}`, body); | |
assert.ok(res.ok, "failed to post data to the channel"); | |
qrString = `${baseURL}?aes=${aesKey}&t=3`; | |
console.log(`QR string: ${qrString}`); | |
console.log( | |
`Deep link: https://app.brightid.org/connection-code/${encodeURIComponent( | |
qrString | |
)}` | |
); | |
qrcode.generate(qrString, { small: true }); | |
return { channelId, aesKey, signingKey: b64PublicKey }; | |
}; | |
const createSyncQR = async (brightID, signingKey, lastSyncTime) => { | |
const array = CryptoJS.lib.WordArray.random(16); | |
const aesKey = b64ToUrlSafeB64(CryptoJS.enc.Base64.stringify(array)); | |
console.log(`aesKey: ${aesKey}`); | |
const channelId = hash(aesKey); | |
console.log(`channelId: ${channelId}`); | |
const dataObj = { signingKey, lastSyncTime, isPrimaryDevice: false }; | |
const data = JSON.stringify(dataObj); | |
let body = JSON.stringify({ data, uuid: "data" }); | |
let res = await api.post(`/upload/${channelId}`, body); | |
assert.ok(res.ok, "failed to post data to the channel"); | |
// although the device has nothing to send for the primary device, | |
// it's required to send the completed flag to the channel | |
const uuid = `completed_${brightID}:${b64ToUrlSafeB64(signingKey)}`; | |
body = JSON.stringify({ data: "completed", uuid }); | |
res = await api.post(`/upload/${channelId}`, body); | |
assert.ok(res.ok, "failed to post completed flag to the channel"); | |
qrString = `${baseURL}?aes=${aesKey}&t=4`; | |
console.log(`QR string: ${qrString}`); | |
console.log( | |
`Deep link: https://app.brightid.org/connection-code/${encodeURIComponent( | |
qrString | |
)}` | |
); | |
qrcode.generate(qrString, { small: true }); | |
return { channelId, aesKey, signingKey }; | |
}; | |
const readChannel = async (data) => { | |
const { channelId, aesKey, signingKey } = data; | |
let res = await api.get(`/list/${channelId}`); | |
const dataIds = res.data.profileIds; | |
const uploader = (id) => id.replace("completed_", "").split(":")[1]; | |
const completed = dataIds.find( | |
(dataId) => | |
dataId.startsWith("completed_") && | |
uploader(dataId) != b64ToUrlSafeB64(signingKey) | |
); | |
if (!completed) { | |
return; | |
} | |
for (const dataId of dataIds) { | |
if (dataId.startsWith("userinfo_")) { | |
res = await api.get(`/download/${channelId}/${dataId}`); | |
const encrypted = res.data.data; | |
const data = decryptData(encrypted, aesKey); | |
console.log(data, "user info"); | |
} | |
if (dataId.startsWith("connection_")) { | |
res = await api.get(`/download/${channelId}/${dataId}`); | |
const encrypted = res.data.data; | |
const data = decryptData(encrypted, aesKey); | |
console.log(data, "connection"); | |
} | |
} | |
clearInterval(intervalID); | |
}; | |
const importBrightID = async () => { | |
const data = await createImportQR(); | |
intervalID = setInterval(() => readChannel(data), 3000); | |
}; | |
const syncBrightID = async () => { | |
const brightID = "5cvu9DUZyzPUclHHcgNhs0S71Z2nAOwAYAljYgisGgA"; // the brightid of the user | |
const signingKey = "WPL5WOLMbJ9M2wKbx9QaGOlJcXcIwQ7o8FfdoP+EX5g="; // use b64PublicKey created in import step | |
const lastSyncTime = 1645509278250; //last sync (or import) timestamp in milliseconds | |
const data = await createSyncQR(brightID, signingKey, lastSyncTime); | |
intervalID = setInterval(() => readChannel(data), 3000); | |
}; | |
// importBrightID(); | |
syncBrightID(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
heads up -
userinfo_
->sig_ userinfo_
completed_
->sig_ completed_