Unknown error when requesting keyturner state

Hi,

I am working on an open source repo to control a nuki lock directly via BLE from an esp32 (without the use of a bridge).

I have the first part (authentication) working and the esp pairs with the lock.
after sending authorization id confirmation I get:
0e 00 00 9d d7 (status 00, COMPLETE)

I am now struggling with sending encrypted commands, I tested the encryption with predefined values from the api pdf and that seems to work fine, problem I now have is that I get
12 00 ff 92 c3 10 09 (unknown error)
when I am requesting the key turner states.

Data that is being used:
Secret key: bb a0 4e 0f 37 07 27 48 50 48 d3 1d 4f d2 75 fc 2d 2c 55 93 72 14 af 94 e8 40 bd 86 2d d3 d1 92
authorizationId: 20 5d 34 00
Plain data: 20 5d 34 00 01 00 0c 00 3f 50
Nonce: 92 c3 38 60 c8 7c 46 cf dc ee 68 9e 46 e6 31 a8 04 9d d1 72 34 c6 e6 4d
Additional data: : 92 c3 38 60 c8 7c 46 cf dc ee 68 9e 46 e6 31 a8 04 9d d1 72 34 c6 e6 4d 20 5d 34 00 0a 00
Plain data encrypted: : f5 36 55 71 88 a7 e4 eb ce cb f5 8d e0 6a 5a f6 51 bc 51 df 6e 84 ba d5 6d 57
Sending encrypted message: 92 c3 38 60 c8 7c 46 cf dc ee 68 9e 46 e6 31 a8 04 9d d1 72 34 c6 e6 4d 20 5d 34 00 0a 00 f5 36 55 71 88 a7 e4 eb ce cb f5 8d e0 6a 5a f6 51 bc 51 df 6e 84 ba d5 6d 57

Getting a bit dazzled by all the bytes… :wink:

Hope somebody can help me in the right direction

Regards,
Jeroen

Hi Jeroen,

Additional data: : 92 c3 38 60 c8 7c 46 cf dc ee 68 9e 46 e6 31 a8 04 9d d1 72 34 c6 e6 4d 20 5d 34 00 0a 00

The last two bytes are the length of the encrypted part.
0a 00 = length of 10 bytes

But your encrypted message is 26 bytes long so it should be 1a 00 instead.

best,
Marc

Hi Marc,

thx for helping me out.

So the msg length I need to send in the unencrypted header should be the length of the encrypted message…

I updated this but am still getting the same result:

Result of connect:

Received data: 0e 00 00 9d d7 
status: 00

Result of sending the “request data” command:

authorizationId: 24 5d 34 00

Plain data with CRC: : 25 5d 34 00 01 00 0c 00 81 18 
Nonce: 52 17 62 c3 bd 94 52 3a 5e 57 b8 f6 bb fa 02 e2 11 ea 76 20 fc da c5 ab 
encrypted msgLen: 26
Additional data: : 52 17 62 c3 bd 94 52 3a 5e 57 b8 f6 bb fa 02 e2 11 ea 76 20 fc da c5 ab 25 5d 34 00 1a 00 
Plain data encrypted: : 9b d9 1c fb 41 2d 1f 10 e6 49 33 62 12 09 8d 14 a1 16 a1 24 4f 9f ce 1e b2 04 
Sending encrypted message: 52 17 62 c3 bd 94 52 3a 5e 57 b8 f6 bb fa 02 e2 11 ea 76 20 fc da c5 ab 25 5d 34 00 1a 00 9b d9 1c fb 41 2d 1f 10 e6 49 33 62 12 09 8d 14 a1 16 a1 24 4f 9f ce 1e b2 04 

Received data: 12 00 ff 52 17 bd 94 
which translates to ERROR_UNKNOWN

below for reference the code I use:

 keyturnerStates	              = 0x000C
 requestData	                  = 0x0001

  uint16_t payload = (uint16_t)nukiCommand::keyturnerStates;
  nukiBle.sendEncryptedMessage(nukiCommand::requestData, (char*)&payload, 2);


