Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Samsung Smart Tag #65

Open
trep-kalkyl opened this issue Oct 18, 2024 · 37 comments
Open

Implement Samsung Smart Tag #65

trep-kalkyl opened this issue Oct 18, 2024 · 37 comments

Comments

@trep-kalkyl
Copy link

Hi everyone,

Have you seen this repo?
Samsung SmartTag Hack

Samsung holds a significant market share in the tracker space, and this could be interesting for anyone looking into.

@trep-kalkyl
Copy link
Author

https://arxiv.org/pdf/2210.14702

@biemster
Copy link
Owner

I remember seeing that repo yes, but besides from the very impressive rom dump there is no more info there (as you already mentioned there). The paper on the other hand has a lot of info, and I did not see that one yet. I'll have a look soon.

@biemster
Copy link
Owner

@KieronQuinn does your work on the Samsung tags also include creating fake tags, or is it focused on using official tags on non-samsung androids?

@KieronQuinn
Copy link

@KieronQuinn does your work on the Samsung tags also include creating fake tags, or is it focused on using official tags on non-samsung androids?

Only official tags on non-Samsung. I'm not sure a similar setup to Open Haystack is even possible with Samsung's network, the Bluetooth communication is a lot more complex and would need replicating.

@KieronQuinn
Copy link

KieronQuinn commented Nov 29, 2024

Got a moment to spare so some more detail on the above:

  • The vast majority of calls to Samsung's API can be done on an account with no FME (find my everything) phone registered. Most of SmartThings' Tag functionality is actually functional on non-Samsung devices, with little modification. The FMD (Samsung's find my device) app has far more OneUI-specific calls, which is what I've needed to replace.
    • Registering a phone and uploading locations for other people's Tags require a certificate that has to have been generated on a real Samsung device. It uses the on-device attestation (SAK) to verify this. While I was able to extract a generated certificate and sign my own requests, the certificate could be revoked at any point so it would be pointless to share.
    • Registering a Tag does not require this certificate, nor does sending location updates for your own Tags
    • Setting and changing the offline location encryption PIN requires a Samsung FME phone on the account, but only its ID, so it can be spoofed. Simply having an old Samsung phone signed in to the account is enough to enable this functionality (it doesn't even need to be on)
  • There are signs that Samsung maybe intended for Tags to be available on non-Samsung phones (the biggest of which is the enum which differentiates between Samsung phone or tablet for who reported a location update also including entries for non-Samsung phone and non-Samsung tablet)
  • All BLE calls are encrypted, but the SmartThings app exposes a service (when given the right permissions) to access the Tag from another app. SmartThings then handles all the encryption and Bluetooth calls for you. I haven't needed to reverse the BLE encryption for this reason.
  • It's possible to use UWB with the Tags, but only with lower-level access to the UWB service (Shizuku allows this). The AndroidX implementation is not currently compatible (I wonder if this is what the delay is with Google's Find Device app too?)
  • As with other implementations, everything non-owner devices need most of the time is in the service data advertisement, but the BLE service has calls to ring and enable UWB locating for non-owners too. Unsure if these are also encrypted, I've not yet checked since Samsung's unknown tag scanner doesn't seem to work at all on my Pixels.

Like I said on the linked ticket, I plan to include extensive API documentation with my app once it's released. The app won't require root, but will come in the form of a modified SmartThings APK and a companion app (with the option of using the official SmartThings build and Xposed for rooted users)

@biemster
Copy link
Owner

I'm very much looking forward to the docs! My focus is on the BLE advertisement, I'll try to get that info from the paper linked a couple comments up.

@KieronQuinn
Copy link

I do have a list of characteristic IDs and what they're for, as well as the output of an endpoint which gives a list of "commands" that can be made on them if that'd be useful to you. I think it includes which are encrypted too.

@biemster
Copy link
Owner

I do have a list of characteristic IDs and what they're for, as well as the output of an endpoint which gives a list of "commands" that can be made on them if that'd be useful to you. I think it includes which are encrypted too.

Am I understanding correctly that the FD5A uuid in the advertisement is all a lost tag needs to transmit?

@KieronQuinn
Copy link

KieronQuinn commented Dec 1, 2024

I do have a list of characteristic IDs and what they're for, as well as the output of an endpoint which gives a list of "commands" that can be made on them if that'd be useful to you. I think it includes which are encrypted too.

Am I understanding correctly that the FD5A uuid in the advertisement is all a lost tag needs to transmit?

As far as I've been able to find, the only difference between tags that are connected to a device (not lost) and not (potentially lost) is a single flag in the advertisement data on that service: the tag state. Here's two decoded service datas:

Tag connected to phone:

TagData(
	version=1, 
	advertisingType=0, 
	tagState=5, 
	privacyId=*removed*, 
	regionId=11, 
	batteryLevel=3, 
	uwbFlag=1, 
	encryptionFlag=0, 
	motionDetection=0, 
	activityTrackingMode=false, 
	signature=*removed*, 
	reserved=[0, 0], 
	agingCounter=154849
)

Tag not connected to phone:

TagData(
	version=1, 
	advertisingType=0, 
	tagState=4, 
	privacyId=*removed*, 
	regionId=11, 
	batteryLevel=3, 
	uwbFlag=1, 
	encryptionFlag=0, 
	motionDetection=0, 
	activityTrackingMode=false, 
	signature=*removed*, 
	reserved=[0, 0], 
	agingCounter=154849
)

And here's the Kotlin code for decoding these items:

val version = (serviceData[0].toInt() and 0xF0) shr 4
val tagStateAndAdvertisementType = serviceData[0].toInt() and 15
val tagState = tagStateAndAdvertisementType and 7
val advertisementType = tagStateAndAdvertisementType shr 3 and 1
val agingCounter = serviceData[1].toInt() and 0xFF or
		((serviceData[2].toInt() and 0xFF) shl 8) or
		((serviceData[3].toInt() and 0xFF) shl 16)
val privacyId = ByteArray(8).apply {
	serviceData.copyInto(this, startIndex = 4, endIndex = 12)
}.toHexString()
val regionId = (serviceData[12].toInt() and 0xF0) shr 4
val flags = serviceData[12].toInt() and 15
val uwbFlag = flags shr 2 and 1
val encryptionFlag = flags shr 3 and 1
val batteryLevel = flags and 3
val motionDetection = 1 and ((serviceData[13].toInt() and 0xFF) shr 7)
val reserved = ByteArray(2).apply {
	serviceData.copyInto(this, startIndex = 14, endIndex = 16)
}
val activityTrackingMode = (serviceData[15].toInt() and 1) != 0
val signature = ByteArray(4).apply {
	serviceData.copyInto(this, startIndex = 16, endIndex = 20)
}

I'm just checking now to see if there's an enum in the SmartThings APK for what the tag states correspond to, but obviously 5 is connected and 4 is disconnected.

Edit: There doesn't seem to be one, it's always checking against just integers. Most likely it's been optimised out of the APK.

@KieronQuinn
Copy link

KieronQuinn commented Dec 1, 2024

Here's the full list of characteristics for a SmartTag2 from the https://client.smartthings.com/miniature/configure?profileId=*removed* endpoint: characteristics.json

And my own table of them that was built before I found that endpoint, based on what they were used for in the APK:

eedd5e73-6aa8-4673-8219-398a489da87c (Encryption/Auth Service)

Characteristic Modes Configuration Use
50f98bfd-158c-4efa-add4-0a70c2f5df5d R, W   Cipher
a12be31c-5b38-4773-9b9d-3d5735233a7c I, W 0x2902 Nonce
4ebe81f6-b952-465e-9ece-5ca39d4e8955 I, W 0x2902 Encrypted Nonce

0000fd5a-0000-1000-8000-00805f9b34f (Control Service)

Characteristic Modes Configuration Use
dee30001-182d-5496-b1ad-14f216324184 I, R, W 0x2902 Alarm
dee30002-182d-5496-b1ad-14f216324184 N, R, W 0x2902 Audio Volume
dee30003-182d-5496-b1ad-14f216324184 I, W 0x2902 Tag Button
dee30004-182d-5496-b1ad-14f216324184 I, R, W 0x2902 Battery
dee30005-182d-5496-b1ad-14f216324184 I, W 0x2902 Time
dee30006-182d-5496-b1ad-14f216324184 W   Factory Reset
dee30007-182d-5496-b1ad-14f216324184 R, W   E2E Encryption flag
dee30008-182d-5496-b1ad-14f216324184 R, W   UWB Ranging flag
dee30009-182d-5496-b1ad-14f216324184 I, W 0x2902 UWB Parameter
dee3000a-182d-5496-b1ad-14f216324184 R, W   Ringtone
dee3000b-182d-5496-b1ad-14f216324184 R   Firmware Version
dee3000c-182d-5496-b1ad-14f216324184 I, R, WNR 0x2902 Firmware Transfer
dee3000d-182d-5496-b1ad-14f216324184 R, W   Bond
dee3000e-182d-5496-b1ad-14f216324184 R   Spec Version
dee3000f-182d-5496-b1ad-14f216324184 I, W 0x2902 Bond
dee3001a-182d-5496-b1ad-14f216324184 I, W 0x2902 Private ID Pool Size
dee3001d-182d-5496-b1ad-14f216324184 I, R, W 0x2902 Activity Tracker
dee30025-182d-5496-b1ad-14f216324184 R, W   TxPower
dee3001f-182d-5496-b1ad-14f216324184 N, R, W 0x2902 Button Sound Volume
dee30030-182d-5496-b1ad-14f216324184 I, W 0x2902 Debug Flag
dee3001b-182d-5496-b1ad-14f216324184 R, W   Lost Message URL
dee3001c-182d-5496-b1ad-14f216324184 I, R 0x2902 Motion Detection
dee30041-182d-5496-b1ad-14f216324184 N, W 0x2902 Pet Activity

a0e78d39-75b5-4182-8fdc-c4b7365c9062 (?)

Characteristic Modes Configuration Use
040c6507-f08a-f7c2-abea-6382ee250230 N, WNR 0x2902 ?
cb91b0d6-3080-dbfb-876b-407db045e52b I, R, W 0x2902 ?

@danergo
Copy link

danergo commented Jan 27, 2025

I do have a list of characteristic IDs and what they're for, as well as the output of an endpoint which gives a list of "commands" that can be made on them if that'd be useful to you. I think it includes which are encrypted too.

Am I understanding correctly that the FD5A uuid in the advertisement is all a lost tag needs to transmit?

As far as I've been able to find, the only difference between tags that are connected to a device (not lost) and not (potentially lost) is a single flag in the advertisement data on that service: the tag state. Here's two decoded service datas:

Tag connected to phone:

TagData(
	version=1, 
	advertisingType=0, 
	tagState=5, 
	privacyId=*removed*, 
	regionId=11, 
	batteryLevel=3, 
	uwbFlag=1, 
	encryptionFlag=0, 
	motionDetection=0, 
	activityTrackingMode=false, 
	signature=*removed*, 
	reserved=[0, 0], 
	agingCounter=154849
)

Tag not connected to phone:

TagData(
	version=1, 
	advertisingType=0, 
	tagState=4, 
	privacyId=*removed*, 
	regionId=11, 
	batteryLevel=3, 
	uwbFlag=1, 
	encryptionFlag=0, 
	motionDetection=0, 
	activityTrackingMode=false, 
	signature=*removed*, 
	reserved=[0, 0], 
	agingCounter=154849
)

And here's the Kotlin code for decoding these items:

val version = (serviceData[0].toInt() and 0xF0) shr 4
val tagStateAndAdvertisementType = serviceData[0].toInt() and 15
val tagState = tagStateAndAdvertisementType and 7
val advertisementType = tagStateAndAdvertisementType shr 3 and 1
val agingCounter = serviceData[1].toInt() and 0xFF or
((serviceData[2].toInt() and 0xFF) shl 8) or
((serviceData[3].toInt() and 0xFF) shl 16)
val privacyId = ByteArray(8).apply {
serviceData.copyInto(this, startIndex = 4, endIndex = 12)
}.toHexString()
val regionId = (serviceData[12].toInt() and 0xF0) shr 4
val flags = serviceData[12].toInt() and 15
val uwbFlag = flags shr 2 and 1
val encryptionFlag = flags shr 3 and 1
val batteryLevel = flags and 3
val motionDetection = 1 and ((serviceData[13].toInt() and 0xFF) shr 7)
val reserved = ByteArray(2).apply {
serviceData.copyInto(this, startIndex = 14, endIndex = 16)
}
val activityTrackingMode = (serviceData[15].toInt() and 1) != 0
val signature = ByteArray(4).apply {
serviceData.copyInto(this, startIndex = 16, endIndex = 20)
}
I'm just checking now to see if there's an enum in the SmartThings APK for what the tag states correspond to, but obviously 5 is connected and 4 is disconnected.

Edit: There doesn't seem to be one, it's always checking against just integers. Most likely it's been optimised out of the APK.

Hi @KieronQuinn: amazing job.

Can you tell me: this kotlin code is decoding the "5AFD" service data, or some GATT characteristic value?

@KieronQuinn
Copy link

Hi @KieronQuinn: amazing job.

Can you tell me: this kotlin code is decoding the "5AFD" service data, or some GATT characteristic value?

The service data. Characteristics are encrypted for the most part.

@danergo
Copy link

danergo commented Jan 27, 2025

Thank you! One more thing: in case having 2 SmartTags, is there any way based on this advertisement data to distinguish which one is my BLE scanner seeing, or in case of seeing 5AFD service data I just can be sure there is "A" SmartTag nearby (I can't even be sure it belongs to me)?

I though either signature or privacyId is constant but it seems they are keep changing just as the mac.

@KieronQuinn
Copy link

I was never able to figure out that bit. SmartThings seems to know which are owned by you, so I've been using that to filter the scan results.

The API does send some privacy ID generation related code, so perhaps it's possible to work out which if you own it from that.

@danergo
Copy link

danergo commented Jan 27, 2025

Which API do you use for that?

@KieronQuinn
Copy link

https://client.smartthings.com/devices with the right authentication, I'll post the full API documentation soon

@danergo
Copy link

danergo commented Jan 28, 2025

https://client.smartthings.com/devices with the right authentication, I'll post the full API documentation soon

For me, this page is 401, maybe I need first login into somewhere before opening this link?

@KieronQuinn
Copy link

https://client.smartthings.com/devices with the right authentication, I'll post the full API documentation soon

For me, this page is 401, maybe I need first login into somewhere before opening this link?

Yes, like I said it needs the correct authentication. The auth system is complex so I won't explain it here now, but it's covered in my API documentation which will be posted soon.

@quack3d
Copy link

quack3d commented Jan 29, 2025

Just thought I'd link this component in case it's interesting: https://github.com/Vedeneb/HA-SmartThings-Find

Even though you have to add the Samsung Smart Tags from a Samsung phone, you can use this integration to track the tags afterwards. Once in a while (every 2 weeks perhaps) you have to reauth, but that's basically logging in again on your Samsung account from a browser.

@KieronQuinn
Copy link

Yes I saw that, but the web interface is very limited compared to the app

@danergo
Copy link

danergo commented Jan 31, 2025

One thing @KieronQuinn: I just realized, that even SmartThings Find doesn't work without network connection. It's easy to test with airplane mode. Simple it can't tell you wheather your SmartTag is nearby if it doesn't get internet. Pretty lame.

Can you confirm / do you know this API works without internet?

What I need is an Android app that exists on the same phone as SmartThings Find (if this matters), and able to identify my Tags via Bluetooth even when there is no internet coverage.

@KieronQuinn
Copy link

You can parse the service data without internet, but obviously the API call requires internet. The response can be cached just fine though.

@danergo
Copy link

danergo commented Jan 31, 2025

You can parse the service data without internet,

So in case I have the Bluetooth LE Advertisement's Service Data, I can parse it without internet? This sounds great!

but obviously the API call requires internet

What does exactly the API call do?

Are my "to-be" solution steps correct here?

1.) API call to initiate some key-exchange (Needs internet)
2.) Wait for API response with key-exchange (Needs internet)
3.) Store API response (No internet needed)
4.) Scan BLE, and parse service data with stored API response (No internet needed) ? Is this correct?

If so - how frequent do we have to call the API, i.e. how long is the key/response valid for?

Thank you!

@danergo
Copy link

danergo commented Feb 7, 2025

@KieronQuinn:

Yes, like I said it needs the correct authentication. The auth system is complex so I won't explain it here now, but it's covered in my API documentation which will be posted soon.

Do you mind sharing this? It might be not ready yet, but the auth part would help me continue my project.

Thank you!

@KieronQuinn
Copy link

@KieronQuinn:

Yes, like I said it needs the correct authentication. The auth system is complex so I won't explain it here now, but it's covered in my API documentation which will be posted soon.

Do you mind sharing this? It might be not ready yet, but the auth part would help me continue my project.

Thank you!

All documentation is now at https://github.com/KieronQuinn/uTag/wiki

@danergo
Copy link

danergo commented Feb 11, 2025

@KieronQuinn: wow. That's a nice and comprehensive writeup.

Would you mind putting me onto the correct track with my idea:

Goal: identify my SmartTag (there can be many SmartTags around, and I need to know in my app that it's seeing mine).

I can easily get the "BLE Service Data" on the Android phone in my app via Bluetooth (no server communication is needed for this).

Q1: Is my intention correct, that I need to decode "Privacy ID" to identify this SmartTag?

Important: I need to identify this SmartTag even during my phone is offline (no network coverage): you mentioned earlier here, that I can get the auth, then reuse it later.

Q2: Do I have to do this Authentication, and it will let me use the tokens for 90 days?

Q3: In case I have thies auth token bearer, is there any known way to decode the SmartTag's ID (again I only need to know if it's mine or not) without network coverage?

