Skip to content

Chaser Authentication

Kieron Quinn edited this page Feb 10, 2025 · 2 revisions

The Chaser service uses an authentication system which does not require login. Keystores are used to sign a nonce and sent when generating an access token which can be used to send location updates for other SmartTags, as well as getting encryption keys for Tag locations where a Tag has end to end encryption enabled.

Generating a Keystore

Certificate generation must be done on an Android device. libfmm_ct.so provides the native code to generate the certificate, and has some integrity checks that must be defeated for it to return valid data:

  • The caller must have the package name com.samsung.android.fmm
  • The package com.samsung.android.fmm must have the shared user ID android.uid.system

It's possible to defeat both of these checks without needing to be a system app, root, or even using Shizuku. Check the classes FmmContext and FmmPackageManager for implementation.

JNI calls to the native library generate the keystore, alias and passwords. Calls must be made from com.samsung.android.fmm.maze.FmmFontJNI:

Method Parameters Returns
getFont1 None Keystore password
getFont2 Context* Base64 encoded Keystore
getFont3 None Key alias
getFont4 None Key password

* This Context must be the Context which fakes the existence of the FMM package & its shared user ID from above.

Note: While it is possible to reuse a keystore, it's also possible that Samsung may revoke them. To prevent this as much as possible, uTag generates a new keystore every time the app is restarted. The same native library is used on generations of Samsung devices, so blocking all keystores generated using this method would in theory also block a significant number of real users. If you are intending to use this API outside of Android and are not going to be the only one using it, consider creating a small companion app which generates a unique keystore for your usage from a real device, rather than hardcoding it.

Loading Keystore

The Keystore is in BKS (Bouncy Castle) format. For inspection, it can be loaded using Keystore Explorer, using the provided passwords & alias. On Android, it's loaded as follows:

val cert = Base64.decode(rawCertificate.certificate, Base64.NO_WRAP).inputStream()
KeyStore.getInstance("BKS").apply {
    load(cert, rawCertificate.keystorePassword.toCharArray())
}

Where rawCertificate is a data class containing the results of the JNI calls.

Certificate Chain

After the keystore has been loaded, get the certificate chain for the provided alias, then combine the 2nd and 1st certificates (in that order) in the chain and Base64 encode it. For example, in Kotlin:

private fun encodeChain(certificates: Array<Certificate>): String? {
    if (certificates.size >= 2) {
        val certs = certificates[1].encoded + certificates[0].encoded
        return Base64.encodeToString(certs, Base64.NO_WRAP)
    }
    return null
}

val certificate = try {
    keyStore.getCertificateChain(rawCertificate.alias)?.let {
        encodeChain(it)
    }
} catch (e: Exception) {
    null
}

Where rawCertificate is a data class containing the results of the JNI calls, and keyStore is the loaded Keystore.

Private Key

After the keystore has been loaded, get the private key for the provided alias using the key password. For example, in Kotlin:

val privateKey = try {
    keyStore.getKey(
        rawCertificate.alias, rawCertificate.keyPass.toCharArray()
    ) as? PrivateKey
} catch (e: Exception) {
    null
}

Where rawCertificate is a data class containing the results of the JNI calls, and keyStore is the loaded Keystore.

Region URL

There are several regions a Tag can be registered to, and location updates must be sent to the right one. The Tag's service data contains a region ID (see the service data documentation for how to decode it), which are mapped to URLs as follows:

Region ID URL
NA03D 1 chaser-na03d-useast2.samsungiots.com
NA03S 3 chaser-na03s-useast2.samsungiots.com
EU02S 5 chaser-eu02s-euwest1.samsungiots.com
NA03A 7 chaser-na03a-useast2.samsungiots.com
NA03 10 chaser-na03-useast2.samsungiotcloud.com
EU02 11 chaser-eu02-euwest1.samsungiotcloud.com
AP03 12 chaser-ap03-apnortheast2.samsungiotcloud.com

The relevant URL will from this point be referred to as the "base URL".

Nonce

You can generate a nonce by making an unauthenticated call:

Request

GET https://<base URL>/nonce

Query Parameters

None

Response

Key Type Value
nonce String The generated nonce

Signing the Nonce

Sign the generated nonce using the private key loaded from the keystore, in the SHA256withRSA algorithm, then encode it in Base64. For example:

val signature = try {
    Signature.getInstance("SHA256withRSA").apply {
        initSign(privateKey)
        update(nonce.toByteArray())
    }.sign().let {
        Base64.encodeToString(it, Base64.NO_WRAP)
    }
} catch (e: Exception) {
    return null
}

Where privateKey is the loaded private key from the keystore, and nonce is the server-generated nonce.

Generating a Key Pair

In addition to the certificate generated by FMM, we also need to generate and store our own key pair which will be used in the encryption process. It must match the following specifications:

Spec Value
Algorithm RSA
Subject Name: CN=ChaserKeyStore
Digests SHA-256, SHA-512
Signature Padding PKCS1
Encryption Padding PKCS1
Serial Number 0x539
Key Size 0x800

Store this key pair, on Android it should be stored in the Android keystore.

Getting an Access Token

Now we are ready to get an access token. Make a call to the same server as before:

Request

POST https://<base URL>/accesstoken

Headers

Key Type Value
x-iot-findnode-version Long FMM version code, eg. 731802100
signature String Signed nonce
certificate String Encoded certificate chain
X-Iot-Findnode-Publickey String Base64 encoded public key from key pair
x-iot-findnode-type String MOVING
x-iot-findnode-host String GALAXY_PHONE
nonce String Server-generated nonce

Response

Key Type Value
accessToken String The access token
expirationTime Long Expiry timestamp of token
findNode Find Node Configuration information that gets sent back to the server later

Clone this wiki locally