Skip to content

Instantly share code, notes, and snippets.

@abramsymons
Last active March 31, 2022 19:54
Show Gist options
  • Save abramsymons/c7c8ef37d392706bbe8cb9b913dee5f5 to your computer and use it in GitHub Desktop.
Save abramsymons/c7c8ef37d392706bbe8cb9b913dee5f5 to your computer and use it in GitHub Desktop.
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();
@Ashakibp
Copy link

heads up - userinfo_ -> sig_ userinfo_
completed_ -> sig_ completed_

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment