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.
19
  • 2
    I recommend that you cut and paste the exact text of any errors you encountered rather than paraphrasing. Commented Dec 7, 2021 at 16:54
  • 3
    And what is the type of the thing that you passed instead of a string, Buffer, ArrayBuffer, Array, or array-like object? Commented Dec 7, 2021 at 17:02
  • 1
    What's the issue? You never supply the error. I'm not sure why this questions is getting up votes. Commented Dec 7, 2021 at 21:52
  • 2
    @wahwahwah The error is quoted verbatim in the first literal block of the question. Commented Dec 7, 2021 at 22:00
  • 2
    provide all of your code. Don't omit things. identify what runs in node and what runs in browser. Commented Dec 8, 2021 at 1:16

1 Answer 1

3

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)    
})()

Sign up to request clarification or add additional context in comments.

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.