void NukiBle::sendEncryptedMessage(nukiCommand commandIdentifier, char* payload, uint8_t payloadLen) {
  /*
  #     ADDITIONAL DATA (not encr)      #                    PLAIN DATA (encr)                             #
  #  nonce  # auth identifier # msg len # authorization identifier # command identifier # payload #  crc   #
  # 24 byte #    4 byte       # 2 byte  #      4 byte              #       2 byte       #  n byte # 2 byte #
  */


  //compose plain data
  unsigned char plainData[6 + payloadLen] = {};
  unsigned char plainDataWithCrc[8 + payloadLen] = {};

  Crc16 crcObj;
  uint16_t dataCrc;

  memcpy(&plainData[0], &authorizationId, sizeof(authorizationId));
  memcpy(&plainData[4], &commandIdentifier, sizeof(commandIdentifier));
  memcpy(&plainData[6], payload, payloadLen);

  //get crc over plain data
  crcObj.clearCrc();
  // CCITT-False:	width=16 poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1
  dataCrc = crcObj.fastCrc((uint8_t*)plainData, 0, sizeof(plainData), false, false, 0x1021, 0xffff, 0x0000, 0x8000, 0xffff);
  memcpy(&plainDataWithCrc[0], &plainData, sizeof(plainData));
  memcpy(&plainDataWithCrc[sizeof(plainData)], &dataCrc, sizeof(dataCrc));
  #ifdef DEBUG_NUKI
  printBuffer((byte*)plainDataWithCrc, sizeof(plainDataWithCrc), false, "Plain data with CRC: ");
  #endif

  //compose additional data
  unsigned char additionalData[30] = {};
  unsigned char nonce[24] = {};
  uint8_t msgLen = 0;
  generateNonce(nonce, sizeof(nonce));

  memcpy(&additionalData[0], nonce, sizeof(nonce));
  memcpy(&additionalData[24], authorizationId, sizeof(authorizationId));

  //Encrypt plain data
  unsigned char plainDataEncr[26] = {0};
  encode(plainDataWithCrc, plainDataEncr, sizeof(plainDataWithCrc), nonce, secretKeyK);

  msgLen = sizeof(plainDataEncr);
  log_d("encrypted msgLen: %d", msgLen);
  memcpy(&additionalData[28], &msgLen, sizeof(msgLen));

  #ifdef DEBUG_NUKI
  printBuffer((byte*)additionalData, 30, false, "Additional data: ");
  printBuffer((byte*)plainDataEncr, sizeof(plainDataEncr), false, "Plain data encrypted: ");
  #endif

  //compose complete message
  unsigned char dataToSend[sizeof(additionalData) + sizeof(plainDataEncr)] = {};
  memcpy(&dataToSend[0], additionalData, sizeof(additionalData));
  memcpy(&dataToSend[30], plainDataEncr, sizeof(plainDataEncr));

  #ifdef DEBUG_NUKI
  printBuffer((byte*)dataToSend, sizeof(dataToSend), false, "Sending encrypted message");
  #endif

  pGdioCharacteristic->writeValue((uint8_t*)dataToSend, sizeof(dataToSend), true);
  delay(1000); //wait for response via BLE char
}

Hi Jeroen,

Just to be sure:

pGdioCharacteristic->writeValue((uint8_t*)dataToSend, sizeof(dataToSend), true);

You need to send this encrypted command to the USDIO characteristic, not to GDIO.
Are you using USDIO?

best,
Marc

ah, no I was not, I was still writing to the keyturner pairing service :unamused:

If I am correct the USDIO char becomes active after the pairing/connecting. So I added registration on

#define keyturnerServiceUUID a92ee200-5501-11e4-916c-0800200c9a66
#define userDataUUID         a92ee202-5501-11e4-916c-0800200c9a66

Now it continues (do get a CRC CHECK failed on the returned message but now I can continue.)

A question, what is the difference between
“General Data Input Output characteristic” UUID: a92ee201-5501-11e4-916c-0800200c9a66
and
“User-Specific Data Input Output characteristic” UUID: a92ee202-5501-11e4-916c-0800200c9a66
that belong to the “Keyturner service”

What is the “General Data Input Output characteristic” used for?

thx!!

Hi Jeroen,

The GDIO is only used for unencrypted communication.
USDIO is the user specific (encrypted with users key) communication.

Normally you won’t receive anything on GDIO as communication has to be encrypted all the time after pairing.
But if the Smart Lock is unable to decrypt your request (e.g. because the wrong key is used) and therefore does not know which key to use to encrypt the response you will receive an unencrypted error code on GDIO.

best,
Marc

Issue I am now running into is that the decryption fails on the message I get from Nuki when requesting key turner states…

msg sent (encrypted with the secret key):

