CCM 模式


【CCM mode】

CCM 是支持的 AEAD 算法 之一。使用此模式的应用在使用加密 API 时必须遵守某些限制:

【CCM is one of the supported AEAD algorithms. Applications which use this mode must adhere to certain restrictions when using the cipher API:】

  • 在创建密码时必须通过设置 authTagLength 选项来指定身份验证标签的长度,并且长度必须为 4、6、8、10、12、14 或 16 字节之一。
  • 初始化向量(随机数)N 的长度必须在 7 到 13 字节之间(7 ≤ N ≤ 13)。
  • 明文的长度限制为 2 ** (8 * (15 - N)) 字节。
  • 在解密时,必须通过 setAuthTag() 设置认证标签,然后才能调用 update()。否则,解密将失败,并且 final() 会抛出错误,这符合 RFC 3610 第 2.6 节的规定。
  • 在 CCM 模式下使用诸如 write(data)end(data)pipe() 等流方法可能会失败,因为 CCM 无法在每个实例中处理多个数据块。
  • 在传递附加认证数据(AAD)时,必须通过 plaintextLength 选项将实际消息的字节长度传递给 setAAD()。许多加密库会在密文中包含认证标签,这意味着它们生成的密文长度为 plaintextLength + authTagLength。Node.js 不包含认证标签,因此密文长度始终为 plaintextLength。如果不使用 AAD,这一步不是必需的。
  • 由于 CCM 会一次性处理整个消息,因此 update() 必须且只能调用一次。
  • 尽管调用 update() 足以加密/解密消息,应用 必须 调用 final() 来计算或验证认证标签。
import { Buffer } from 'node:buffer';
const {
  createCipheriv,
  createDecipheriv,
  randomBytes,
} = await import('node:crypto');

const key = 'keykeykeykeykeykeykeykey';
const nonce = randomBytes(12);

const aad = Buffer.from('0123456789', 'hex');

const cipher = createCipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
const plaintext = 'Hello world';
cipher.setAAD(aad, {
  plaintextLength: Buffer.byteLength(plaintext),
});
const ciphertext = cipher.update(plaintext, 'utf8');
cipher.final();
const tag = cipher.getAuthTag();

// Now transmit { ciphertext, nonce, tag }.

const decipher = createDecipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
decipher.setAuthTag(tag);
decipher.setAAD(aad, {
  plaintextLength: ciphertext.length,
});
const receivedPlaintext = decipher.update(ciphertext, null, 'utf8');

try {
  decipher.final();
} catch (err) {
  throw new Error('Authentication failed!', { cause: err });
}

console.log(receivedPlaintext);const { Buffer } = require('node:buffer');
const {
  createCipheriv,
  createDecipheriv,
  randomBytes,
} = require('node:crypto');

const key = 'keykeykeykeykeykeykeykey';
const nonce = randomBytes(12);

const aad = Buffer.from('0123456789', 'hex');

const cipher = createCipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
const plaintext = 'Hello world';
cipher.setAAD(aad, {
  plaintextLength: Buffer.byteLength(plaintext),
});
const ciphertext = cipher.update(plaintext, 'utf8');
cipher.final();
const tag = cipher.getAuthTag();

// Now transmit { ciphertext, nonce, tag }.

const decipher = createDecipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
decipher.setAuthTag(tag);
decipher.setAAD(aad, {
  plaintextLength: ciphertext.length,
});
const receivedPlaintext = decipher.update(ciphertext, null, 'utf8');

try {
  decipher.final();
} catch (err) {
  throw new Error('Authentication failed!', { cause: err });
}

console.log(receivedPlaintext);