Note: in case we don't have network coverage SmartThings can't run at all.

@KieronQuinn
Copy link

If there is a way to use the privacy ID to know if the Tag belongs to you, I've not found it. In the response for the Device Info call (which needs that authentication token), there are fields in the metadata:

"privacyIdPoolSize":2000,"privacyIdSeed":"...","privacyIdInitialVector":"..."

(I removed the content but they're both base64 encoded strings)

These might be useful in working it out, and if they are you could store them offline for the check. The privacy ID changes often, so these values would only be part of the process.

@danergo
Copy link

danergo commented Feb 11, 2025

I see thank you!

One more question:

The privacy ID changes often

Is this change managed by the SmartTag itself without any BT phone nearby or for this change there must be a phone in BT range which can facilitate the process?

My scenario is that at first, my phone has network coverage, so it can get new token, new auth, etc, based on your Wiki. Then it will go offline for 1-2 days. During this 1-2 day I wish to monitor my SmartTag (battery and presence) with my phone. During this period there might or might not be other phones (and also SmartTags) around, which phones might or might not have network.

I assume SmartTag on its own can't change it's privacy ID, but in case it finds a compatible "peer" phone having valid network, it can suddenly get its new privacy ID via that peer phone from the internet. Is this correct?

@KieronQuinn
Copy link

The Tag changes its own privacy ID, it doesn't need another device nor a connection to the internet to do it. The inclusion of the IV and seed in the response from the server suggests the Tag holds the same value in its memory and uses them in combination with something reproducible (perhaps the timestamp to the minute or something) to generate an ID itself.

If this is the case, it might be possible to replicate given we can get the IV and seed.

The one exception to the "privacy ID changes often" rule is when it's in the "overmature offline" state, which happens between 12 and 24 hours after a disconnect. When this happens, the privacy ID stays the same until midnight, when it resets. If the owner doesn't connect again, it stays this way until the next midnight, and then changes again etc.

This is how unknown tag scans work, it provides an ID that's unchanged for a long enough time to be detected, but only when the Tag isn't with the owner so it couldn't be used as a way of a third party to track the owner.

@danergo
Copy link

danergo commented Feb 12, 2025

The Tag changes its own privacy ID, it doesn't need another device nor a connection to the internet to do it. The inclusion of the IV and seed in the response from the server suggests the Tag holds the same value in its memory and uses them in combination with something reproducible (perhaps the timestamp to the minute or something) to generate an ID itself.

Totally understandable.

The one exception to the "privacy ID changes often" rule is when it's in the "overmature offline" state, which happens between 12 and 24 hours after a disconnect. When this happens, the privacy ID stays the same until midnight, when it resets. If the owner doesn't connect again, it stays this way until the next midnight, and then changes again etc.

So there are two cases:

1.) Tag is in "idle" state: tag had some network coverage to retrieve IV and seed, and it uses these to generate new ID for itself frequently.