Plain data with CRC: : 3e 5d 34 00 01 00 0c 00 9a f3 
Nonce: 33 c7 7e a3 68 58 38 c2 ee 61 67 cd e0 92 b2 70 f6 a2 ed 51 b7 0e 61 19 
Additional data: : 33 c7 7e a3 68 58 38 c2 ee 61 67 cd e0 92 b2 70 f6 a2 ed 51 b7 0e 61 19 3e 5d 34 00 1a 00 
Plain data encrypted: : 3d 2d a2 2e 8a ae 90 19 72 9a b6 d3 0b 95 37 6b ae b9 b9 f9 36 1e 5d f4 c1 85
Sending encrypted message: 33 c7 7e a3 68 58 38 c2 ee 61 67 cd e0 92 b2 70 f6 a2 ed 51 b7 0e 61 19 3e 5d 34 00 1a 00 3d 2d a2 2e 8a ae 90 19 72 9a b6 d3 0b 95 37 6b ae b9 b9 f9 36 1e 5d f4 c1 85

I receive this:

Received data: 17 43 f8 4c b2 02 15 b7 8a 9c d6 1a c6 b2 ee a8 d6 17 ac be 3b 24 8f 10 3e 5d 34 00 2d 00 19 88 b7 48 19 7f 46 ac f3 fb fc 22 04 81 d8 1f 75 ef 5c 14 32 d3 72 6c 09 1c b8 30 c1 e6 43 bc c6 2b f8 05 ac da 27 f2 b0 92 36 5b f2

received nonce: 17 43 f8 4c b2 02 15 b7 8a 9c d6 1a c6 b2 ee a8 d6 17 ac be 3b 24 8f 10 
Received AuthorizationId: 3e 5d 34 00 
Rec encrypted data: 19 88 b7 48 19 7f 46 ac f3 fb fc 22 04 81 d8 1f 75 ef 5c 14 32 d3 72 6c 09 1c b8 30 c1 e6 43 bc c6 2b f8 05 ac da 27 f2 b0 92 36 5b f2

But decryption fails (I use the same secret key as used to encrypt a message), I tried using the nonce I received and the nonce I used in sending the message for decryption but both fail.

PS. The encrypted message I receive is also 6 bytes longer as in the example in the documentation…? (example is 39, I receive 45 bytes)

any hints?

Hey Jeroen,

Could you provide the shared secret for the used authorization ID?

Best Regards,
Peter

Hey Peter,

Your question indicates the shared key s needs to be used for decryption…?
Tried it but did not work:

Credentials retreived from storage:
secretKeyK: ea 44 4a b1 27 b9 78 c0 05 02 e6 8a 97 4c ae d7 55 5f 0b 8b d3 d0 63 b3 68 2c 18 06 66 10 b8 92 
sharedKeyS: db 75 eb 02 c7 d6 f7 dd 24 21 f7 f7 8e b8 1c 78 c2 30 df 88 ce f2 82 d6 7a 7e e7 14 23 df f4 57 
authorizationId: 3f 5d 34 00

sending request keyturner states:

Plain data with CRC: : 3f 5d 34 00 01 00 0c 00 49 b4 
Nonce: 9e 21 22 e5 04 0e 61 89 48 b3 27 3d f9 5d c1 a3 39 00 d7 02 e3 4d 2d 2e 
Additional data: : 9e 21 22 e5 04 0e 61 89 48 b3 27 3d f9 5d c1 a3 39 00 d7 02 e3 4d 2d 2e 3f 5d 34 00 1a 00 
Encryption key (secretKey): : ea 44 4a b1 27 b9 78 c0 05 02 e6 8a 97 4c ae d7 55 5f 0b 8b d3 d0 63 b3 68 2c 18 06 66 10 b8 92 
Plain data encrypted: : af 7a 65 88 3f cc 48 3b 1d e4 8a 57 7b 13 22 12 f0 19 24 ae b9 a8 11 af bf 06 
Sending encrypted message: 9e 21 22 e5 04 0e 61 89 48 b3 27 3d f9 5d c1 a3 39 00 d7 02 e3 4d 2d 2e 3f 5d 34 00 1a 00 af 7a 65 88 3f cc 48 3b 1d e4 8a 57 7b 13 22 12 f0 19 24 ae b9 a8 11 af bf 06

receiving:

Received data: 93 8e 46 5c ec ef cb 55 ea 32 c6 9c 48 5e b0 af 69 dc cd 8c 0f 12 44 fd 3f 5d 34 00 2d 00 84 ee dc 9a 61 d2 ba 35 60 3f a7 f1 08 8a ba 79 f9 fb 7a 30 3b 11 da 0d 59 17 82 d7 c2 a3 1c 13 c3 2c b4 9f 98 75 64 e8 24 73 b2 60 b6
received nonce: 93 8e 46 5c ec ef cb 55 ea 32 c6 9c 48 5e b0 af 69 dc cd 8c 0f 12 44 fd 
Received AuthorizationId: 3f 5d 34 00  
len encr msg: 45
Rec encrypted data: 84 ee dc 9a 61 d2 ba 35 60 3f a7 f1 08 8a ba 79 f9 fb 7a 30 3b 11 da 0d 59 17 82 d7 c2 a3 1c 13 c3 2c b4 9f 98 75 64 e8 24 73 b2 60 b6 

