4

These work:

crypto.subtle.digest('SHA-512', new Uint8Array([0]))
crypto.subtle.digest('SHA-512', new Uint8Array([0]).buffer)

These don't:

crypto.subtle.digest('SHA-512', new Proxy(new Uint8Array([0]),{}))
crypto.subtle.digest('SHA-512', new Proxy(new Uint8Array([0]).buffer,{})

Error:

Failed to execute 'digest' on 'SubtleCrypto': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'

instanceof Uint8Array and instanceof ArrayBuffer return true in both cases.

Mihail Malostanidis
  • 2,686
  • 3
  • 22
  • 38
  • 1
    Looks like the native `crypto.subtle.digest` function needs a real typed array, not a proxy. Even if they're otherwise indistinguishable to javascript. – Bergi Oct 06 '17 at 15:34
  • 1
    What are you trying to do, why do you want to use a proxy here? – Bergi Oct 06 '17 at 15:34
  • @Bergi, I want to pass it a file in slices by hijacking whatever access method it uses. Loading an ArrayBuffer of 1.2GB or more crashes the tab. – Mihail Malostanidis Oct 06 '17 at 15:46
  • 1
    You can perfectly slice a file (Blob or ArrayBuffer) without using a proxy? Though I'm not sure how you meant to use the proxy to lower memory usage anyway. – Bergi Oct 06 '17 at 15:49
  • Native crypto doesn't support `.update(data)` for hashing. You give it one `ArrayBuffer` and it returns a promise for an `ArrayBuffer` of the hash. End of story. – Mihail Malostanidis Oct 06 '17 at 15:51
  • That doesn't really answer my question about how you intended to use a proxy to emulate `update`, though. – Bergi Oct 06 '17 at 15:54
  • I was planning to give it the first slice, then, when it reaches the end, dispose of it and start feeding the next slice, and so on. I have pretty much accepted this as impossible right now, it clearly wants the buffer for its backend implementation and not for its JavaScript API. – Mihail Malostanidis Oct 06 '17 at 16:04
  • IIRC, all methods load a slice of a file representation that didn't already allocate the data into a buffer are asynchronous, so I don't think this would have worked anyway. – Bergi Oct 06 '17 at 16:08
  • What would proxying ArrayBuffer give you? It doesn't look like you can intercept gets and sets the underlying data, since there's no ability to get or set from an ArrayBuffer originally. – CMCDragonkai Nov 08 '17 at 09:13

1 Answers1

2

digest is specified by its IDL interface to only accept a BufferSource, which is either an ArrayBufferView or an ArrayBuffer. This IDL-level typing indicates that a correct implementation will categorically reject any inputs that doesn't have the correct internal type.

Any tricks you might want to use a Proxy for simply aren't going to work on digest. Instead, you could do proxy tricks to get the exact ArrayBuffer you want immediately before you pass in your data to digest.

For example, here's a proxy that fakes a buffer that differs from the buffer on its internal object. The buffer is genuine, so it can be passed into digest, but it was created by Proxy magic:

var proxy = new Proxy(new Uint8Array([0]), {
                          get:function(obj, prop) {
                              if(prop=="buffer"){ return new Uint8Array([42]).buffer }
                              else { return obj[prop]; }
                          }
            });
crypto.subtle.digest('SHA-512', proxy.buffer)

If it is impossible to produce the buffer (for example if it is too big to fit in RAM) you would currently have to rely on something other than SubtleCrypto.

This seems like a great point to raise with the W3C, e.g., to support an update mechanism to iteratively collect input.

apsillers
  • 112,806
  • 17
  • 235
  • 239
  • The ArrayBuffer I want is illegal. Because it is too big. – Mihail Malostanidis Oct 06 '17 at 16:00
  • @MihailMalostanidis You may be out of luck, then. You might need to see if you can use a non-native implementation that supports incremental updates. This seems like a great point to raise with the W3C. Also, for certain hashes, it may be possible to do an incremental update (e.g., [this Crypto.SE post](https://crypto.stackexchange.com/a/9195/1643) hints that you can write some combining function `F` yourself such that `F(Hash(s1), s2) == Hash(s1 + s2)` for SHA-1, -2, and -3), but you'd need to consult with a hash expert to create such a function, if it exists for a given hash algorithm. – apsillers Oct 06 '17 at 16:11
  • @MihailMalostanidis So it looks such a function `F(oldHash, newInput)` is possible for SHA-1 and SHA-2 family functions (including 512), but it is not possible for SHA-3 family functions. See https://en.wikipedia.org/wiki/Length_extension_attack for how to start implementing such a function. – apsillers Oct 06 '17 at 16:33
  • I was thinking of doing it this way (`H(F(H(s1),s2`) before going to pure js solutions. However, even it can defeat one iteration's padding, having to load state into the SHA-512 machine means the native function won't work once more. – Mihail Malostanidis Oct 06 '17 at 17:57
  • Perhaps I should look into doing it in wasm, it's supported on mobile now (which is where the pure js hurt the most) – Mihail Malostanidis Oct 06 '17 at 17:59