2.) Overmature offline state: happens after disconnect, generation of new ID is scheduled for midnight.

Is it a valid point to think that most tags are in overmature offline state in their life? I check mines regularly, but for sure not every single day. So 12-24hours always spent after disconnect (in case we mean "disconnect" when a user quits from SmartThings, or FindMy, etc, whichever establishes a directed BLE connection to the tag).

Thank you!

@KieronQuinn
Copy link

Nope, unless a tag is lost most will not enter overmature offline. The owner's phone makes sure to connect to the Tag often enough that the state is never entered. uTag does this too.

@biemster
Copy link
Owner

All documentation is now at https://github.com/KieronQuinn/uTag/wiki

Awesome, this is huge! I managed to scan through the whole thing but did not have enough time to dive in properly. Do you @KieronQuinn see a possibility to create "fake tags" like for the Apple (and very recently Google too!) networks?
Maybe the "track items" feature as you mention in KieronQuinn/uTag#2 could be abused for that?

@KieronQuinn
Copy link

All documentation is now at https://github.com/KieronQuinn/uTag/wiki

Awesome, this is huge! I managed to scan through the whole thing but did not have enough time to dive in properly. Do you @KieronQuinn see a possibility to create "fake tags" like for the Apple (and very recently Google too!) networks? Maybe the "track items" feature as you mention in KieronQuinn/uTag#2 could be abused for that?

