From 81959f68836b952792182f32615838ebba5177b5 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 23 Sep 2024 18:51:44 +0300 Subject: [PATCH] added nonce to group resource --- src/components/Group/Group.tsx | 9 +- src/qdn/encryption/group-encryption.ts | 161 +++++++++++++++---------- 2 files changed, 99 insertions(+), 71 deletions(-) diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 338b0e8..5f98782 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -165,8 +165,8 @@ export function validateSecretKey(obj) { return false; } - // Check for messageKey and nonce properties - if (!value.hasOwnProperty("messageKey") || !value.hasOwnProperty("nonce")) { + // Check for messageKey + if (!value.hasOwnProperty("messageKey")) { return false; } @@ -177,9 +177,6 @@ export function validateSecretKey(obj) { ) { return false; } - if (typeof value.nonce !== "string" || value.nonce.trim() === "") { - return false; - } } // If all checks passed, return true @@ -2162,7 +2159,7 @@ export const Group = ({ ); }; - + return ( <> { const messageKey = new Uint8Array(32); // 32 bytes for the symmetric key crypto.getRandomValues(messageKey); - const nonce = new Uint8Array(24); // 24 bytes for the nonce - crypto.getRandomValues(nonce); + - return { messageKey: uint8ArrayToBase64(messageKey), nonce: uint8ArrayToBase64(nonce) }; + return { messageKey: uint8ArrayToBase64(messageKey)}; }; @@ -138,76 +137,108 @@ export const encryptDataGroup = ({ data64, publicKeys, privateKey, userPublicKey } } -export const encryptSingle = async ({ data64,secretKeyObject }: any) => { - - const highestKey = Math.max(...Object.keys(secretKeyObject).filter(item=> !isNaN(+item)).map(Number)); - - +export const encryptSingle = async ({ data64, secretKeyObject }: any) => { + // Find the highest key in the secretKeyObject + const highestKey = Math.max(...Object.keys(secretKeyObject).filter(item => !isNaN(+item)).map(Number)); const highestKeyObject = secretKeyObject[highestKey]; - - const Uint8ArrayData = base64ToUint8Array(data64) - const nonce = base64ToUint8Array(highestKeyObject.nonce) - const messageKey = base64ToUint8Array(highestKeyObject.messageKey) - + + // Convert data and keys from base64 + const Uint8ArrayData = base64ToUint8Array(data64); + const messageKey = base64ToUint8Array(highestKeyObject.messageKey); + if (!(Uint8ArrayData instanceof Uint8Array)) { - throw new Error("The Uint8ArrayData you've submitted is invalid") + throw new Error("The Uint8ArrayData you've submitted is invalid"); } - try { - const encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey); - - const encryptedDataBase64 = uint8ArrayToBase64(encryptedData) - const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits - const concatenatedData = highestKeyStr + encryptedDataBase64; - const finalEncryptedData = btoa(concatenatedData); - - - return finalEncryptedData; - } catch (error) { - - throw new Error("Error in encrypting data") + + let nonce, encryptedData, encryptedDataBase64, finalEncryptedData; + + if (highestKeyObject.nonce) { + // Old format: Use the nonce from secretKeyObject + nonce = base64ToUint8Array(highestKeyObject.nonce); + + // Encrypt the data with the existing nonce and message key + encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey); + encryptedDataBase64 = uint8ArrayToBase64(encryptedData); + + // Concatenate the highest key with the encrypted data (old format) + const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits + finalEncryptedData = btoa(highestKeyStr + encryptedDataBase64); + } else { + // New format: Generate a random nonce and embed it in the message + nonce = new Uint8Array(24); // 24 bytes for the nonce + crypto.getRandomValues(nonce); + + + // Encrypt the data with the new nonce and message key + encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey); + encryptedDataBase64 = uint8ArrayToBase64(encryptedData); + + // Convert the nonce to base64 + const nonceBase64 = uint8ArrayToBase64(nonce); + + // Concatenate the highest key, nonce, and encrypted data (new format) + const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits + finalEncryptedData = btoa(highestKeyStr + nonceBase64 + encryptedDataBase64); } -} - -export const decryptSingle = async ({ data64, secretKeyObject, skipDecodeBase64 }: any) => { - + + return finalEncryptedData; + }; + +export const decryptSingle = async ({ data64, secretKeyObject, skipDecodeBase64 }: any) => { + // First, decode the base64-encoded input (if skipDecodeBase64 is not set) const decodedData = skipDecodeBase64 ? data64 : atob(data64); - - // Extract the key (assuming it's 10 characters long) - const decodeForNumber = atob(decodedData) - const keyStr = decodeForNumber.slice(0, 10); - - // Convert the key string back to a number - const highestKey = parseInt(keyStr, 10); - - // Extract the remaining part as the Base64-encoded encrypted data - const encryptedDataBase64 = decodeForNumber.slice(10); - let _encryptedMessage = encryptedDataBase64 - if(!secretKeyObject[highestKey]) throw new Error('Cannot find correct secretKey') - const nonce64 = secretKeyObject[highestKey].nonce - const messageKey64 = secretKeyObject[highestKey].messageKey - - const Uint8ArrayData = base64ToUint8Array(_encryptedMessage) - const nonce = base64ToUint8Array(nonce64) - const messageKey = base64ToUint8Array(messageKey64) - - if (!(Uint8ArrayData instanceof Uint8Array)) { - throw new Error("The Uint8ArrayData you've submitted is invalid") + + // Then, decode it again for the specific format (if double encoding is used) + const decodeForNumber = atob(decodedData); + + // Extract the key (assuming it's 10 characters long) + const keyStr = decodeForNumber.slice(0, 10); + + // Convert the key string back to a number + const highestKey = parseInt(keyStr, 10); + + // Check if we have a valid secret key for the extracted highestKey + if (!secretKeyObject[highestKey]) { + throw new Error('Cannot find correct secretKey'); } - + + const secretKeyEntry = secretKeyObject[highestKey]; - // Decrypt the data using the nonce and messageKey - const decryptedData = nacl.secretbox.open(Uint8ArrayData, nonce, messageKey); - - // Check if decryption was successful - if (!decryptedData) { - throw new Error("Decryption failed"); - } - - // Convert the decrypted Uint8Array back to a UTF-8 string - return uint8ArrayToBase64(decryptedData) - -} + let nonceBase64, encryptedDataBase64; + + if (secretKeyEntry.nonce) { + // Old format: nonce is present in the secretKeyObject + nonceBase64 = secretKeyEntry.nonce; + encryptedDataBase64 = decodeForNumber.slice(10); // The remaining part is the encrypted data + } else { + // New format: nonce is included in the message (first 32 characters) + nonceBase64 = decodeForNumber.slice(10, 42); // First 32 characters for the nonce + encryptedDataBase64 = decodeForNumber.slice(42); // The remaining part is the encrypted data + } + + // Convert Base64 strings to Uint8Array + const Uint8ArrayData = base64ToUint8Array(encryptedDataBase64); + const nonce = base64ToUint8Array(nonceBase64); + const messageKey = base64ToUint8Array(secretKeyEntry.messageKey); + + if (!(Uint8ArrayData instanceof Uint8Array)) { + throw new Error("The Uint8ArrayData you've submitted is invalid"); + } + + // Decrypt the data using the nonce and messageKey + const decryptedData = nacl.secretbox.open(Uint8ArrayData, nonce, messageKey); + + // Check if decryption was successful + if (!decryptedData) { + throw new Error("Decryption failed"); + } + + // Convert the decrypted Uint8Array back to a Base64 string + return uint8ArrayToBase64(decryptedData); + }; + + export function decryptGroupData(data64EncryptedData: string, privateKey: string) {