CopyPastor

Detecting plagiarism made easy.

Score: 1; Reported for: Exact paragraph match Open both answers

Possible Plagiarism

Reposted on 2021-09-11
by Topaco

Original Post

Original - Posted on 2021-09-11
by Topaco



            
Present in both answers; Present only in the new answer; Present only in the old answer;

[GCM][1] uses an authentication tag that is handled separately by NodeJS/Crypto, while WebCrypto automatically concatenates it with the ciphertext.<br> Therefore, in the NodeJS code, the tag must be explicitly determined and appended to the ciphertext. This is missing in the current NodeJS code and can be taken into account as follows. Note the determination of the tag with [`cipher.getAuthTag()`][2] and its concatenation:
```js var crypto = require('crypto');
function encrypt(key, plaintext) { var nonce = getRandomIV(); var cipher = crypto.createCipheriv('aes-256-gcm', key, nonce); var nonceCiphertextTag = Buffer.concat([ nonce, cipher.update(plaintext), cipher.final(), cipher.getAuthTag() // Fix: Get tag with cipher.getAuthTag() and concatenate: nonce|ciphertext|tag ]); return nonceCiphertextTag.toString('base64'); }
function getRandomIV() { return crypto.randomBytes(12); }
var message = Buffer.from('The quick brown fox jumps over the lazy dog', 'utf8'); var sharedKey = Buffer.from('MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=', 'base64'); var ciphertext = encrypt(sharedKey, message); console.log(ciphertext); // wRE5KM6FG81QSMNvG0xR+iaIeF77cyyeBceGS5NkcYaD17K9nL0/helnqRBOkD9pLVoWM/nRAcaKg/YdvfNJcO1Zn/7ZM0k= ```
A possible output is
```none wRE5KM6FG81QSMNvG0xR+iaIeF77cyyeBceGS5NkcYaD17K9nL0/helnqRBOkD9pLVoWM/nRAcaKg/YdvfNJcO1Zn/7ZM0k= ```
The following code for decryption on the WebCrypto side is essentially based on your code (without the derivation of the key from a shared secret, which is irrelevant to the current problem):
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
(async () => {
var nonceCiphertextTag = base64ToArrayBuffer('wRE5KM6FG81QSMNvG0xR+iaIeF77cyyeBceGS5NkcYaD17K9nL0/helnqRBOkD9pLVoWM/nRAcaKg/YdvfNJcO1Zn/7ZM0k='); var nonceCiphertextTag = new Uint8Array(nonceCiphertextTag); var decrypted = await decrypt(nonceCiphertextTag); console.log(decrypted); // The quick brown fox jumps over the lazy dog })();
async function decrypt(nonceCiphertextTag) { const SERVER_ENCRYPTION_IV_LENGTH = 12; // For GCM a nonce length of 12 bytes is recommended! var nonce = nonceCiphertextTag.subarray(0, SERVER_ENCRYPTION_IV_LENGTH); var ciphertextTag = nonceCiphertextTag.subarray(SERVER_ENCRYPTION_IV_LENGTH);
var aesKey = base64ToArrayBuffer('MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE='); aesKey = await window.crypto.subtle.importKey('raw', aesKey, 'AES-GCM', true, ['encrypt', 'decrypt']); var decrypted = await crypto.subtle.decrypt({name: 'AES-GCM', iv: nonce}, aesKey, ciphertextTag); return new TextDecoder().decode(decrypted); }
// Helper
// https://stackoverflow.com/a/21797381/9014097 function base64ToArrayBuffer(base64) { var binary_string = window.atob(base64); var len = binary_string.length; var bytes = new Uint8Array(len); for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } return bytes.buffer; }

<!-- end snippet -->
with the output:
```none The quick brown fox jumps over the lazy dog ```
[1]: https://en.wikipedia.org/wiki/Galois/Counter_Mode [2]: https://nodejs.org/api/crypto.html#crypto_cipher_getauthtag
Since GCM is based on CTR, decryption with CTR is in principle also possible. However, this should generally not be done in practice, since it skips the authentication of the ciphertext, which is the added value of GCM over CTR. The correct way is to decrypt on the NodeJS side with GCM and properly consider the authentication tag.<br> The authentication tag is automatically appended to the ciphertext by the WebCrypto API, while the crypto module of NodeJS handles ciphertext and tag separately. Therefore, not only the nonce but also the authentication tag must be separated on the NodeJS side.
The following JavaScript/WebCrypto code demonstrates the encryption:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
(async () => { var nonce = crypto.getRandomValues(new Uint8Array(12));
var plaintext = 'The quick brown fox jumps over the lazy dog'; var plaintextEncoded = new TextEncoder().encode(plaintext);
var aesKey = base64ToArrayBuffer('a068Sk+PXECrysAIN+fEGDzMQ3xlpWgE1bWXHVLb0AQ='); var aesCryptoKey = await crypto.subtle.importKey('raw', aesKey, 'AES-GCM', true, ['encrypt', 'decrypt']); var ciphertextTag = await crypto.subtle.encrypt({name: 'AES-GCM', iv: nonce}, aesCryptoKey, plaintextEncoded); ciphertextTag = new Uint8Array(ciphertextTag); var nonceCiphertextTag = new Uint8Array(nonce.length + ciphertextTag.length); nonceCiphertextTag.set(nonce); nonceCiphertextTag.set(ciphertextTag, nonce.length); nonceCiphertextTag = arrayBufferToBase64(nonceCiphertextTag.buffer); document.getElementById("nonceCiphertextTag").innerHTML = nonceCiphertextTag; // ihAdhr6595oyQ3koj52cnZp7VeB1fzWuY1v7vqFdSQGxK0VQxIXUegB1mVG4rC5Aymij7bQ9rmnFWbpo7C2znN4ROnnChB0= })();
// Helper
// https://stackoverflow.com/a/9458996/9014097 function arrayBufferToBase64(buffer){ var binary = ''; var bytes = new Uint8Array(buffer); var len = bytes.byteLength; for (var i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); }
// https://stackoverflow.com/a/21797381/9014097 function base64ToArrayBuffer(base64) { var binary_string = window.atob(base64); var len = binary_string.length; var bytes = new Uint8Array(len); for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } return bytes.buffer; }
<!-- language: lang-html -->
<p style="font-family:'Courier New', monospace;" id="nonceCiphertextTag"></p>
<!-- end snippet -->
This code is basically the same as your code, with some changes needed because of methods you didn't post like `generateAesKey()` or `arrayBufferToBase64()`.
Example output:
```none ihAdhr6595oyQ3koj52cnZp7VeB1fzWuY1v7vqFdSQGxK0VQxIXUegB1mVG4rC5Aymij7bQ9rmnFWbpo7C2znN4ROnnChB0= ```
------------------
The following NodeJS/crypto code demonstrates the decryption. Note the tag separation and explicit passing with [`setAuthTag()`][2]:
```js var crypto = require('crypto');
function decrypt(key, nonceCiphertextTag) {
key = Buffer.from(key, 'base64'); nonceCiphertextTag = Buffer.from(nonceCiphertextTag, 'base64'); var nonce = nonceCiphertextTag.slice(0, 12); var ciphertext = nonceCiphertextTag.slice(12, -16); var tag = nonceCiphertextTag.slice(-16); // Separate tag! var decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce); decipher.setAuthTag(tag); // Set tag! var decrypted = decipher.update(ciphertext, '', 'utf8') + decipher.final('utf8');
return decrypted; }
var nonceCiphertextTag = 'ihAdhr6595oyQ3koj52cnZp7VeB1fzWuY1v7vqFdSQGxK0VQxIXUegB1mVG4rC5Aymij7bQ9rmnFWbpo7C2znN4ROnnChB0='; var key = 'a068Sk+PXECrysAIN+fEGDzMQ3xlpWgE1bWXHVLb0AQ='; var decrypted = decrypt(key, nonceCiphertextTag); console.log(decrypted); ```
Output:
```none The quick brown fox jumps over the lazy dog ```
-----------
For completeness: Decryption of a GCM ciphertext with CTR is also possible by appending 4 bytes to the 12 bytes nonce (0x00000002). For other nonce sizes the relation is more complex, see e.g. [*Relationship between AES GCM and AES CTR*][1]. However, as already said, this should not be done in practice, since it bypasses the authentication of the ciphertext and is thus insecure.
[1]: https://crypto.stackexchange.com/questions/44764/relationship-between-aes-gcm-and-aes-ctr [2]: https://nodejs.org/api/crypto.html#crypto_decipher_setauthtag_buffer_encoding

        
Present in both answers; Present only in the new answer; Present only in the old answer;