(chore): merge mostly pathway changes
This commit is contained in:
@@ -52,7 +52,7 @@ export const newIdentifier = (
|
||||
*
|
||||
*
|
||||
* @param {string} mnemonic
|
||||
* @return {*} {[string, string, string, string]}
|
||||
* @return {[string, string, string, string]} address, privateHex, publicHex, derivationPath
|
||||
*/
|
||||
export const deriveAddress = (
|
||||
mnemonic: string,
|
||||
@@ -88,7 +88,8 @@ export const generateSeed = (): string => {
|
||||
/**
|
||||
* Retrieve an access token, or "" if no DID is provided.
|
||||
*
|
||||
* @return {*}
|
||||
* @param {string} did
|
||||
* @return {string} JWT with basic payload
|
||||
*/
|
||||
export const accessToken = async (did?: string) => {
|
||||
if (did) {
|
||||
@@ -147,3 +148,156 @@ export const nextDerivationPath = (origDerivPath: string) => {
|
||||
.join("/");
|
||||
return newDerivPath;
|
||||
};
|
||||
|
||||
// Base64 encoding/decoding utilities for browser
|
||||
function base64ToArrayBuffer(base64: string): Uint8Array {
|
||||
const binaryString = atob(base64);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function arrayBufferToBase64(buffer: ArrayBuffer): string {
|
||||
const binary = String.fromCharCode(...new Uint8Array(buffer));
|
||||
return btoa(binary);
|
||||
}
|
||||
|
||||
const SALT_LENGTH = 16;
|
||||
const IV_LENGTH = 12;
|
||||
const KEY_LENGTH = 256;
|
||||
const ITERATIONS = 100000;
|
||||
|
||||
// Encryption helper function
|
||||
export async function encryptMessage(message: string, password: string) {
|
||||
const encoder = new TextEncoder();
|
||||
const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));
|
||||
const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
|
||||
|
||||
// Derive key from password using PBKDF2
|
||||
const keyMaterial = await crypto.subtle.importKey(
|
||||
"raw",
|
||||
encoder.encode(password),
|
||||
"PBKDF2",
|
||||
false,
|
||||
["deriveBits", "deriveKey"],
|
||||
);
|
||||
|
||||
const key = await crypto.subtle.deriveKey(
|
||||
{
|
||||
name: "PBKDF2",
|
||||
salt,
|
||||
iterations: ITERATIONS,
|
||||
hash: "SHA-256",
|
||||
},
|
||||
keyMaterial,
|
||||
{ name: "AES-GCM", length: KEY_LENGTH },
|
||||
false,
|
||||
["encrypt"],
|
||||
);
|
||||
|
||||
// Encrypt the message
|
||||
const encryptedContent = await crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv,
|
||||
},
|
||||
key,
|
||||
encoder.encode(message),
|
||||
);
|
||||
|
||||
// Return a JSON structure with base64-encoded components
|
||||
const result = {
|
||||
salt: arrayBufferToBase64(salt),
|
||||
iv: arrayBufferToBase64(iv),
|
||||
encrypted: arrayBufferToBase64(encryptedContent),
|
||||
};
|
||||
|
||||
return btoa(JSON.stringify(result));
|
||||
}
|
||||
|
||||
// Decryption helper function
|
||||
export async function decryptMessage(encryptedJson: string, password: string) {
|
||||
const decoder = new TextDecoder();
|
||||
const { salt, iv, encrypted } = JSON.parse(atob(encryptedJson));
|
||||
|
||||
// Convert base64 components back to Uint8Arrays
|
||||
const saltArray = base64ToArrayBuffer(salt);
|
||||
const ivArray = base64ToArrayBuffer(iv);
|
||||
const encryptedContent = base64ToArrayBuffer(encrypted);
|
||||
|
||||
// Derive the same key using PBKDF2 with the extracted salt
|
||||
const keyMaterial = await crypto.subtle.importKey(
|
||||
"raw",
|
||||
new TextEncoder().encode(password),
|
||||
"PBKDF2",
|
||||
false,
|
||||
["deriveBits", "deriveKey"],
|
||||
);
|
||||
|
||||
const key = await crypto.subtle.deriveKey(
|
||||
{
|
||||
name: "PBKDF2",
|
||||
salt: saltArray,
|
||||
iterations: ITERATIONS,
|
||||
hash: "SHA-256",
|
||||
},
|
||||
keyMaterial,
|
||||
{ name: "AES-GCM", length: KEY_LENGTH },
|
||||
false,
|
||||
["decrypt"],
|
||||
);
|
||||
|
||||
// Decrypt the content
|
||||
const decryptedContent = await crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: ivArray,
|
||||
},
|
||||
key,
|
||||
encryptedContent,
|
||||
);
|
||||
|
||||
// Convert the decrypted content back to a string
|
||||
return decoder.decode(decryptedContent);
|
||||
}
|
||||
|
||||
// Test function to verify encryption/decryption
|
||||
export async function testEncryptionDecryption() {
|
||||
try {
|
||||
const testMessage = "Hello, this is a test message! 🚀";
|
||||
const testPassword = "myTestPassword123";
|
||||
|
||||
console.log("Original message:", testMessage);
|
||||
|
||||
// Test encryption
|
||||
console.log("Encrypting...");
|
||||
const encrypted = await encryptMessage(testMessage, testPassword);
|
||||
console.log("Encrypted result:", encrypted);
|
||||
|
||||
// Test decryption
|
||||
console.log("Decrypting...");
|
||||
const decrypted = await decryptMessage(encrypted, testPassword);
|
||||
console.log("Decrypted result:", decrypted);
|
||||
|
||||
// Verify
|
||||
const success = testMessage === decrypted;
|
||||
console.log("Test " + (success ? "PASSED ✅" : "FAILED ❌"));
|
||||
console.log("Messages match:", success);
|
||||
|
||||
// Test with wrong password
|
||||
console.log("\nTesting with wrong password...");
|
||||
try {
|
||||
await decryptMessage(encrypted, "wrongPassword");
|
||||
console.log("Should not reach here");
|
||||
} catch (error) {
|
||||
console.log("Correctly failed with wrong password ✅");
|
||||
}
|
||||
|
||||
return success;
|
||||
} catch (error) {
|
||||
console.error("Test failed with error:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user