trouble obtaining the same hash of a file on server as on the client

218 Views Asked by At

I would like to obtain the same hash of a file on server as on the client.

I tried:

  1. creating a form to upload
  2. computing the hash with node-forge client-side
  3. computing the hash on Linux command-line
  4. uploading the file through the form
  5. grabbing the file server-side as a blob
  6. reading contents with .text() server-side
  7. computing the hash server-side with node-forge

I am stuck at 7.

So, first I upload a file through a form

<form method="POST" action="?/upload" enctype="multipart/form-data">
        <input
            id="csvFile"
            name="csvFile"
            type="file"
            on:change={handleChange}
        />
    <button type="submit" class="btn">Upload</button>
</form>

and I compute the hash of a file in the browser with node-forge:

function handleChange(event) {
        let files = event.target.files;
        const file = files[0]
                reader = new FileReader();
                reader.onload = function (event) {
                var binary = event.target.result;
            var md = forge.md.sha256.create();
            sha256 = md.update(binary).digest().toHex();
        };
                reader.readAsBinaryString(f);
}

This computes a hash that is the same as when I execute it from the command line with sha256sum, so it is reliable.

However, when I upload the file and process it on SvelteKit, I am unable to obtain the same hash. I am thinking this is because the filename or something else is missing from the resulting blob compared to what is available in the browser.

export const actions = {
    upload: async ({ request }) => {
        const formData = await request.formData()
        const file = await formData.get(`csvFile`) as File;
        const contents = await file.text()

        const md = forge.md.sha256.create();
        md.update(contents);
        const sha256 = md.digest().toHex()
    }
}

I cannot use new FileReader() here because I am in a Node environment.

How can I create the exact same hash server-side and client-side? Do I need to add the filename or something to obtain the same hash as from the command line?

Thanks in advance!

2

There are 2 best solutions below

7
brunnerh On

Reading as text implies some encoding. The best approach is probably to read the data as simple array buffers in both cases to be consistent.

You can use the asynchronous arrayBuffer() function on the file in both cases. (If you want to target older browsers, you can also use FileReader.readAsArrayBuffer() on the client.)

In both cases you can convert the data to a hash-able string, e.g. by converting the individual bytes and joining them:

const content = [...new Uint8Array(fileArrayBuffer)]
    .map(b => String.fromCharCode(b))
    .join('');
2
sryscad On

The SvelteKit POST page endpoint had to do a mapping:

File (Blob) -> ArrayBuffer (raw binary data) -> UInt8Array -> Utf-16 Array -> String

async upload({ request }) {
        const formData = await request.formData();
        const blob = formData.get('csvFile');
        const ab = await blob.arrayBuffer()
        const contents = [...new Uint8Array(ab)]
            .map((b) => String.fromCharCode(b))
            .join('');
        const md = forge.md.sha256.create();
        md.update(contents);
        const sha256 = md.digest().toHex()

Thanks to @H.B. for the hints.