自动密钥加密

Autokey Encryption

我正在做一个项目,要写入和读取 TP Link / Kaza 电源板或智能插头。

发送的数据已加密json,已“自动密钥加密”。

到目前为止,我已经能够转换打字稿加密函数并且效果很好。我得到了预期的结果。但是,我需要在我的加密数据中添加一个“header”。该数据是 3 个空字节,后跟一个衡量加密字节长度的字节。

typescript 示例有这段代码“用 headers 加密”,但是,我在尝试将其转换为可用的东西时遇到了一些困难。有人可以在路上轻推我吗?

首先是两个打字稿函数:(借自https://github.com/plasticrake/tplink-smarthome-crypto/blob/master/src/index.ts

/**
 * Encrypts input where each byte is XOR'd with the previous encrypted byte.
 *
 * @param input - Data to encrypt
 * @param firstKey - Value to XOR first byte of input
 * @returns encrypted buffer
 */
export function encrypt(input: Buffer | string, firstKey = 0xab): Buffer {
  const buf = Buffer.from(input);
  let key = firstKey;
  for (let i = 0; i < buf.length; i += 1) {
    // eslint-disable-next-line no-bitwise
    buf[i] ^= key;
    key = buf[i];
  }
  return buf;
}
/**
 * Encrypts input that has a 4 byte big-endian length header;
 * each byte is XOR'd with the previous encrypted byte.
 *
 * @param input - Data to encrypt
 * @param firstKey - Value to XOR first byte of input
 * @returns encrypted buffer with header
 */
export function encryptWithHeader(
  input: Buffer | string,
  firstKey = 0xab
): Buffer {
  const msgBuf = encrypt(input, firstKey);
  const outBuf = Buffer.alloc(msgBuf.length + 4);
  outBuf.writeUInt32BE(msgBuf.length, 0);
  msgBuf.copy(outBuf, 4);
  return outBuf;
}

第二个是我目前所拥有的。

// This part works well and produces the expected results
String encrypt(String input)
{
    int16_t firstKey = 0xab;
    String buf;
    int key;
    int i;
    buf = input;
    key = firstKey;
    i = 0;
    for (;i < buf.length();(i = i + 1))
    {
        buf[i] ^= key;
        key = buf[i];
    }
    return buf;
}

// This does not function yet, as I'm pretty lost..
// This was orginally converted from typescript with https://andrei-markeev.github.io/ts2c/
// I started work on converting this, but ran into errors I don't know how to solve. 

String encryptWithHeader(String input){
    String msgBuf;
    String outBuf;
    int16_t firstKey = 0xab;
    char * null = NULL;
    msgBuf = encrypt(input);
    outBuf = msgBuf.length() +1;
  
//this is where I got lost...  
    assert(null != NULL);
    null[0] = '[=11=]';
    strncat(null, outBuf, msgBuf.length());
    str_int16_t_cat(null, 4);
    outBuf = msgBuf + 4
    return outBuf;

}

最后,数据:

//this is the unencrypted json
String offMsg = "{\"system\":{\"set_relay_state\":{\"state\":0}}}";

//current encrypt function produces:
d0f281f88bff9af7d5ef94b6c5a0d48bf99cf091e8b7c4b0d1a5c0e2d8a381f286e793f6d4eedea3dea3

//the working "withheaders" should produce:

00002ad0f281f88bff9af7d5ef94b6c5a0d48bf99cf091e8b7c4b0d1a5c0e2d8a381f286e793f6d4eedea3dea3 

诚然,我的C/C++能力非常有限,我会拼打字稿,仅此而已。我与 PHP 有着非常广泛的历史。尽可能有用。所以,我了解数据结构的基础知识等等,但我正在冒险进入我从未涉足的领域。任何帮助将不胜感激。

看起来加密相当简单:将当前字符与密钥异或后写入缓冲区,并将新写入的字符作为新密钥。看起来“withHeaders”版本将加密字符串的长度作为 4 字节整数添加到缓冲区的开头。我认为分配一个字符数组并将该数组传递给将结果写入该缓冲区的函数可能更容易。例如:

void encryptWithHeader(byte buffer[], int bufferLength, byte key, String message) {
  int i;
  uint32_t messageLength = message.length();

  Serial.println(message);
  Serial.println(message.length());

  // check that we won't overrun the buffer
  if ( messageLength + 5 < bufferLength) {
    buffer[0] = messageLength >> 24 & 0xFF;
    buffer[1] = messageLength >> 16 & 0xFF;
    buffer[2] = messageLength >> 8 & 0xFF;
    buffer[3] = messageLength & 0xFF;

    for (i = 0; i < messageLength; i++) {
      buffer[i + 4] = message[i] ^ key;
      key = buffer[i + 4];
    }
  }
  else { // we would have overrun the buffer
    Serial.println("not enough room in buffer for message");
  }
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  byte theBuffer[64];
  int i;
  String offMsg = "{\"system\":{\"set_relay_state\":{\"state\":0}}}";

  encryptWithHeader(theBuffer, 64, 0xab, offMsg);

  // now print it out to check
  for (i = 0; i < offMsg.length() + 4; i++) {
    if (theBuffer[i] < 0x10) // adds an extra zero if a byte prints as on1y 1 char
      Serial.print("0");
    Serial.print(theBuffer[i], HEX);
  }
  while (true)
    ;
}

如果您想将字符缓冲区发送到远程设备,您可以一次发送一个字节:

for (i = 0; i < offMsg.length() + 4; i++)
  Serial.write(theBuffer[i]);