diff --git a/docs/1_pure_tricks/HowTo_decrypt.md b/docs/1_pure_tricks/HowTo_decrypt.md new file mode 100644 index 0000000..4c82aba --- /dev/null +++ b/docs/1_pure_tricks/HowTo_decrypt.md @@ -0,0 +1,53 @@ +# HowTo: Decrypt data + +Decryption is an operation, the reverse of the encryption operation. To encrypt with a secret key, you need to know both password and initialization vector used to encrypt the data. + +Since iv is placed in an encrypted data string, the `decryptData()` function accepts the password and the string. +First, it is necessary to split encrypted string to separate the `iv` and the encrypted data. Then convert `iv` to _UTF-8_ and encrypted string to Base64 format. Since the data encrypted with `btoa()`, `atob()` is used to decrypt the data, which returns the original string. +```javascript + +async function decryptData(data, password) { + const [ivText, cipherB64url] = data.split('.'); //split encrypted data to get iv and cipher + const iv = hexStringToUint8(ivText); + const cipher = atob(fromBase64url(cipherB64url)); + return await decryptAESGCM(password, iv, cipher); + + function fromBase64url(base64urlStr) { + base64urlStr = base64urlStr.replace(/-/g, '+').replace(/_/g, '/'); + if (base64urlStr.length % 4 === 2) + return base64urlStr + '=='; + if (base64urlStr.length % 4 === 3) + return base64urlStr + '='; + return base64urlStr; + } + +} +``` +The next step is `decryptAESGCM()` which does the opposite of the `encryptAESGCM()` function described in [HowTo: encrypt](HowTo_encrypt.md). + +```javascript +async function passHash(pw) { + return cachedPassHash || (cachedPassHash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(pw))); +} + +function hexStringToUint8(str) { + return new Uint8Array(str.match(/.{2}/g).map(byte => parseInt(byte, 16))); +} + +async function makeKeyAESGCM(password, iv) { + const pwHash = await passHash(password); //small value generated by a hash function from a whole message + const alg = { name: 'AES-GCM', iv: iv }; // specify algorithm to use + return await crypto.subtle.importKey('raw', pwHash, alg, false, ['decrypt', 'encrypt']); //make crypto key +} + +async function decryptAESGCM(password, iv, ctStr) { + const key = await makeKeyAESGCM(password, iv); //make crypto key + const ctUint8 = new Uint8Array(ctStr.match(/[\s\S]/g).map(ch => ch.charCodeAt(0))); // ciphertext as Uint8Array + const plainBuffer = await crypto.subtle.decrypt({ name: key.algorithm.name, iv: iv }, key, ctUint8); // decrypt ciphertext using key + return new TextDecoder().decode(plainBuffer); // return the plaintext +} +``` + +# Reference + +* [MDN: atob()](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/atob)