3

I am attempting to fetch encrypted raw buffer data (AES-256) from Arweave, pass to a decrypt function and use this to display an image. I am trying to fetch and decrypt the ArrayBuffer on the front end (in my React app).

First, I am encrypting the Buffer data in NodeJS and storing the file. Here is the code for it:

/**********************
 **  Runs in NodeJS  **
 **********************/

const encrypt = (dataBuffer, key) => {
    // Create an initialization vector
    const iv = crypto.randomBytes(IV_LENGTH);
    // Create cipherKey
    const cipherKey = Buffer.from(key);
    // Create cipher
    const cipher = crypto.createCipheriv(ALGORITHM, cipherKey, iv);

    const encryptedBuffer = Buffer.concat([
        cipher.update(dataBuffer),
        cipher.final(),
    ]);
    const authTag = cipher.getAuthTag();
    let bufferLength = Buffer.alloc(1);
    bufferLength.writeUInt8(iv.length, 0);

    return Buffer.concat([bufferLength, iv, authTag, encryptedBuffer]);
};

const encryptedData = encrypt(data, key)

fs.writeFile("encrypted_data.enc", encryptedData, (err) => {
    if(err){
        return console.log(err)
    }
});

Next, I try to fetch and decrypt on the front-end. What I have so far returns an ArrayBuffer from the response. I try to pass this ArrayBuffer to the decrypt function. Here is the code:

/***********************
 **  Runs in React  **
 ***********************/
 import crypto from "crypto"

const getData = async (key) => {
  const result = await (await fetch('https://arweave.net/u_RwmA8gP0DIEeTBo3pOQTJ20LH2UEtT6LWjpLidOx0/encrypted_data.enc')).arrayBuffer()
  const decryptedBuffer = decrypt(result, key)
  console.log(decryptedBuffer)
}

//  Here is the decrypt function I am passing the ArrayBuffer to:
export const decrypt = (dataBuffer, key) => {
    // Create cipherKey
    const cipherKey = Buffer.from(key);
    // Get iv and its size
    const ivSize = dataBuffer.readUInt8(0);
    const iv = dataBuffer.slice(1, ivSize + 1);
    // Get authTag - is default 16 bytes in AES-GCM
    const authTag = dataBuffer.slice(ivSize + 1, ivSize + 17);

    // Create decipher
    const decipher = crypto.createDecipheriv("aes-256-gcm", cipherKey, iv);
    decipher.setAuthTag(authTag);

    return Buffer.concat([
        decipher.update(dataBuffer.slice(ivSize + 17)),
        decipher.final(),
    ]);
};

When I pass the ArrayBuffer data to the decrypt function, I get this error:

Unhandled Rejection (TypeError): First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.
  • The purpose is to encrypt raw buffer data from an image, fetch the encrypted buffer data and decrypt it. Then, I want to convert this buffer to base64 data to display the image. – anonymoususer8893 Dec 07 '21 at 16:34
  • 2
    I recommend that you cut and paste the exact text of any errors you encountered rather than paraphrasing. – Wyck Dec 07 '21 at 16:54
  • Thank you @Wyck, I will do that now. – anonymoususer8893 Dec 07 '21 at 16:56
  • 3
    And what is the type of the thing that you passed instead of a _string, Buffer, ArrayBuffer, Array, or array-like object_? – Wyck Dec 07 '21 at 17:02
  • For the first variation of code, in console it says: Uint8Array. Console logging the typeof says it's an object. For the second variation of code, console says ArrayBuffer, console logging typeof says it's an object as well. I tried to access the buffer of the "object" by doing buffer_data.buffer, still the same error and it also says it is an object. – anonymoususer8893 Dec 07 '21 at 17:03
  • I have added an update with another attempt using fetch(). Still no luck. – anonymoususer8893 Dec 07 '21 at 21:46
  • 1
    What's the issue? You never supply the error. I'm not sure why this questions is getting up votes. – wahwahwah Dec 07 '21 at 21:52
  • The error is in the question @wahwahwah . It is within the first few lines of the question. – anonymoususer8893 Dec 07 '21 at 22:00
  • 2
    @wahwahwah The error is quoted verbatim in the first literal block of the question. – Wyck Dec 07 '21 at 22:00
  • `get()` is not defined. Please include its function declaration. – anthumchris Dec 08 '21 at 00:34
  • @AnthumChris thank you for pointing that out! I thought I had pasted the get function above it. I updated the question with it. As well, I attached the fetch() version which was my initial attempt. The get() function was my second attempt, both give the same error. – anonymoususer8893 Dec 08 '21 at 00:49
  • Your `get()` function doesn't work in NodeJS. `ReferenceError: XMLHttpRequest is not defined`. If you are running in a browser, use `fetch()` as I exemplified below. – anthumchris Dec 08 '21 at 01:01
  • Hello, yes I am only using NodeJS to encrypt and store the encrypted file in my webserver. Then I am using fetch() and decryption on the client-side. I have implemented the same way you are fetching (I think), and received the same error. I will update my question with the exact code – anonymoususer8893 Dec 08 '21 at 01:06
  • This question is very disorganized and difficult to follow. Please ensure that everything is provided in a well-organized fashion. Seems myself and the other folks trying to help are very confused. – anthumchris Dec 08 '21 at 01:12
  • If you have any suggestions as to how I can organize it, please let me know. – anonymoususer8893 Dec 08 '21 at 01:13
  • 2
    provide all of your code. Don't omit things. identify what runs in node and what runs in browser. – anthumchris Dec 08 '21 at 01:16
  • @AnthumChris thank you, I believe I have done that now. – anonymoususer8893 Dec 08 '21 at 01:20
  • Your browser code does not run in a browser. `Buffer` and `crypto.createDecipheriv` are not global/public objects, nor did you define them elsewhere. You're still omitting things. – anthumchris Dec 08 '21 at 01:30
  • Apologies, I added the import for crypto. Buffer is a method available in Node.js. I specified in the beginning that the front-end code is being used in a React app. – anonymoususer8893 Dec 08 '21 at 01:34

1 Answers1

2

You're omitting a lot of details that would help the community understand how you're encrypting the image, how you're retrieving it, and how you're decrypting it. Here's a full example of fetching an image, encrypting it, decrypting it, and displaying it in the browser. This runs in Chrome v96 and Firefox v95.

(async () => {
  const encryptionAlgoName = 'AES-GCM'
  const encryptionAlgo = {
      name: encryptionAlgoName,
      iv: window.crypto.getRandomValues(new Uint8Array(12)) // 96-bit
  }
  
  // create a 256-bit AES encryption key
  const encryptionKey = await crypto.subtle.importKey(
    'raw',
    new Uint32Array([1,2,3,4,5,6,7,8]),
    { name: encryptionAlgoName },
    true,
    ["encrypt", "decrypt"],
  )

  // fetch a JPEG image
  const imgBufferOrig = await (await fetch('https://fetch-progress.anthum.com/images/sunrise-baseline.jpg')).arrayBuffer()

  // encrypt the image
  const imgBufferEncrypted = await crypto.subtle.encrypt(
    encryptionAlgo,
    encryptionKey,
    imgBufferOrig
  )

  // decrypt recently-encrypted image
  const imgBufferDecrypted = await crypto.subtle.decrypt(
    encryptionAlgo, 
    encryptionKey,
    imgBufferEncrypted
  )

  // display unencrypted image
  const img = document.createElement('img')
  img.style.maxWidth = '100%'
  img.src = URL.createObjectURL(
    new Blob([ imgBufferDecrypted ])
  )
  document.body.append(img)    
})()
anthumchris
  • 8,245
  • 2
  • 28
  • 53
  • Thank you for your response. I genuinely appreciate your help. I am sorry that I did not give the full details, I was trying to not make it overly lengthy so I erred on the side of caution. I now realize that was the wrong way to ask for help, as it made it difficult to help with my specific error without the context. I have run your code and it is working exactly as expected. I have attached to my original question some code showing how I am encrypting and storing the Buffer data from the image. I am writing to my server's filesystem and fetching the data on the front-end to decrypt. – anonymoususer8893 Dec 08 '21 at 00:27