【问题标题】:Convert number to ArrayBuffer将数字转换为 ArrayBuffer
【发布时间】:2021-06-27 03:41:44
【问题描述】:

我正在尝试使用 AES-CTR 算法解密浏览器上的数据。 WebCrypto API 要求将计数器作为BufferSource 传递。如何将计数器(一个数字)转换为预期的输入(一个字节数组)?

我使用的是全零 IV,因此计数器从 0 开始。假设我正在尝试解密计数器 = 445566 的数据。如何将 445566 转换为 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)

编辑:在挖掘了一些加密库之后,我最终使用了这个。它似乎做我想做的事,但不知道正确性、性能等。

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
}

【问题讨论】:

  • 加密期间如何创建整数计数器?
  • 像这样,在节点中:const iv = Buffer.alloc(16)。有人告诉我,如果密钥仅用于加密单个消息,则可以使用全零 iv。
  • 在客户端,我希望解密给定字节范围的数据,例如 243-695。所以我需要从字节范围的第一个值中派生一个计数器 ArrayBuffer。

标签: javascript cryptography aes webcrypto-api


【解决方案1】:

IE10+ 有一个单线解决方案:)

new Int32Array([n]).buffer

【讨论】:

    【解决方案2】:

    这是我用的:

    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
    }
    

    【讨论】:

      【解决方案3】:

      不确定这是否有帮助。但只是为了交叉检查代码的真实性。

      我写了一些测试用例,首先将数字转换为数组缓冲区并使用数组缓冲区值将其解码为相同。

      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]
      

      两个结果都是一样的。只是您的代码以大端格式生成结果,而我的代码以小端格式生成结果。

      所以你遵循的方法是正确的。至于性能,我认为影响不大

      【讨论】:

        【解决方案4】:

        你不需要做任何事情,ArrayBuffer 被初始化为所有值为零的字节,这当然也表示使用静态无符号大端编码的数字值零 - 这是表示的常用方式CTR 模式下的计数器。

        这里是ArrayBuffer的返回值的说明,只是指定了大小:

        指定大小的新ArrayBuffer 对象。它的内容被初始化为0。

        所以你去了,你在不知不觉中已经准备好了。我唯一肯定会做的就是评论缓冲区中的计数器已经为下一个开发者初始化为零(实际上可能是几年后的你)。

        请注意,如果计数器从不针对同一个键重复,则 CTR 模式是安全的。为此,nonce(在左手,最重要的一侧获取未知数量的字节)通常设置为随机值,通常只需将前 8 个字节初始化为随机值。所以你也不需要为此做任何数字编码。

        如果随机数本身由 32 位或 64 位计数器组成,则您只需要对其进行编码。

        【讨论】:

          【解决方案5】:
          const uIntToBytes = (num, size, method) => {
             const arr = new ArrayBuffer(size)
             const view = new DataView(arr)
             view[method + (size * 8)](0, num)
             return arr
          }
          
          const toBytes = (data, type) =>
             type == "u8"  ? uIntToBytes(data, 1, "setUint") :
             type == "u16" ? uIntToBytes(data, 2, "setUint") :
             type == "u32" ? uIntToBytes(data, 4, "setUint") :
             type == "u64" ? uIntToBytes(BigInt(data), 8, "setBigUint")
                           : `Not Sure about type - ${type}`
          

          用法:
          toBytes(5, "u8")

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-03-14
            • 2011-11-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-18
            • 2021-06-03
            • 2018-11-03
            相关资源
            最近更新 更多