import { newUser, encryptorFromExistingUser, changePassword, exportKeys, importKeys, ResolvedNewUserResponse } from "index"; import { DecryptionError, VerificationError } from "./error"; describe("basic user encryptor usage", () => { // Users take a long time to create due to key generation. Use the same users across multiple tests let genericUser1: ResolvedNewUserResponse; let genericUser2: ResolvedNewUserResponse; const genericUser1Pass = "1234556789"; const genericUser2Pass = "987654321"; beforeAll(async function() { genericUser1 = await newUser(genericUser1Pass); genericUser2 = await newUser(genericUser2Pass); // We need a long timeout as key generation can take a while }, 20000); test("an encryptor cannot be recreated with an incorrect password", async function() { const { userDoc } = genericUser1; await expect( encryptorFromExistingUser(userDoc, "a") ).rejects.toThrowError( new DecryptionError( "Failed to decrypt WUEPriK (Wrapped User Encryption Private Key) with the supplied password.", "The password is probably incorrect." ) ); }); test("data encrypted upon signup can be decrypted on subsequent login", async function() { const textToEncrypt = "Some very sensitive information."; const { encryptor, userDoc } = genericUser1; const encryptedText = await encryptor.encrypt(textToEncrypt); const signInEncryptor = await encryptorFromExistingUser( // Ensure that all required data can be JSON serialized and then deserialized JSON.parse(JSON.stringify(userDoc)), genericUser1Pass ); const decryptedText = await signInEncryptor.decrypt(encryptedText); expect(decryptedText).toBe(textToEncrypt); }); test("data encrypted by one user cannot be decrypted by another", async function() { const textToEncrypt = "Some very sensitive information."; const { encryptor } = genericUser1; const encryptedText = await encryptor.encrypt(textToEncrypt); await expect( genericUser2.encryptor.decrypt(encryptedText) ).rejects.toThrowError( new DecryptionError( "Failed to decrypt WDEK (Wrapped Data Encryption Key).", "The WDEK probably belongs to another user or application." ) ); }); test("different users do not share kek salt", function() { const kekSalt1 = genericUser1.userDoc.kek_derivation_config.salt; const kekSalt2 = genericUser2.userDoc.kek_derivation_config.salt; expect(kekSalt1).not.toBe(kekSalt2); }); test("encrypted data can be shared with another user", async function() { const textToEncrypt = "Some very sensitive information."; const decryptingUser = genericUser1; const encryptingUser = genericUser2; const encryptedData = await encryptingUser.encryptor.encrypt( textToEncrypt ); // Shared key will be encrypted for the receiving user const sharedKey = await encryptingUser.encryptor.share( encryptedData, decryptingUser.userDoc.encryption_key_pair.public_key ); const decryptedText = await decryptingUser.encryptor.decrypt( // Ensure that it is JSON serializable JSON.parse(JSON.stringify(encryptedData)), // Ensure that it is JSON serializable JSON.parse(JSON.stringify(sharedKey)) ); expect(decryptedText).toBe(textToEncrypt); }); test("encrypted data cannot be shared with another user and decrypted by another", async function() { const textToEncrypt = "Some very sensitive information."; const decryptingUser = genericUser1; const encryptingUser = genericUser2; const encryptedData = await encryptingUser.encryptor.encrypt( textToEncrypt ); const sharedKey = await encryptingUser.encryptor.share( encryptedData, // Note - not the user that will be decryting! encryptingUser.userDoc.encryption_key_pair.public_key ); await expect( decryptingUser.encryptor.decrypt(encryptedData, sharedKey) ).rejects.toThrowError( new DecryptionError( "Failed to decrypt WDEK (Wrapped Data Encryption Key).", "The WDEK probably belongs to another user or application." ) ); }); test("data can be signed and verified by another user", async function() { const dataToSign = "This is definitely true"; const signingUser = genericUser1; const verifyingUser = genericUser2; const signedData = await signingUser.encryptor.sign(dataToSign); const verified = await verifyingUser.encryptor.verifyAgainst( signingUser.userDoc.signing_key_pair.public_key, // Ensure that all data is JSON serializable JSON.parse(JSON.stringify(signedData)) ); expect(verified).toBe(dataToSign); }); test("data signed by one user cannot be verified with a different user's public key", async function() { const dataToSign = "This is definitely true"; const signingUser = genericUser1; const verifyingUser = genericUser2; const signedData = await signingUser.encryptor.sign(dataToSign); await expect( verifyingUser.encryptor.verifyAgainst( verifyingUser.userDoc.signing_key_pair.public_key, signedData ) ).rejects.toThrowError( new VerificationError("Failed to verify signed data.") ); }); test("data signed by Python backend can be verified", async function() { const verifyingUser = genericUser1; const signedData = { data: { a: "b", c: { d: 1 } }, signature: "kZT7TUb+jLxr/HMUi/Muj+xxDqkEQxfWpecvNg7OtbmlXUmpAOvjyFdiUuh3QzHhxmCqwZj8/FjY\nePC408rYBKHe6PxXS7mwou9G5uzzm6k05r73vlmGhKPwovu390PYldlZyrqvzT6OhMCRFeDMDeD8\n9BiLaXHlY10NpCnPzMBxbL4jFha2jLYq5XAQGwrLXeuzYUiEQe5mEHAJuUl/3UE5etSmMNIOHiQj\nXFNuBpZZuxhZVR9YZpT3aaN2JQqihovLlPmhfC52fijoZFptev4FgUNDJo1rsJI8V2gRPFNp2FTV\nGuyykep6mB7wgotOX/97LI0HSuJi7FA1prrtAeF632airZj5dO9fSsYbrzK6HVH/yZA/2xkkeseg\nuywsuJf3VW4mfLUSh/GN1OsAdVIfcE4tjmityThkRKG8OHg1zl6ksRGh/1nn2lMZdLggInj6anL/\nYaN0B2kP3Ol126omGYCsnB651eClGFZvWOQgEcM/gIkWsvRx0zHeCgfpBmLEsZ2h3dKH7IjgT0qr\n6b5eXaiXFQgj1kVRnspFLSemWoisyjZFqKdumU/DITGUy5pZKqiGs0FAcLKvpGhtAZ/MHdymQDTT\n6oG00Xn6ycpTPPXoaqzB7s94nNnjPojmxHk8pF9uDTkY6zE4PEZ5Rh4HtjisgwOg5AV5uLdyAOk=\n" }; const publicKey = ` -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqKqkZDueKTQAjT8ZiBDF Bq13jBLLBtjD+8iE50c7d4gqnJTfkjK2t5H8ktKMRr1mjMA8BDmLdvkWa8dc8G8y V8XzoyQYhA8Y1YzbU7V9tVgO0IXRgDsP1N/PjjHST0l5RfQjjqvFJ1ds2YRs0rId Zn7VYLx9wSDlxTOUkqB1zLGNQdBCAjcZTx+rbEK4YxsozpYhunQzZmWPh7IgXSse 9dVqQyAs1cA2vwM0VlEqC2QwNb6mqlbL4Gc7lRKpCnsmar3ZnhAUJhdIT1Kwc5hK FphfFNvLYl1qDzfJO5yirB8Om+HUDM8AQAs4xZaEz3Q/RU7D4h4iq4AUd+c+xC0W W1ynvp8X60mjB4gpw4wmbceKbf17P2/4FMpn/RichR59UkKrPAIwXLflYh2Qj2bd iuGDFd6/3sv2E7OCCcv8X7uxflegUhTEJgJ8MkbtsqH6EpDIhMmYIvnQ4QccLkxF PeFhuCJzkUZVwti1HsMAifrTrTgdDJ1hLC3T8DnbJsPO5PL5NTynstOcQMuFzKCq MKplx42PxFNnNITl36KXe2Gpm60UsAliaM2qTfOvFvXmkeMGQBrmin8e3SHn9Nce Taf5FxcIhItCn6/BI6IW+XBh/Gw2MjUZv137WNu/UR4EvYRDDQxzazASTerN14jm nC/wTZ+lSv7teE7lue/7NfkCAwEAAQ== -----END PUBLIC KEY----- `.trim(); const verified = await verifyingUser.encryptor.verifyAgainst( publicKey, signedData ); expect(verified).toBe(signedData["data"]); }); test("changing password still allows decrypting previously encrypted data", async function() { const initialPassword = genericUser1Pass; const textToEncrypt = "Some very sensitive information."; const { encryptor, userDoc: initialUserDoc } = genericUser1; const encryptedText = await encryptor.encrypt(textToEncrypt); const newPassword = "987654321"; const newUserDoc = await changePassword( initialUserDoc, initialPassword, newPassword ); // We should not be able to get an encryptor using the old password await expect( encryptorFromExistingUser(newUserDoc, initialPassword) ).rejects.toThrowError( new DecryptionError( "Failed to decrypt WUEPriK (Wrapped User Encryption Private Key) with the supplied password.", "The password is probably incorrect." ) ); const signInEncryptor = await encryptorFromExistingUser( newUserDoc, newPassword ); const decryptedText = await signInEncryptor.decrypt(encryptedText); expect(decryptedText).toBe(textToEncrypt); }); test("exporting and importing keys still allows decrypting previously encrypted data", async function() { const initialPassword = genericUser1Pass; const textToEncrypt = "Some very sensitive information."; const { encryptor, userDoc: initialUserDoc } = genericUser1; const encryptedText = await encryptor.encrypt(textToEncrypt); const exportedKeys = await exportKeys(initialUserDoc, initialPassword); const newPassword = "987654321"; const newUserDoc = await importKeys( // Ensure we can convert to and from JSON JSON.parse(JSON.stringify(exportedKeys)), newPassword ); // We should not be able to get an encryptor using the old password await expect( encryptorFromExistingUser(newUserDoc, initialPassword) ).rejects.toThrowError( new DecryptionError( "Failed to decrypt WUEPriK (Wrapped User Encryption Private Key) with the supplied password.", "The password is probably incorrect." ) ); const signInEncryptor = await encryptorFromExistingUser( newUserDoc, newPassword ); const decryptedText = await signInEncryptor.decrypt(encryptedText); expect(decryptedText).toBe(textToEncrypt); }); });