C# 如何复制蓝牙&x27;NET中的CCM方案?
我正在研究一个固件更新方案,该方案需要对固件映像进行端到端加密。目标设备为蓝牙低能芯片,硬件支持Blueooth规范AES-CCM中规定的加密。我们希望利用这个硬件来最小化代码大小和速度,因此我们需要以构建硬件的格式加密固件映像 所以,我尝试使用.NET,这样我就可以重现(p1547)中给出的数据样本,但是我没有得到相同的输出。以下是示例数据: 有效负载字节长度:08C# 如何复制蓝牙&x27;NET中的CCM方案?,c#,.net,ecb,C#,.net,Ecb,我正在研究一个固件更新方案,该方案需要对固件映像进行端到端加密。目标设备为蓝牙低能芯片,硬件支持Blueooth规范AES-CCM中规定的加密。我们希望利用这个硬件来最小化代码大小和速度,因此我们需要以构建硬件的格式加密固件映像 所以,我尝试使用.NET,这样我就可以重现(p1547)中给出的数据样本,但是我没有得到相同的输出。以下是示例数据: 有效负载字节长度:08 K:89678967 89678967 45234523 45234523 有效负载计数器:0000bc614e 零长度ACL-
K:89678967 89678967 45234523 45234523
有效负载计数器:0000bc614e
零长度ACL-U延续:0
方向:0
初始化向量:66778899 aabbccdd
地址:1
数据包类型:3
LLID:2
有效载荷:68696a6b 6c6d6e6f B0:494e61bc 0000ddcc bbaa9988 77660008
B1:001902000000000000
B2:68696a6b 6c6d6e6f 00000000 00000000 Y0:95ddc3d4 2c9a70f1 61a28ee2 c08271ab
Y1:418635ff 54615443 8aceca41 fe274779
Y2:08d78b32 9d78ed33 b285fc42 e178d781 T:08d78b32 CTR0:014e61bc 0000ddcc bbaa9988 77660000
CTR1:014e61bc 0000ddcc bbaa9988 77660001 S0:b90f2b23 f63717d3 38e0559d 1e7e785e
S1:d8c7e3e1 02050abb 025d0895 17cbe5fb 麦克风:b1d8a011
加密有效负载:b0ae898a 6e6864d4 现在,我很高兴能够在没有身份验证的情况下实现加密。我注意到MIC和加密负载分别与S0和S1进行T和负载异或,所以我的目标只是生成S0。我的理解是,我应该能够通过使用K键ECB'ing CTR0数组来实现这一点:
//I've tried a few endian-ness permutations of K, none work
byte[] sampleKey = { 0x23, 0x45, 0x23, 0x45, 0x23, 0x45, 0x23, 0x45,
0x67, 0x89, 0x67, 0x89, 0x67, 0x89, 0x67, 0x89};
byte[] sampleCtr0 = { 01, 0x4e, 0x61, 0xbc, 00, 00, 0xdd, 0xcc,
0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 00, 00 };
byte[] encrypted;
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Mode = CipherMode.ECB; //CTR implemented as ECB w/ manually-incrementing counter
// Create an encrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(sampleKey, zeros); //zeros is a byte array of 16 0's
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(sampleCtr0);
}
encrypted = msEncrypt.ToArray();
}
}
}
我希望在加密中看到S0,但我没有。出什么问题了?原来问题出在StreamWriter的使用上。删除它并用csEncrypt.Write()替换后,我得到了预期的输出 我仍然不太理解我的修正,所以我正要编辑这个问题,但鉴于这个问题可能与密码学无关,我认为最好作为一个单独的问题来解决。或者,如果有人能解释修复方法,我会将已接受的答案改为
编辑:Dark Falcon找到了。复制前,流或其源流可能需要flush()。否则,如果未完成,它可能会截断末端。以下是我的AES-CCM推进,与C#2.0兼容: 请注意,需要一些可用的字节操作类(例如XOR):
/* Copyright (C) 2020 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.IO;
using System.Security.Cryptography;
namespace Utilities
{
/// <summary>
/// Implements the Counter with CBC-MAC (CCM) detailed in RFC 3610
/// </summary>
public static class AesCcm
{
private static byte[] CalculateMac(byte[] key, byte[] nonce, byte[] data, byte[] associatedData, int signatureLength)
{
byte[] messageToAuthenticate = BuildB0Block(nonce, true, signatureLength, data.Length);
if (associatedData.Length > 0)
{
if (associatedData.Length >= 65280)
{
throw new NotSupportedException("Associated data length of 65280 or more is not supported");
}
byte[] associatedDataLength = BigEndianConverter.GetBytes((ushort)associatedData.Length);
messageToAuthenticate = ByteUtils.Concatenate(messageToAuthenticate, associatedDataLength);
messageToAuthenticate = ByteUtils.Concatenate(messageToAuthenticate, associatedData);
int associatedDataPaddingLength = (16 - (messageToAuthenticate.Length % 16)) % 16;
messageToAuthenticate = ByteUtils.Concatenate(messageToAuthenticate, new byte[associatedDataPaddingLength]);
}
messageToAuthenticate = ByteUtils.Concatenate(messageToAuthenticate, data);
int dataPaddingLength = (16 - (messageToAuthenticate.Length % 16)) % 16;
messageToAuthenticate = ByteUtils.Concatenate(messageToAuthenticate, new byte[dataPaddingLength]);
byte[] encrypted = AesEncrypt(key, new byte[16], messageToAuthenticate, CipherMode.CBC);
return ByteReader.ReadBytes(encrypted, messageToAuthenticate.Length - 16, signatureLength);
}
public static byte[] Encrypt(byte[] key, byte[] nonce, byte[] data, byte[] associatedData, int signatureLength, out byte[] signature)
{
if (nonce.Length < 7 || nonce.Length > 13)
{
throw new ArgumentException("nonce length must be between 7 and 13 bytes");
}
if (signatureLength < 4 || signatureLength > 16 || (signatureLength % 2 == 1))
{
throw new ArgumentException("signature length must be an even number between 4 and 16 bytes");
}
byte[] keyStream = BuildKeyStream(key, nonce, data.Length);
byte[] mac = CalculateMac(key, nonce, data, associatedData, signatureLength);
signature = ByteUtils.XOR(keyStream, 0, mac, 0, mac.Length);
return ByteUtils.XOR(data, 0, keyStream, 16, data.Length);
}
public static byte[] DecryptAndAuthenticate(byte[] key, byte[] nonce, byte[] encryptedData, byte[] associatedData, byte[] signature)
{
if (nonce.Length < 7 || nonce.Length > 13)
{
throw new ArgumentException("nonce length must be between 7 and 13 bytes");
}
if (signature.Length < 4 || signature.Length > 16 || (signature.Length % 2 == 1))
{
throw new ArgumentException("signature length must be an even number between 4 and 16 bytes");
}
byte[] keyStream = BuildKeyStream(key, nonce, encryptedData.Length);
byte[] data = ByteUtils.XOR(encryptedData, 0, keyStream, 16, encryptedData.Length);
byte[] mac = CalculateMac(key, nonce, data, associatedData, signature.Length);
byte[] expectedSignature = ByteUtils.XOR(keyStream, 0, mac, 0, mac.Length);
if (!ByteUtils.AreByteArraysEqual(expectedSignature, signature))
{
throw new CryptographicException("The computed authentication value did not match the input");
}
return data;
}
private static byte[] BuildKeyStream(byte[] key, byte[] nonce, int dataLength)
{
int paddingLength = (16 - (dataLength % 16) % 16);
int keyStreamLength = 16 + dataLength + paddingLength;
int KeyStreamBlockCount = keyStreamLength / 16;
byte[] keyStreamInput = new byte[keyStreamLength];
for (int index = 0; index < KeyStreamBlockCount; index++)
{
byte[] aBlock = BuildABlock(nonce, index);
ByteWriter.WriteBytes(keyStreamInput, index * 16, aBlock);
}
return AesEncrypt(key, new byte[16], keyStreamInput, CipherMode.ECB);
}
private static byte[] BuildB0Block(byte[] nonce, bool hasAssociatedData, int signatureLength, int messageLength)
{
byte[] b0 = new byte[16];
Array.Copy(nonce, 0, b0, 1, nonce.Length);
int lengthFieldLength = 15 - nonce.Length;
b0[0] = ComputeFlagsByte(hasAssociatedData, signatureLength, lengthFieldLength);
int temp = messageLength;
for (int index = 15; index > 15 - lengthFieldLength; index--)
{
b0[index] = (byte)(temp % 256);
temp /= 256;
}
return b0;
}
private static byte[] BuildABlock(byte[] nonce, int blockIndex)
{
byte[] aBlock = new byte[16];
Array.Copy(nonce, 0, aBlock, 1, nonce.Length);
int lengthFieldLength = 15 - nonce.Length;
aBlock[0] = (byte)(lengthFieldLength - 1);
int temp = blockIndex;
for (int index = 15; index > 15 - lengthFieldLength; index--)
{
aBlock[index] = (byte)(temp % 256);
temp /= 256;
}
return aBlock;
}
private static byte ComputeFlagsByte(bool hasAssociatedData, int signatureLength, int lengthFieldLength)
{
byte flags = 0;
if (hasAssociatedData)
{
flags |= 0x40;
}
flags |= (byte)(lengthFieldLength - 1); // L'
flags |= (byte)(((signatureLength - 2) / 2) << 3); // M'
return flags;
}
private static byte[] AesEncrypt(byte[] key, byte[] iv, byte[] data, CipherMode cipherMode)
{
using (MemoryStream ms = new MemoryStream())
{
RijndaelManaged aes = new RijndaelManaged();
aes.Mode = cipherMode;
aes.Padding = PaddingMode.None;
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
}
}
}
}
/*版权所有(C)2020 Tal Aloni。版权所有。
*
*您可以根据以下条款重新发布和/或修改此程序:
*自由软件基金会公布的GNU较少公共许可证,
*许可证的第3版或(由您选择)任何更高版本。
*/
使用制度;
使用System.IO;
使用System.Security.Cryptography;
命名空间实用程序
{
///
///使用RFC 3610中详述的CBC-MAC(CCM)实现计数器
///
公共静态类AesCcm
{
专用静态字节[]计算器MAC(字节[]键,字节[]nonce,字节[]数据,字节[]关联数据,整数签名长度)
{
字节[]messageToAuthenticate=BuildB0Block(nonce,true,signatureLength,data.Length);
如果(associatedData.Length>0)
{
如果(associatedData.Length>=65280)
{
抛出新的NotSupportedException(“不支持65280或更大的关联数据长度”);
}
byte[]associatedDataLength=BigEndianConverter.GetBytes((ushort)associatedData.Length);
messageToAuthenticate=ByteUtils.Concatenate(messageToAuthenticate,associatedDataLength);
messageToAuthenticate=ByteUtils.Concatenate(messageToAuthenticate,关联数据);
int associatedDataPaddingLength=(16-(messageToAuthenticate.Length%16))%16;
messageToAuthenticate=ByteUtils.Concatenate(messageToAuthenticate,新字节[associatedDataPaddingLength]);
}
messageToAuthenticate=ByteUtils.Concatenate(messageToAuthenticate,data);
int-dataPaddingLength=(16-(messageToAuthenticate.Length%16))%16;
messageToAuthenticate=ByteUtils.Concatenate(messageToAuthenticate,新字节[dataPaddingLength]);
byte[]encrypted=AesEncrypt(密钥,新字节[16],messageToAuthenticate,CipherMode.CBC);
返回ByteReader.ReadBytes(加密,messageToAuthenticate.Length-16,signatureLength);
}
公共静态字节[]加密(字节[]密钥、字节[]nonce、字节[]数据、字节[]关联数据、int signatureLength、out字节[]签名)
{
如果(当前长度<7 | |当前长度>13)
{
抛出新ArgumentException(“nonce长度必须介于7到13字节之间”);
}
如果(signatureLength<4 | | signatureLength>16 | |(signatureLength%2==1))
{
抛出新ArgumentException(“签名长度必须是4到16字节之间的偶数”);
}
字节[]keyStream=BuildKeyStream(key、nonce、data.Length);
字节[]mac=CalculateMac(键、nonce、数据、关联数据、签名长度);
signature=ByteUtils.XOR(密钥流,0,mac,0,mac.Length);
返回ByteUtils.XOR(数据,0,keyStream,16,data.Length);
}
公共静态字节[]解密和身份验证(字节[]密钥、字节[]nonce、字节[]加密数据、字节[]关联数据、字节[]签名)
{
如果(当前长度<7 | |当前长度>13)
{
抛出新ArgumentException(“nonce长度必须介于7到13字节之间”);
}
if(signature.Length<4 | | signature.Length>16 | |(signature.Length%2==1))
byte[] sampleCtr0 = { 01, 0x4e, 0x61, 0xbc, 00, 00, 0xdd, 0xcc,
0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 00, 00 };
using (var mem = new MemoryStream())
{
using (var wri = new StreamWriter(mem))
{
//Write all data to the stream.
wri.Write(sampleCtr0);
}
Console.WritELine(Encoding.UTF8.GetString(mem.ToArray()));
}
System.Byte[]