Michael Micsen Johannessen Wehus 6860adf6ea Add card Version number
2026-01-26 19:38:32 +01:00
2026-01-26 19:38:32 +01:00

Introduction

Smartair is a access control system made by an Assa Abloy subsidiary based out of Spain called Tesa

It's primarly meant to be an offline system where the communication between the door and the database is intermitten and done through credentials and programming tools only.

Making it look something like this.

 +---------------+     +----------+     +----------+ 
 |DATABASE SERVER|     |CONTROLLER|     |CONTROLLER| 
 +---------------+     +----------+     +----------+ 
         ^                  ^                ^      
         |                  |                |      
         |                  |                |      
         |                  |                |      
         v                  v                v      
 +---------------+       +------+         +------+  
 |    ENCODER    |       |DOOR 1|         |DOOR 2|  
 +---------------+       +------+         +------+  

This is a collection of the research we have conducted. These vulnerabilities was reported in 2021 and a upgrade path to patch these vulnerabilites have been available from Tesa since early 2022.

Most of the research has been done using locks and hardware we sourced ourselves second hand using a publicly available copy of the TS1000 software version 7.02.00 running in demo mode.

Tesa Security Resources

The intentions of this repo is to document all the research that was done to increase the awareness and interest by both product users/owners but also other researchers.

Smartair card formats

The installation gets programmed with an "Entry point" to the User sector in the card using the programming tool.

The rest is defined by the data in the card.

User sector format

X 0xX0 0xX1 0xX2 0xX3 0xX4 0xX5 0xX6 0xX7 0xX8 0xX9 0xXA 0xXB 0xXC 0xXD 0xXE 0xXF
0x0X Card Config LICSENSE? SITE ID .. .. .. ? User ID .. CardVersion Encoding Time .. .. .. 00 Valid From
0x1X .. .. Valid Until .. .. Pin code .. .. UserSettings UOC Block ADDR .. .. Grants .. .. ..
0x2X 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Decoded

CardConfig:

[Enchanced encryptage info](Enhanced Encryptage.md)

0: Enhanced encryptage 
1: ?
2: ?
3: ?
4: ?
5: ?
6: ?
7: ?

Licsense? We don't know but we assume it's config based on the licsense given from Tesa

SiteID: The site id which is embedded in the licsense from Tesa

UserID: A 2 Byte sequential increasing number which increases for each cardholder added in the TS1000 software

CardVersion: Increases every time the user is issued a new card and the user id is kept meant to be used if the old one is lost etc. Potential denial of access here since the lock keeps the highest known version for each user by emulating many users with high Version numbers the legitimate cards may stop working.

Encoding time 4 Bytes with 1 minute accuracy

Valid from/until: 3 Byte dates with 10 minute resolution

The time format is roughly like this but we do have some unknowns in the extended times used for encoding time.

Time formats

Year  = 2000 + uint7
Month = uint4
Day   = uint5
Hour  = uint5

Minutes can be with 10 minute resolution used for valid from/to or seconds(A bit unsure exactly how much resolution) resolution used for encoding time.

Minutes *10 = uint3
Minutes = uint6
Seconds = uint5 * 2

Cyberchef decoder:

From_Hex('Auto')
To_Binary('None',8)
Find_/_Replace({'option':'Regex','string':'^([0|1]{7})([0|1]{4})([0|1]{5})([0|1]{5})(([0|1]{3})|(([0|1]{6})([0|1]{5})))$'},'$1\\n$2\\n$3\\n$4\\n$6\\n$8\\n$90',true,false,true,false)
Fork('\\n',',',false)
From_Binary('Space',8)
To_Decimal('Space',false)
Merge(true)
Find_/_Replace({'option':'Regex','string':'^(.*)$'},'Year,Month,Day,Hour,Minute*10,Minute*1,Seconds\\n$1',true,false,false,true)
To_Table(',','\\r\\n',false,'ASCII')

PinCode: 3 bytes with the users pin code terminated with FF if the pin is only 4 digits

UserSettings(0x18): Seems to act somewhat like "User flags" or similar

0: ?
1: Disabled user(Extended opening time)
2: ?
3: Audit 1?
4: ?
5: ?
6: Audit 2?
7: ?

Audit 1/2 are assumptions they seem to be related to the "Cardholder collects audit trail" and "Do not open if audit is full"

UOC Block:

Reversed endianess of the location where the Update on card config block is located

00 c0 = 00 0c = 12 = Block 0 Sector 3

Grants: Each grant is assigned a bit position so a reader configured to accept grants just checks expirations of the card and that the bit configured has been set on the card presented used for things like Pool access, Elevator floors or other generic functions you want to allow many people with a credential to do without keeping a big locking plan in the lock

UOC Sector

This sector tells the lock where to look for access plan changes and where to write log entries

X 0xX0 0xX1 0xX2 0xX3 0xX4 0xX5 0xX6 0xX7 0xX8 0xX9 0xXA 0xXB 0xXC 0xXD 0xXE 0xXF
0x0X ? ? ? ? ? ? ? Door Block Start .. Number of entries .. ? ? ? ? ?
0x1X ? ? ? ? ? ? ? Door Block Start .. Number of entries .. ? ? ? ? ?
0x2X 00 00 00 00 00 00 00 00 00 ? ? ? ? 00 00 00

Door Block start is in reverse endianes again

For some reason the first block is duplicate we don't know why

Door sector (Access plan)

X 0xX0 0xX1 0xX2 0xX3 0xX4 0xX5 0xX6 0xX7 0xX8 0xX9 0xXA 0xXB 0xXC 0xXD 0xXE 0xXF
0x0X DoorID .. Schedule ShouldUpdate DoorID .. Schedule ShouldUpdate DoorID .. Schedule ShouldUpdate DoorID .. Schedule ShouldUpdate
0x0X DoorID .. Schedule ShouldUpdate DoorID .. Schedule ShouldUpdate DoorID .. Schedule ShouldUpdate DoorID .. Schedule ShouldUpdate
0x0X DoorID .. Schedule ShouldUpdate DoorID .. Schedule ShouldUpdate DoorID .. Schedule ShouldUpdate DoorID .. Schedule ShouldUpdate

DoorID: Just a sequential number of what door this access is for

Schedule: Split into 2 parts First nibble is what Schedule this access should be valid for 0-F where 0 = Never and F = Always 1-E are globally configured in the access control software and loaded into the lock using a programming tool

The second nibble is bit bools with extra grants like the ability to leave a door unlocked by tapping the card twice or override privacy

Should Update is 0x01 when the encoder adds a new permission and gets set to 0x00 when a lock "Reads" the change and adds it to it's local memory.

Mifare KEYS

For Mifare Classic the cards use a Site Specific key as keyA where the last 4 bytes of the key is the SiteID and the readers auth with KeyA due to the weak crypto in Mifare auth this makes it possible to use the exsisting Mfkey32 attack to extract the key from a reader. And since keyA includes the SiteID it is possible to forge a credential that may be recognized by locks on the site

keyB is static and the same across all sites making it easy to identify a site by the credential data E7 31 68 .. .. ..

Description
No description provided
Readme 44 KiB
Languages
JavaScript 100%