elementary/website

Allow verification of the OS download via the website itself

Opened this issue · 4 comments

Expected behavior

I wonder if it would help more non-technical users to actually verify their downloads instead of skipping this step due to complexity (using terminal application).

There is a beautiful working example at the Tails project website.

https://tails.boum.org/install/linux/index.en.html
(see here step 3 "Verify your download")

Actual behavior

At the moment, the installation guide still advises to check through the terminal.
look at https://elementary.io/docs/installation/#verify-your-download

Do you have any feelings and thoughts about this?

That's very cool, I like that idea a lot!

It's a little slow since it's running in the browser under JavaScript, but definitely way less of a technical barrier.

I guess if someone was to propose a design of the most user friendly way to fit this into the elementary download flow, I'd be willing to have a go at implementing it.

From #3189 (comment)

there's no way to do chunking on the hash creation

This is correct when referring to the SubtleCrypto API. Using a different JavaScript library would allow you to perform "updates" using the chunks as long as they are in sequential order. It could also bring down the space complexity since it looks like you're currently loading the entire image into memory before validating the hash.

From #3179 (comment)

It's a little slow since it's running in the browser under JavaScript

Would it be worth discussing a move to WASM for faster results? I think JavaScript is the winner here since it lessens the number of languages in the code-base, but I think it's worth discussing since this could bring the verification time down significantly.

edit:

I just finished benchmarking a few different methods. I think @RMcNeely's method may be best, even without being able to do an accurate progress bar. This was done on an i5 with 32GB of ram. Perhaps some others could benchmark and see if the size of the image will cause issues:

method time (s)
RMcNeely 4.184
shasum -a 256 6.68
tails 50.918

Personally, I never like to add a library that would need to be maintained when you have a capable alternative in Web API's. I don't think that configuring WASM for just this is going to make an incredible difference.

For the benchmarking when you say shasum are you just running that locally on your computer? And is tails the time it took the tails to check an example file from the first post?

@RMcNeely, I guess I should have benchmarked with the elementary file originally, because I just got this error using your method:
image
Elementary is just under 3GB. Here's the updated benchmark results with the elementary image:

method time (s)
RMcNeely Error
shasum -a 256 14.30
tails 104.119

Yes, that's the shasum that comes with MacOS. So it's faster to run your version in Javascript than it is to run the compiled machine code version on the local workstation.

time shasum -a 256 <filename>

Yes, I extracted the pertinent code from this page: https://tails.boum.org/install/linux/index.en.html and added a little timer outside of the file read/hash steps

Benchmarked tails code

    const start = new Date();

    try {
      sha256=forge.md.sha256.create();
      await readFile(file);
      var fileactualchecksum = sha256.digest().toHex();
    } catch(err) {
      showVerificationResult("error-image");
      return;
    }

    const end = new Date();
    const seconds = (end - start) / 1000;
    console.log("Hash found using tails method in ", seconds, "s");

Benchmarked RMcNeely's code

        const start = new Date();
        const file = this.files[0];

        console.log('got file!')
        console.log(file)
        window.file = file;
        const Reader = new FileReader(file)
        const buf = await file.arrayBuffer();
        // const buf = Reader.readAsArrayBuffer(file)

        console.log(buf)

        const hash = await crypto.subtle.digest('SHA-256', buf)
        
        const uintHash = new Uint8Array(hash);
        
        // Map the new intHash to Hex to be displayed as a string value
        const stringifiedHash = Array.from(uintHash).map((b) => b.toString(16).padStart(2, '0')).join('');
        
        document.querySelector('#hash').innerText = stringifiedHash;

        const end = new Date();
        const seconds = (end - start) / 1000;
        console.log("Hash found using RMcNeely method in ", seconds, "s");