Aliro
Aliro access credential protocol research
Install / Use
/learn @kormax/AliroREADME
Aliro
<p float="left"> <img src="./assets/ALIRO.APPLE.webp" alt="Aliro-based Home Key settings screen with UWB configuration in Apple Wallet" width=200px> <img src="./assets/ALIRO.SAMSUNG.webp" alt="Aliro-based Home Key in Samsung Wallet" width=200px> <img src="./assets/ALIRO.GOOGLE.webp" alt="Aliro-based card used in Google Wallet" width=200px> </p>[!Note] On February 26, 2026, the Connectivity Standards Alliance announced the release of Aliro 1.0: Introducing Aliro 1.0: A Unified Standard to Transform the Access Control Ecosystem.
[!NOTE]
This repository previously contained observations on the development of the standard. This information has been moved into OBSERVATIONS.md.
Overview
Aliro is an access credential standard developed by the Connectivity Standards Alliance that lets devices present credentials over NFC or BLE + UWB to open doors or authenticate with compatible access systems.
This protocol is based on PKI, with readers and devices performing mutual authentication using public keys and certificates (currently ECDSA), also enabling offline-native credential sharing and revocation.
Endpoints that have recently completed mutual authentication can reuse the persistent secure context to speed up repeated authentications with symmetric cryptography.
To preserve privacy, device endpoints withhold identifying data until a reader is authenticated and a secure channel is established.
Commands
Aliro commands use ISO7816 APDUs over NFC and BLE and largely follow UnifiedAccess protocols such as CCC CarKey and Apple HomeKey, but with different cryptography, command parameters, and some expanded capabilities:
| Command | CLA | INS | P1 | P2 | Command Data | Le | Response Data | Description |
|-----------------------------|------|------|------|------|--------------------------------|-------------------|-----------------------------------|------------------------------------------------------------------------------------------|
| SELECT ALIRO PRIMARY APPLET | 00 | A4 | 04 | 00 | A000000909ACCE5501 | 00 | BER-TLV encoded data | Select the primary applet to get a list of supported protocol versions and features |
| AUTH0 | 80 | 80 | 00 | 00 | BER-TLV encoded data | [empty] | BER-TLV encoded data | Attempt authentication and optionally request a FAST cryptogram tied to a shared context |
| LOAD CERTIFICATE | 80 | D1 | 00 | 00 | ASN.1 encoded certificate | [empty] | [empty] | Supply a compressed reader certificate signed by the known reader group public key |
| AUTH1 | 80 | 81 | 00 | 00 | BER-TLV encoded data | [empty] | Encrypted BER-TLV encoded data | Authenticate with a known public key or with a key from a supplied, verified certificate |
| EXCHANGE | 80 | C9 | 00 | 00 | Encrypted BER-TLV encoded data | [empty] | Encrypted data | Write or read data from the endpoint's mailbox memory |
| CONTROL FLOW | 80 | 3C | 00 | 00 | BER-TLV encoded data | [empty] | [empty] | Notify the endpoint about the state of the transaction |
| SELECT ALIRO STEP UP APPLET | 00 | A4 | 04 | 00 | A000000909ACCE5502 | 00 | BER-TLV encoded data | Select the step-up applet |
| ENVELOPE | 00 | C3 | 00 | 00 | BER-TLV with nested CBOR data | [empty] or 00 | BER-TLV with nested CBOR data | Request attestation or revocation certificates from the endpoint |
| GET RESPONSE | 00 | C0 | 00 | 00 | [empty] | [expected length] | Remaining encrypted response data | Read leftover response bytes after a command returns 61 xx |
Running these commands moves the credential-holder endpoint through the following states:
stateDiagram-v2
state "Deselected" as Deselected
state "Selected / Unauthenticated" as Unauth
state "Auth0 authenticated" as Auth0Auth
state "Auth0 skipped" as Auth0Skip
state "Certificate loaded" as LoadCert
state "Auth1 authenticated" as Auth1Auth
state "Step Up" as StepUp
state "Exchange Auth0" as ExchangeAuth0
state "Exchange Auth1" as ExchangeAuth1
[*] --> Deselected
Deselected --> Unauth : Select Aliro primary applet
Unauth --> Auth0Auth : Auth0 (known mutual key)
Unauth --> Auth0Skip : Auth0 (skipped or unknown mutual key)
Auth0Auth --> ExchangeAuth0 : Exchange
ExchangeAuth0 --> ExchangeAuth0 : Exchange/Get Response
Auth0Auth --> LoadCert : Load certificate
Auth0Auth --> Auth1Auth : Auth1 (with public key)
Auth0Skip --> LoadCert : Load certificate
Auth0Skip --> Auth1Auth : Auth1 (with public key)
LoadCert --> Auth1Auth : Auth1 (with certificate)
Auth1Auth --> StepUp : Select Aliro Step Up applet
Auth1Auth --> ExchangeAuth1 : Exchange
ExchangeAuth1 --> ExchangeAuth1 : Exchange/Get Response
ExchangeAuth1 --> StepUp : Select Aliro Step Up applet
StepUp --> StepUp : Envelope/Get Response
<sub>Deselection or Control Flow is possible in all states, so it is not displayed in the diagram.</sub>
Secure Channel
Aliro secure messaging uses directional AES-GCM keys and per-direction counters.
Channel keys are derived during authentication and then reused by EXCHANGE, ENVELOPE, and GET RESPONSE.
Aliro uses three secure channels with different keys and independent state:
- NFC Exchange
- BLE Exchange
- StepUp
Each channel uses a reader/device key pair generated as a result of AUTH0 or AUTH1.
| Item | Value |
|-------------------------|---------------------------------------|
| Cipher | AES-GCM |
| Authentication tag size | 16 bytes (128 bits) |
| IV format | [MODE (8 bytes)] [COUNTER (4 bytes)] |
| Reader mode prefix | 0000000000000000 |
| Endpoint mode prefix | 0000000000000001 |
- Reader encrypts outbound command payload with the channel's
SKReader. - Endpoint encrypts outbound response payload with the channel's
SKDevice. - Reader decrypts inbound responses with
SKDevice; endpoint decrypts inbound commands withSKReader. - Counters are maintained per direction and incremented after each encrypted message.
- If a response returns
61xx, use GET RESPONSE to collect remaining encrypted chunks before final payload processing.
Example (Python pseudocode):
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
READER_MODE = bytes.fromhex("0000000000000000")
ENDPOINT_MODE = bytes.fromhex("0000000000000001")
def encrypt_aes_gcm(key: bytes, iv: bytes, plaintext: bytes) -> bytes:
assert len(iv) == 12, "IV must be 12 bytes for GCM mode"
encryptor = Cipher(algorithms.AES(key), modes.GCM(iv)).encryptor()
return encryptor.update(plaintext) + encryptor.finalize() + encryptor.tag
def decrypt_aes_gcm(key: bytes, iv: bytes, ciphertext: bytes) -> bytes:
encrypted, tag = ciphertext[:-16], ciphertext[-16:]
decryptor = Cipher(algorithms.AES(key), modes.GCM(iv, tag)).decryptor()
return decryptor.update(encrypted) + decryptor.finalize()
class AliroSecureChannel:
def __init__(
self,
sk_reader: bytes,
sk_device: bytes,
counter_reader: int = 1,
counter_endpoint: int = 1,
):
self.sk_reader = sk_reader
self.sk_device = sk_device
self.counter_reader = counter_reader
self.counter_endpoint = counter_endpoint
def encrypt_reader_data(self, plaintext: bytes) -> bytes:
# Use when the reader sends encrypted payload to the endpoint.
iv = READER_MODE + self.counter_reader.to_bytes(4, "big")
ciphertext = plaintext if not plaintext else encrypt_aes_gcm(self.sk_reader, iv, plaintext)
self.counter_reader += 1
return ciphertext
def decrypt_reader_data(self, ciphertext: bytes) -> bytes:
# Use when endpoint-side logic needs to decrypt reader-originated payload.
iv = READER_MODE + self.counter_reader.to_bytes(4, "big")
plaintext = ciphertext if not ciphertext else decrypt_aes_gcm(self.sk_reader, iv, ciphertext)
self.counter_reader += 1
return plaintext
def encrypt_endpoint_data(self, plaintext: bytes) -> bytes:
# Use when the endpoint sends encrypted payload back to the reader.
iv = ENDPOINT_MODE + self.counter_endpoint.to_bytes(4, "big")
ciphertext = plaintext if not plaintext else encrypt_aes_gcm(self.sk_device, iv, plaintext)
self.counter_endpoint += 1
return ciphertext
def decrypt_endpoint_data(self, ciphertext: bytes) -> bytes:
# Use when reader-side logic decrypts endpoint-originated payload.
iv = ENDPOINT_MODE + self.counter_endpoint.to_bytes(4, "big")
plaintext = ciphertext if not ciphertext else decrypt_aes_gcm(self.sk_device, iv, ciphertext)
self.counter_endpoint += 1
return plaintext
SELECT ALIRO PRIMARY APPLET
This is an initial command used to select the Aliro applet and receive capability inf
Related Skills
YC-Killer
2.7kA library of enterprise-grade AI agents designed to democratize artificial intelligence and provide free, open-source alternatives to overvalued Y Combinator startups. If you are excited about democratizing AI access & AI agents, please star ⭐️ this repository and use the link in the readme to join our open source AI research team.
best-practices-researcher
The most comprehensive Claude Code skills registry | Web Search: https://skills-registry-web.vercel.app
groundhog
399Groundhog's primary purpose is to teach people how Cursor and all these other coding agents work under the hood. If you understand how these coding assistants work from first principles, then you can drive these tools harder (or perhaps make your own!).
last30days-skill
10.3kAI agent skill that researches any topic across Reddit, X, YouTube, HN, Polymarket, and the web - then synthesizes a grounded summary
Security Score
Audited on Mar 26, 2026
