问题
I'm trying to decrypt data on the browser using the AES-CTR algo. The WebCrypto API requires the counter to be passed as a BufferSource. How do I convert the counter (a number) to the expected input (a byte array)?
I'm using an all zero IV, so the counter starts at 0. Let's say I'm trying to decrypt data where counter = 445566. How do I convert 445566 into an ArrayBuffer?
const key = // retrieve decryption key
const encrypted = // retrieve encrypted data
const iv = new ArrayBuffer(16)
// iv is all zeros. I need it to represent 445566, how?
const algo = {
name: 'AES-CTR',
counter: iv,
length: 128
}
const decrypted = await crypto.subtle.decrypt(algo, key, encrypted)
EDIT: After digging around some crypto libraries, I ended up using this. It seems to do what I want, but no idea on correctness, performance, etc.
function numberToArrayBuffer(value) {
const view = new DataView(new ArrayBuffer(16))
for (var index = 15; index >= 0; --index) {
view.setUint8(index, value % 256)
value = value >> 8;
}
return view.buffer
}
回答1:
Not sure if this is helpful. But just to cross check the authenticity of the code.
I wrote some test case to first convert the number to array buffer and use the array buffer value to decode it to the same.
var hex = 445566..toString(16);
var buffer = new ArrayBuffer(16);
var dataView = new DataView(buffer);
dataView.setInt32(0, '0x'+ hex);
console.log(dataView.getInt32(0)); //445566
//Using the uint16array data generated by the above code
var data = [0, 6, 204, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var buf = new ArrayBuffer(16);
var view = new DataView(buf);
data.forEach(function (b, i) {
view.setUint8(i, b % 256);
});
var num = view.getInt32(0);
console.log(num);//445566
function numberToArrayBuffer(value) {
const view = new DataView(new ArrayBuffer(16))
for (var index = 15; index >= 0; --index) {
view.setUint8(index, value % 256)
value = value >> 8;
}
return view.buffer
}
console.log(numberToArrayBuffer(445566)) // Uint8Array(16) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 204, 126]
Both the results are same. It's just that your code is producing the result in big endian format and mine in little endian.
So the approach you have followed is correct. As for the performance i don't think there is much impact
回答2:
You don't need to do anything, the ArrayBuffer
is initialized to bytes all with value zero, which of course also represents the number value zero using in statically unsigned, big endian encoding - which is the common way of representing the counter in CTR mode.
Here is the description of the return value of ArrayBuffer
with just a specified size:
A new
ArrayBuffer
object of the specified size. Its contents are initialized to 0.
so there you go, you were ready without knowing it. The only thing I would certainly do is to comment that the counter in the buffer has been initialized to zero for the next developer (which may actually be you in a couple of years).
Note that CTR mode is secure if the counter is never repeated for the same key. For this, the nonce (which takes an unknown number of bytes to the left hand, most significant side) is commonly set to a random value, usually by simply initializing the first 8 bytes to random values. So you don't need to do any number encoding for that either.
You only need to encode the nonce if it consists of a 32 or 64 bit counter itself.
回答3:
This is what I use:
const bytesArray = (n) => {
if (!n) return new ArrayBuffer(0)
const a = []
a.unshift(n & 255)
while (n >= 256) {
n = n >>> 8
a.unshift(n & 255)
}
return new Uint8Array(a).buffer
}
来源:https://stackoverflow.com/questions/55583037/convert-number-to-arraybuffer