Very, very simple. I assume you can provision a per device symmetric key and a back-end database containing 1KB per device is fine. Devices keep a sequential nonce counter which starts at zero. we derive a `k_nonce_hint` key from the main device symmetric key. Each nonce is used to generate a `nonce_hint` byte string. This can be as simple as `nonce_hint(nonce)=AES_enc(nonce,k_nonce_hint)[:HINTLEN]` or use a hash function or something. messages sent over the air look like `nonce_hint || AEAD(M,nonce)` where AEAD is some encrypt+MAC scheme. ### At the other end For each `(device,nonce)` pair there's an associated nonce hint that can be stored in a database. Assuming not too many messages get lost, you only need to keep some small number `10`-`100` for each device in the DB. The application server after receiving a message queries the DB to find a matching (Device,Nonce) pair(s) and tries decrypting the message with that(or those) device key(s) and nonce(s). If it works, they update the list of nonce hints for that device in the DB. Note that if the nonce hint is derived reversibly (EG:16 byte nonce length derived using AES encryption) nonce hints can be checked against all device keys as a fallback option. A 64 bit block cipher like XTEA would be perfect for this. ### Why This Works Symmetric crypto and databases are cheap. Adding a DB and structuring appropriately turns an `O(k)` problem (decrypt one message from one of `k` devices) into an `O(1)` problem (database lookup). For sufficiently large values of `k` this is worthwhile. ### Other Nonce Patterns If the devices have a clock inside them, you could do something time based (EG:`32-bit day || 32 bit sequence counter`). Some BLE location beacons use this to restrict use to paying customers. Beacons generate time based pseudorandom values every 15s or so and device submit these values to a commercial API. The service generates every expected value for the current time (±1 min to account for clock drift) and then does a lookup for submitted values. The cost to run such a service for a few million beacons is only a fraction of a CPU core <1kB RAM per device. ### Comparing to Asymmetric options Cheapest secure option that meets your requirements would be unauthenticated ECDH encryption + symmetric MAC using per device keys. This meets your security requirements. - `ECDH_point,ECDH_secret=Curve25519_ECDH_public()` - `ciphertext` = `encrypt(device_ID || message,k=ECDH_secret)` - `tag` = `HMAC(ciphertext , device_MAC_key)[:MAC_LENGTH]` - `send(ECDH_point || ciphertext || tag)` On the backend: - `ECDH_point,ciphertext,tag=split_message(recv())#get a message` - `ECDH_secret=Curve25519_ECDH_private(ECDH_point,EC_private_key)` - `plaintext=decrypt(device_ID || message,k=ECDH_secret)` - `device_MAC_key=DB_lookup_key(plaintext[:DEVICE_ID_LEN])` - `correct_tag=HMAC(ciphertext , device_MAC_key)[:MAC_LENGTH]` - `if(tag!=correct_tag){return invalid_message}` - `return plaintext` Overhead is - 32 B (ECDH point) - 4 B (device ID) - 8 B (MAC)