Introduction
With the new Beta firmware version 1.22.1 / 2.14.0 Nuki introduces the new encrypted token for the Bridge API. This new feature brings the security of the API access onto the next level and is available as Beta version from now on. That allows to upgrade and verify integrations already before the public release, which is planned for July 2022.
The hashed token method is deprecated by the introduction of this new method but will remain available in the API in order to not break existing integrations until at least end of 2022.
Link to latest Nuki Bridge HTTP API documentation
https://developer.nuki.io/page/nuki-bridge-http-api-1-13/4
Technical details
The idea is to calculate an encrypted token which is only valid for the execution of a single HTTP command. This token is calculated as following:
- Compute secret key by calculating the SHA256 from the API token
- Random 24 byte nonce is created
- The crypted token (=ctoken) is derived from xsalsa20poly1305 encryption of the current timestamp and a random number with the secret key and the nonce from above
This ctoken is then sent in hexadecimal, which results in the fact that information is no longer sent in plaintext (such as the date, random number, API token).
The Bridge implements a similar methodology for decryption. Following security measures and handlings have been introduced on top:
- The ctoken and the nonce is only valid for the execution of one single HTTP API command
- The ctoken is only valid within 60 seconds
Example
Plain Token: http://192.168.1.50:8080/info?token=123456
Hashed Token (Deprecated): http://192.168.1.50:8080/info?ts=20190305T01:06:53Z&rnr=4711&hash=f52eb5ce382e356c4239f8fb4d0a87402bb95b7b3124f0762b806ad7d0d01cb6
Crypted Token (NEW): http://192.168.1.50:8080/info?ctoken=a7f6b4df6758b92445bd5470b755b43ba41cf50af8b3f6e19368348ddfb1686291555dfd90b31f9333&nonce=119c38fb6d7d707b8a45f14e688b74b8c4c1acf33643c71a
Tools
- TweetNaCl.js - JavaScript crypto library
- Erzeugen eines SHA-256 verschlĂĽsselten Hashes
- Base64 to Hex | Base64 Decode | Base64 Converter | Base64
Example calculation
key = SHA256 (123456) = jZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI= (BASE64),
8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 (HEX)
nonce = nacl.randomBytes(24) = EZw4+219cHuKRfFOaIt0uMTBrPM2Q8ca (BASE64),
119c38fb6d7d707b8a45f14e688b74b8c4c1acf33643c71a (HEX)
secretbox (“2019-03-05T01:06:53Z,4711”, nonce, key) = p/a032dYuSRFvVRwt1W0O6Qc9Qr4s/bhk2g0jd+xaGKRVV39kLMfkzM= (BASE64),
a7f6b4df6758b92445bd5470b755b43ba41cf50af8b3f6e19368348ddfb1686291555dfd90b31f9333 (HEX)
Python example
import hashlib
import nacl.utils
import nacl.secret
import datetime
BRIDGE_IP = "100.100.100.1"
BRIDGE_PORT = 8080
BRIDGE_API_TOKEN = "123456"
SESSION_KEY = hashlib.sha256(str(BRIDGE_API_TOKEN).encode('utf-8')).digest()
SESSION_NONCE = 0
SESSION_CTOKEN = 0
def init():
print("Bridge info: "+ str(BRIDGE_IP) + ":" + str(BRIDGE_PORT) + " token=" + str(BRIDGE_API_TOKEN))
def updateNonce():
global SESSION_NONCE
SESSION_NONCE = nacl.utils.random(24)
def getTimestamp():
date = datetime.date.today()
time = datetime.datetime.utcnow().strftime("%H:%M:%S")
timestamp = str(date) + "T" + str(time) + "Z,1234"
print(timestamp)
return timestamp
def updateToken():
global SESSION_KEY
box = nacl.secret.SecretBox(SESSION_KEY)
timestamp = getTimestamp()
ctoken = box.encrypt(timestamp.encode('utf-8'), SESSION_NONCE)
global SESSION_CTOKEN
SESSION_CTOKEN = ctoken.ciphertext.hex()
def fetchBridgeInfo():
infoString = "http://" + str(BRIDGE_IP) + ":" + str(BRIDGE_PORT) + "/info?ctoken=" + str(SESSION_CTOKEN) + "&nonce=" + str(SESSION_NONCE.hex())
print(infoString)
if __name__ == '__main__':
init()
updateNonce()
updateToken()
fetchBridgeInfo()
Updates
Bridge 1.0
Bridge 1.0 Beta 1.22.1
- Initial version supporting the encrypted API token
Bridge 2.0
Bridge 2.0 Beta 2.14.0
- Initial version supporting the encrypted API token