[D][NukiBle.cpp:528] decode(): result: -1
[W][NukiBle.cpp:530] decode(): Decryption failed (length 45, given result 0)

Hey Jeroen,

Please excuse the confusion. I am using the terminology from the Nuki BLE Documentation, thereby referring to the encryption key as ‘shared key’ (in your case the ‘secretKey’). The ‘secretKeyK’ and ‘secretKeyS’ are not required after pairing and should not be stored. Only the shared secret (‘secretKey’) is required for communicating the Nuki devices (from my understanding anyway).

Using your provided shared key and received data I was able to decrypt the message:

#decrypting# 84eedc9a61d2ba35603fa7f1088aba79f9fb7a303b11da0d591782d7c2a31c13c32cb49f987564e82473b260b6
#decrypted# 3f5d34000c0002ff02e607020c0b12290000c803000202020100002796
{
  command: [KEYTURNER_STATES:0x000C],
  nukiState: 2,
  lockState: 255,
  trigger: 2,
  currentTime: { year: 2022, month: 2, day: 12, hour: 11, minute: 18, second: 41 },
  timezoneOffset: 0,
  battery: { critical: false, charging: false, level: 50 },
  configUpdateCount: 3,
  lockAndGoTimer: 0,
  lastLockAction: 2,
  lastLockActionTrigger: 2,
  lastLockActionCompletionStatus: 2,
  doorSensorState: 1,
  nightmodeActive: false,
  accessoryBatteryState: {
    featureSupportedByKeypad: false,
    keypadCriticalBatteryState: false
  }
}

Hence, there must be something wrong in the way you are applying the decryption function. Could you share some more details?

Best Regards,
Peter

no probs, already very glad you are helping me out :slight_smile:
So I am probably messing up the key names…
(very confusing this encryption stuff, having a hard time understanding it all :grimacing:)

Good to see confirmation communications is working as it is, so now only the decryption…

Which nonce do you use for decrypting? The one that was sent in the 1st msg or the one that is received?
This is the code I use when I receive an encrypted msg:

//handle encrypted msg
    unsigned char recNonce[crypto_secretbox_NONCEBYTES];
    unsigned char recAuthorizationId[4];
    unsigned char recMsgLen[2];
    memcpy(recNonce, &recData[0], 24);
    memcpy(recAuthorizationId, &recData[24], 4);
    memcpy(recMsgLen, &recData[28], 2);
    uint16_t encrMsgLen = 0;
    memcpy(&encrMsgLen, recMsgLen, 2);
    unsigned char encrData[encrMsgLen];
    memcpy(encrData, &recData[30], encrMsgLen);

    #ifdef DEBUG_NUKI
    log_d("Received encrypted msg...");
    printBuffer(recNonce, sizeof(recNonce), false, "received nonce");
    printBuffer(recAuthorizationId, sizeof(recAuthorizationId), false, "Received AuthorizationId");
    log_d("len encr msg: %d", encrMsgLen);
    printBuffer(encrData, sizeof(encrData), false, "Rec encrypted data");
    #endif

    unsigned char decrData[encrMsgLen - crypto_secretbox_MACBYTES];
    decode(encrData, decrData, encrMsgLen, recNonce, sharedKeyS);

The decode data code:

int NukiBle::decode(unsigned char* output, unsigned char* input, unsigned long long len, unsigned char* nonce, unsigned char* keyS) {
  int result = crypto_secretbox_open_easy(output, input, len, nonce, keyS);
  log_d("result: %d", result);
  if (result) {
    log_w("Decryption failed (length %i, given result %i)\n", len, result);
    return -1;
  }
  return len;
}

Hey Jeroen,

Always use the nonce (24 bytes) received from the device with the shared secret (32 bytes) which was derived during pairing.

To be precise, use the following buffer for this:
Nonce: 938e465cecefcb55ea32c69c485eb0af69dccd8c0f1244fd
Shared Secret: ea444ab127b978c00502e68a974caed7555f0b8bd3d063b3682c18066610b892
Encrypted: 84eedc9a61d2ba35603fa7f1088aba79f9fb7a303b11da0d591782d7c2a31c13c32cb49f987564e82473b260b6

So from what I read in you are using the correct buffers. However, you are missing the zero byte padding for the secret box (crypto_secretbox_ZEROBYTES). Basically what you need to do is prepend 16 NULL bytes to the encrypted buffer.

The encrypted buffer should look like this:
0000000000000000000000000000000084eedc9a61d2ba35603fa7f1088aba79f9fb7a303b11da0d591782d7c2a31c13c32cb49f987564e82473b260b6

Not being an expert C developer myself, something of the following sort (untested) should do the trick:

unsigned char encrData[crypto_secretbox_ZEROBYTES + encrMsgLen];
memset(encrData, 0, crypto_secretbox_ZEROBYTES);
memcpy(&encrData[crypto_secretbox_ZEROBYTES], &recData[30], encrMsgLen);

Best Regards,
Peter

This nonce:
938e465cecefcb55ea32c69c485eb0af69dccd8c0f1244fd
you used for a successful decryption was not the one received during pairing… It is the one received within the message returned from the Nuki with the keyturner states.

(I deducted that the nonce (number only used once) also should not be reused :slight_smile: ?)

But still it works on your side still not on mine…

updated the code:

unsigned char encrData[encrMsgLen + crypto_secretbox_MACBYTES];
    unsigned char padding[crypto_secretbox_MACBYTES] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    memcpy(encrData, &padding, sizeof(padding));
    memcpy(&encrData[crypto_secretbox_MACBYTES], &recData[30], encrMsgLen);

maybe not completely the correct/best way but it does generate this buffer to be decoded:

Rec encrypted data: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 2f bb e2 c3 82 fd 43 a1 df 4e 81 fa e6 08 96 72 58 4b 59 3b e4 4d 7a e4 c4 07 2f f9 85 0e cf 7d 66 c4 00 3c 45 ef 79 08 f1 29 75 f1

I used the nonce I received as header of the encrypted msg:

received nonce: 22 ad 2d b9 85 96 a6 27 e6 b9 d2 40 f3 39 24 08 0a 34 98 ef 24 95 78 fb

But still decryption failed. Are you still able to decrypt this?

I tried to change the size I give to the decode to the encrypted msg size + the 16 padding bytes but that also did not work:

decode(encrData, decrData, encrMsgLen + crypto_secretbox_MACBYTES, recNonce, secretKeyK);

What should the size be for the decrypted msg?
it’s now:

unsigned char decrData[encrMsgLen - crypto_secretbox_MACBYTES];

thx!

full code as the above snippets do not provide an overview:

//handle encrypted msg
    unsigned char recNonce[crypto_secretbox_NONCEBYTES];
    unsigned char recAuthorizationId[4];
    unsigned char recMsgLen[2];
    memcpy(recNonce, &recData[0], 24);
    memcpy(recAuthorizationId, &recData[24], 4);
    memcpy(recMsgLen, &recData[28], 2);
    uint16_t encrMsgLen = 0;
    memcpy(&encrMsgLen, recMsgLen, 2);
    unsigned char encrData[encrMsgLen + crypto_secretbox_MACBYTES];
    unsigned char padding[crypto_secretbox_MACBYTES] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    memcpy(encrData, &padding, sizeof(padding));
    memcpy(&encrData[crypto_secretbox_MACBYTES], &recData[30], encrMsgLen);

    #ifdef DEBUG_NUKI
    log_d("Received encrypted msg...");
    printBuffer(recNonce, sizeof(recNonce), false, "received nonce");
    printBuffer(recAuthorizationId, sizeof(recAuthorizationId), false, "Received AuthorizationId");
    log_d("len encr msg: %d", encrMsgLen);
    printBuffer(encrData, sizeof(encrData), false, "Rec encrypted data");
    #endif

    unsigned char decrData[encrMsgLen - crypto_secretbox_MACBYTES];
    decode(encrData, decrData, encrMsgLen + crypto_secretbox_MACBYTES, recNonce, secretKeyK);

Hey Jeroen,

Exaclty, use the nonce from the received message. My previous wording was ambiguous.

The length (clen) passed to crypto_secretbox_open_easy must be the received message length + the zero byte padding. The plaintext length should be the same. The first 16 bytes of the plaintext can be ignored.

Also I just spotted you mixed up the plaintext and cypher text. The function arguments encrData and decrData must be swapped:

unsigned char decrData[encrMsgLen + crypto_secretbox_MACBYTES];
decode(decrData, encrData, encrMsgLen + crypto_secretbox_MACBYTES, recNonce, secretKeyK);

Best Regards,
Peter

omg so ashamed :flushed:
I changed this earlier to align the argument order of the functions.

wel spotted, it works now!

THANK YOU VERY MUCH, I ow you at least 1 beer!!!

1 Like