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);