I've not looked at that part of the API, since SmartThings handles it for me. I'll see if I can find some time to have a look some time.

@KieronQuinn
Copy link

KieronQuinn commented Feb 17, 2025

Privacy ID documentation: https://github.com/KieronQuinn/uTag/wiki/BLE-Privacy-ID

You can indeed use the response data from the device info call to re-generate the privacy IDs. You end up with 2000 of them, but once you have the pool it's as simple as checking if it contains the privacy ID of a nearby Tag to know if it belongs to you. This could also be used to identify the Tag without directly logging in, so long as you have the required data from the device info response (which could have been captured elsewhere). It would also work offline once the key, IV and seed have been obtained.

It's also worth noting that the encryption key from the response also seems to be used for BLE communication, replacing "privacy" in the hash generation with bleAuthentication. There's also one called signing which I think is used for verifying the response, but I'm not entirely sure.

@danergo
Copy link

danergo commented Feb 17, 2025

In case tag is nearby to my phone, will it use these 2000 privacy IDs and once it finished it will request a generation of a new pool?

15mins * 2000 would mean more than 20days with same privacy pool.

Or it will request a new pool in every 15mins?

Thank you!

@KieronQuinn
Copy link

The pool is always the same. There is no regeneration.

@danergo
Copy link

danergo commented Feb 17, 2025

The pool is always the same. There is no regeneration.

Yupeee! So in case we can get the pool in our application, we can always identify our SmartTags!

15mins * 2000 would mean more than 20days with same privacy pool.

This would mean, that after 21 days, SmartTags' IDs will be repeated from the same pool (until the SmartTag's owner "signes in into SmartThings on a new device").

We can now proceed with our design. Thank you so much @KieronQuinn.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants