Skip to content

Latest commit

 

History

27 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Technical Report: SAKE library Reverse Engineering

Sake Cryptographic Library (libandroid-sake-lib_v210.so → v260 diff)

Date: June 15, 2026 (updated with v260 passkey/SRP analysis)

Status: 100% Analysis Coverage (Zero Unnamed Functions Remaining) (FOR v210!) — v260 SRP/passkey subsystem reversed (§24–25)

  1. Technical Report: SAKE library Reverse Engineering
    1. Sake Cryptographic Library (libandroid-sake-lib_v210.so → v260 diff)
    2. 0. Final Binary Statistics
    3. 1. Executive Summary & Binary Metadata
      1. 1.1. Core Metadata
    4. 2. Memory Map & Section Structure
    5. 3. Cryptographic Implementation Details
      1. 3.1. AES-128 Symmetric Engine
        1. 3.1.1. S-Box Table (AES_SBOX)
        2. 3.1.2. Round Constants (AES_RCON)
      2. 3.2. Secure Messaging Encryption: AES-128-CTR
      3. 3.3. Message Authentication: AES-CMAC
        1. 3.3.1. Galois Field Multiplication (SakeCrypto_GF_Double at 0x0001749c)
        2. 3.3.2. MAC Appending and Truncation
    6. 4. Reconstructed Struct Layouts & Definitions
      1. 4.1. Core Handshake State Structure (SAKE_CLIENT_S / SAKE_SERVER_S)
      2. 4.2. Key Database Structure (SAKE_KEY_DATABASE_S)
    7. 5. Reverse Engineered Function Catalog
      1. 5.1. SakeClient_HandshakeHandler
      2. 5.2. SakeClient_GenerateChallenge
      3. 5.3. SakeUtil_ReadRandomBytes
      4. 5.4. SakeKeyDB_FindEntryByType
      5. 5.5. SakeCrypto_AES_ProcessBlock
      6. 5.6. SakeCrypto_AES_CTR_Transform
      7. 5.7. SakeClient_InitChallengeAndSetState
      8. 5.8. SakeClient_GenerateAndEncryptSessionProof
      9. 5.9. SakeClient_ProcessHandshakeStep4_FinalizeAndValidate
      10. 5.10. SakeCrypto_EncryptSignAndIncrementSequence
      11. 5.11. SakeClient_GenerateChallengeAndPackOutput
      12. 5.12. SakeClient_VerifyHandshakeProof
      13. 5.13. SakeClient_VerifyHandshakeMAC
      14. 5.14. SakeKeyDB_LookupEntryByType
      15. 5.15. SakeClient_DeriveSessionKeysAndFillRandom
      16. 5.16. SakeClient_PerformKeyConfirmation
      17. 5.17. SakeCrypto_HandshakeVerifyAndDeriveKeys
      18. 5.18. SakeUtil_GenerateRandomIterationCount
      19. 5.19. SakeSecureMessage_GetSequenceByte
      20. 5.20. SakeClient_DecryptAndValidateServerPermit
      21. 5.21. SakeUtil_IsChallengeBufferEmpty
      22. 5.22. SakeUtil_FillChallengeWithRandom
      23. 5.23. SakeCrypto_InitCipherState
      24. 5.24. SakeKeyDB_IsValidDeviceType
      25. 5.25. SakeCrypto_FindAlgorithmDescriptor
      26. 5.26. SakeCrypto_ExecuteCipherVtableOp
      27. 5.27. SakeCrypto_DispatchBlockCipher
      28. 5.28. SakeCrypto_AES_TTable_EncryptBlock
      29. 5.29. SakeCrypto_AES_ECB_SingleBlockEncrypt
      30. 5.30. SakeCrypto_AES_CTR_DecryptBlock
      31. 5.31. SakeCrypto_DispatchCipherOperation
      32. 5.32. SakeCrypto_ClearAESContext
      33. 5.33. SakeCrypto_ResetAESContext
      34. 5.34. SakeCrypto_ComputeCMAC
      35. 5.35. SakeCrypto_ResetBuffer
      36. 5.36. SakeKeyDB_GetEntryByte
      37. 5.37. SakeKeyDB_ValidateBufferState
      38. 5.38. SakeKeyDB_LookupAndValidateType
    8. 6. End-to-End Handshake & Secure Messaging Protocol Flow
      1. 6.1. Reimplementation Specifications
    9. 7. Permit Subsystem (SAKE_PERMIT_S)
      1. 7.1. Permit Structure (SAKE_PERMIT_S)
      2. 7.2. Permit Error Strings
      3. 7.3. Permit Functions
        1. 7.3.1. SakePermit_InitStruct
        2. 7.3.2. SakePermit_EncryptAndSign
        3. 7.3.3. SakeClient_DecryptAndValidateServerPermit
        4. 7.3.4. SakeClient_GetServerPermit
        5. 7.3.5. SakeServer_GetClientPermit
      4. 7.4. Secure Link Functions
        1. 7.4.1. SakeClient_SecureForSending
        2. 7.4.2. SakeClient_UnsecureAfterReceiving
        3. 7.4.3. SakeServer_SecureForSending
        4. 7.4.4. SakeServer_UnsecureAfterReceiving
      5. 7.5. Server State Machine (Permit Flow)
        1. 7.5.1. SakeServer_ProcessState1_InitOrReset (State 1)
        2. 7.5.2. SakeServer_ProcessState2_Exchange (State 2)
        3. 7.5.3. SakeServer_ProcessState3_DeriveAndVerify (State 3)
        4. 7.5.4. SakeServer_ProcessState4_DecryptPermit (State 4)
        5. 7.5.5. SakeServer_ProcessState6_Finalize (State 6)
        6. 7.5.6. SakeServer_DeriveHandshakeKeys
        7. 7.5.7. SakeServer_ParseClientChallenge
        8. 7.5.8. SakeServer_VerifyKeyDerivation
        9. 7.5.9. SakeServer_SetDefaultState
    10. 8. Reconstructed Struct Layouts (Ghidra-Verified)
      1. 8.1. SAKE_CLIENT_S (120 bytes, Ghidra-verified)
      2. 8.2. SAKE_SERVER_S (168 bytes, Ghidra-verified)
      3. 8.3. SAKE_PERMIT_S (Final)
      4. 8.4. SAKE_SECURE_LINK_S (48 bytes, Ghidra-verified)
      5. 8.5. SAKE_SECURE_MESSAGE_S (8 bytes, Ghidra-verified)
      6. 8.6. Permit Wire Format
    11. 9. Ghidra Datatype System (Created & Applied)
    12. 10. JNI Interface Functions (Permit-Related)
    13. 11. SecureMessage Subsystem (Runtime Encryption/Decryption)
      1. 10.1. Secure Message Wire Format
      2. 10.2. Message Size Constraints
      3. 10.3. Encrypt Flow (Sake_SecureMessage_EncryptAndSign)
      4. 10.4. Decrypt Flow (Sake_SecureMessage_DecryptAndAuthenticate)
      5. 10.5. SecureMessage Functions
        1. 10.5.1. SakeCrypto_EncryptSignMessage
        2. 10.5.2. sakeCrypto_DecryptAuthenticate_HighLevel
        3. 10.5.3. SakeCrypto_ComputeMACAndAppend
        4. 10.5.4. SakeCrypto_EncryptMessage_HighLevel
        5. 10.5.5. SakeCrypto_PerformEncryptDecrypt
        6. 10.5.6. sakeCrypto_LowLevelPrimitive
        7. 10.5.7. SakeSecureMessage_GetSequenceByte
        8. 10.5.8. SakeCrypto_HandshakeWrapper
        9. 10.5.9. SakeCrypto_InitCipherAndAllocateState
        10. 10.5.10. SakeCrypto_ProcessCipherBlock
        11. 10.5.11. SakeCrypto_GetCipherOutputSize
        12. 10.5.12. SakeCrypto_ValidateCipherContext
    14. 12. JNI Interface Functions (SecureMessage-Related)
    15. 13. SAKE_SECURE_MESSAGE_S Structure
      1. 12.1. Secure Link State (Embedded in Client/Server)
    16. 14. Complete Function Call Graph (SecureMessage Path)
    17. 15. Key Database Management
      1. 15.1. Database Format
      2. 15.2. Key Database Functions
        1. 15.2.1. SakeKeyDB_CreateDatabase
        2. 15.2.2. SakeKeyDB_OpenDatabase
        3. 15.2.3. SakeKeyDB_AddEntry
        4. 15.2.4. SakeKeyDB_CalculateEntryOffset
        5. 15.2.5. SakeKeyDB_ResetAndRecomputeCRC
        6. 15.2.6. SakeKeyDB_ComputeCRC32
        7. 15.2.7. SakeKeyDB_ValidateCRC
    18. 16. UserMessage Subsystem
      1. 16.1. SAKE_USER_MESSAGE_S Structure (36 bytes, Ghidra-verified)
      2. 16.2. Size Constraints
      3. 16.3. UserMessage JNI Functions
    19. 17. Error Handling Infrastructure
      1. 17.1. Error Code Range
      2. 17.2. Error Strings (Embedded in .rodata)
      3. 17.3. Error Functions
        1. 17.3.1. SakeError_IsValidCode
        2. 17.3.2. SakeError_GetStringForCode
        3. 17.3.3. SakeError_ParseStringToCode
        4. 17.3.4. SakeJNI_HandleKeyDatabaseError
        5. 17.3.5. SakeJNI_ClientGetLastError
        6. 17.3.6. SakeJNI_ServerGetLastError
      4. 17.4. Handshake Error JNI Functions
    20. 18. Complete JNI API Surface
      1. 18.1. Client Lifecycle
      2. 18.2. Client Handshake
      3. 18.3. Client Secure Messaging
      4. 18.4. Server Lifecycle
      5. 18.5. Server Handshake
      6. 18.6. Server Secure Messaging
      7. 18.7. Key Database
      8. 18.8. SecureMessage
      9. 18.9. Permit
      10. 18.10. Utility
      11. 18.11. Java Class: NativeSakeLib (Extracted from classes.dex)
      12. 18.12. Missing Functions (Not in Binary)
    21. 19. JNI Struct Accessors (Complete)
      1. 19.1. SAKE_CLIENT_S Accessors
      2. 19.2. SAKE_SERVER_S Accessors
      3. 19.3. SAKE_PERMIT_S Accessors
      4. 19.4. SAKE_SECURE_MESSAGE_S Accessors
      5. 19.5. SAKE_KEY_DATABASE_S Accessors
    22. 20. Crypto Helper Functions
    23. 21. ARM Unwinding / C++ Runtime Infrastructure
      1. 21.1. ARM Unwinding Functions
      2. 21.2. C++ Runtime Functions
    24. 22. Binary Statistics
      1. 22.1. Coverage Summary
    25. 23. Device Type Table & Enums
      1. 23.1. Device Type Table
      2. 23.2. All Enums (Ghidra-Verified)
      3. 23.3. Enum Application to Struct Fields
      4. 23.4. Enum Application to Function Prototypes
    26. 24. v260 New Subsystem: Passkey / SRP-6a Secure Authentication
      1. 24.1. New Data Types (Ghidra-created)
      2. 24.2. SRP Struct Field Layout
      3. 24.3. Passkey Message Wire Format
      4. 24.4. JNI Surface (new in v260)
      5. 24.5. SRP State Machine
      6. 24.6. Hash / HMAC Layer (embedded)
      7. 24.7. Bignum Library (28-bit limbs)
      8. 24.8. Handshake State Handlers (updated in v260)
    27. 25. Usage: Passkey-Based SRP-6a Authentication
      1. 25.1. When This Applies
      2. 25.2. Call Sequence (Java / JNI)
      3. 25.3. SRP-6a Protocol Steps (mapped to binary)
      4. 25.4. Security Model — NO Public-Key / Certificate Checking

0. Final Binary Statistics

Metric Value
Total functions 337
FUN_* remaining 0
Functions renamed ~120+
EOL comments added ~80+
Ghidra structs created 7
Ghidra enums created 7
Struct fields with enums 9
Function prototypes with enums 8
Labels created 150+
Sections documented 24
SAKE-specific functions ~150
ARM/NDK infrastructure ~187

1. Executive Summary & Binary Metadata

This document serves as the comprehensive, authoritative reverse engineering and reimplementation guide for the libandroid-sake-lib_v210.so binary. This shared library implements SAKE, a proprietary protocol used to secure Bluetooth Low Energy (BTLE) GATT messaging between an Android client and Medtronic Minimed medical devices.

1.1. Core Metadata

  • Filename: libandroid-sake-lib_v210.so
  • Format: Executable and Linking Format (ELF) 32-bit Shared Object
  • Target Architecture: ARM (32-bit, Little-Endian, v8-A/v8-R profile)
  • Default Base Address: 0x00010000
  • Compiler Toolchain: GCC / Android NDK (Clang/LLVM with GCC compatibility wrappers)
  • Security Mechanisms:
    • Stack Smashing Protector (Canary): Enabled (__stack_chk_guard / __stack_chk_fail)
    • Position Independent Code (PIC/PIE): Enabled
    • Symbol Stripping: Non-stripped (symbol table and dynamic linking symbols are fully intact)

2. Memory Map & Section Structure

The binary layout is structured into standard ELF segments. The virtual addresses (relative to base 0x00010000) are detailed below:

Section Name Start Address End Address Access Flags Description
segment_1.1 0x00010000 0x00010133 R-- ELF Header & Program Headers
.note.android.ident 0x00010134 0x000101cb R-- NDK target API levels
.note.gnu.build-id 0x000101cc 0x000101ef R-- GNU Build Identifier Hash
.dynsym 0x000101f0 0x00010c1f R-- Dynamic Linker Symbols (163 entries)
.dynstr 0x00010c20 0x00013335 R-- Dynamic String Constants
.hash 0x00013338 0x000137d7 R-- Symbol Hash Table for lookups
.gnu.version 0x000137d8 0x0001391d R-- Symbol Version Requirements
.rel.dyn 0x0001397c 0x00013c33 R-- Non-PLT dynamic relocations
.rel.plt 0x00013c34 0x00013d03 R-- PLT-specific dynamic relocations
.plt 0x00013d04 0x00013e4f R-X Procedure Linkage Table (stub entries)
.text 0x00013e50 0x00018c23 R-X Executable Code Section (Instructions)
.ARM.exidx 0x00018c24 0x0001940b R-- Exception handling index table
.rodata 0x00019a24 0x0001cb67 R-- Read-only Constants, S-Boxes, Strings
.data.rel.ro 0x0001dc50 0x0001de67 RW- Relocations made read-only after load
.dynamic 0x0001de68 0x0001df67 RW- Dynamic linking structural descriptors
.got 0x0001df70 0x0001dfff RW- Global Offset Table
.data 0x0001e000 0x0001e007 RW- Initialized Mutable Global Variables
.bss 0x0001e008 0x0001e027 RW- Uninitialized Mutable Global Variables

3. Cryptographic Implementation Details

The library implements standard symmetric cryptosystems, heavily optimized using traditional table-lookup architectures to avoid runtime overhead in resource-constrained environments.

3.1. AES-128 Symmetric Engine

The core symmetric cipher is standard AES-128. However, instead of executing shifts, substitutions, and mix-column transformations on the fly, the compiler has flattened the cipher rounds into AES T-Tables mapping to memory page 0x0001a540.

3.1.1. S-Box Table (AES_SBOX)

Located at address 0x0001a568, this contains the standard Rijndael substitution box (256 bytes):

63 7c 77 7b f2 6b 6f c5 30 01 67 2b fe d7 ab 76
ca 82 c9 7d fa 59 47 f0 ad d4 a2 af 9c a4 72 c0
b7 fd 93 26 36 3f f7 cc 34 a5 e5 f1 71 d8 31 15
04 c7 23 c3 18 96 05 9a 07 12 80 e2 eb 27 b2 75
... [standard AES S-box layout]

3.1.2. Round Constants (AES_RCON)

Located at address 0x0001a540, this represents the Rijndael round constant array used during the key schedule expansion:

01 00 00 00  02 00 00 00  04 00 00 00  08 00 00 00
10 00 00 00  20 00 00 00  40 00 00 00  80 00 00 00
1b 00 00 00  36 00 00 00

3.2. Secure Messaging Encryption: AES-128-CTR

Data payloads are encrypted using AES-128-CTR (Counter Mode).

  • Counter Properties: The counter is a full 16-byte (128-bit) big-endian block.
  • Implementation (SakeCrypto_AES_CTR_Transform at 0x00016be6):
    • Checks the block offset index. If it hits a block boundary (modulo 16), it invokes the underlying raw block cipher SakeCrypto_AES_EncryptBlock (0x00016590) on the counter block to yield a fresh 16-byte keystream.
    • Performs a big-endian multi-byte increment on the counter structure:
      // Inlined counter addition loop
      int i = 15;
      do {
          char val = counter_block[i];
          counter_block[i] = val + 1;
          i--;
      } while (val == -1); // Carry propagation if byte overflowed (0xFF -> 0x00)
    • Applies the keystream to the data using parallel bitwise XOR operations.

3.3. Message Authentication: AES-CMAC

For message integrity, the library implements a variant of AES-CMAC (NIST SP 800-38B).

3.3.1. Galois Field Multiplication (SakeCrypto_GF_Double at 0x0001749c)

To derive the subkeys $K_1$ and $K_2$ used to process complete and incomplete blocks, the library implements shift-and-XOR finite-field doubling in $GF(2^8)$.

  • 128-bit Block Mode (Polynomial 0x87): If the size parameter is 0x10 (16 bytes), the shift overflow is XORed with standard AES polynomial parameter: $$\text{Irreducible Polynomial} = x^7 + x^2 + x + 1 \implies 0x87$$
  • 64-bit Block Mode (Polynomial 0x1b): If the size is 8 bytes, the fallback reduction uses the 64-bit polynomial: $$\text{Polynomial} = x^4 + x^3 + x + 1 \implies 0x1b$$
  • Inline C Implementation:
    byte carry = 0;
    for (int i = size - 1; i >= 0; i--) {
        byte next_carry = input[i] >> 7;
        output[i] = (input[i] << 1) | carry;
        carry = next_carry;
    }
    if (carry) {
        output[size - 1] ^= polynomial; // 0x87 or 0x1b depending on block size
    }

3.3.2. MAC Appending and Truncation

  • The raw AES-CMAC operation generates a full 16-byte authentication tag.
  • Truncation: The high-level secure message wrapper (SakeCrypto_EncryptSignMessage at 0x00015028) truncates the 128-bit MAC down to a 16-bit (2-byte) tag.
  • This 2-byte tag is appended to the tail end of the encrypted payload before BLE transmission. The receiver extracts this tag, recalculates the CMAC over the payload and sequence context, and performs a 16-bit comparison.

4. Reconstructed Struct Layouts & Definitions

To reconstruct the protocol or interface with the library, the following memory layouts must be compiled exactly.

4.1. Core Handshake State Structure (SAKE_CLIENT_S / SAKE_SERVER_S)

This structure tracks the overall cryptographic state of a session. It is approximately 0x1A0 bytes in size.

typedef struct {
    uint32_t currentState;                // +0x00: Current state machine index (1-8)
    uint32_t lowSequenceNumber;           // +0x04: Incremented per message (Counter Low)
    uint32_t highSequenceNumber;          // +0x08: Counter High for 64-bit sequence tracking
    uint8_t  localChallenge[20];          // +0x0C: Locally generated 20-byte random challenge (Rc)
    uint8_t  remoteChallenge[20];         // +0x20: Received 20-byte remote challenge (Rs)
    void*    cryptoVtablePtr;             // +0x34: Points to the cryptosystem descriptor vtable (0x0001de04)
    uint32_t dataLength;                  // +0x38: Working buffer length
    uint8_t  sessionKey_CTR[16];          // +0x3C: Inferred Derived AES-CTR Session Key
    uint8_t  sessionKey_CMAC[16];         // +0x4C: Inferred Derived AES-CMAC Session Key
    uint32_t handshakeStatus;             // +0x74: Error/Status flags (2 = Error, 8 = Success)
} SAKE_CLIENT_S;

4.2. Key Database Structure (SAKE_KEY_DATABASE_S)

The library manages an internal file-backed database representing trusted keys.

  • Database Header: *dbPtr contains a base descriptor. *dbPtr + 5 tracks the active entryCount.
  • Entries: Array of packed 81-byte entries (0x51 bytes per entry).
#pragma pack(push, 1)
typedef struct {
    uint8_t  deviceType;                  // +0x00: Device identifier type (compares via strcmp/char)
    uint8_t  reserved[6];                 // +0x01: Padding and metadata
    uint8_t  key1[16];                    // +0x07: Cryptographic Key 1
    uint8_t  key2[16];                    // +0x17: Handshake Root/Verification Key (K_root)
    uint8_t  key3[16];                    // +0x27: Cryptographic Key 3
    uint8_t  key4[16];                    // +0x37: Cryptographic Key 4
    uint8_t  key5[16];                    // +0x47: Cryptographic Key 5
    uint8_t  reserved_tail[10];           // +0x57: Trailing entry padding
} SAKE_KEY_ENTRY;
#pragma pack(pop)

5. Reverse Engineered Function Catalog

The following is an exhaustive directory of reversed functions compiled during this session.

5.1. SakeClient_HandshakeHandler

  • Address: 0x000145b6
  • Signature: uint32_t SakeClient_HandshakeHandler(void* clientContext, void* clientBufferPtr, int* stateMachineStatePtr)
  • Description: The master driver for the client handshake. It evaluates *stateMachineStatePtr and routes execution to specific state processing functions:
    • State 1 (Init/Reset): Calls HandleHandshakeStateInitOrError (0x00014992).
    • State 2 (Exchange): Calls SakeClient_ProcessHandshakeStep2 (0x000149c4) to process and validate incoming remote challenges.
    • State 3 (Derivation): Calls SakeClient_ProcessHandshakeStep3 (0x00014a42) to run the key derivation algorithm.
    • State 4 (Authentication): Calls SakeCrypto_PerformEncryptDecrypt (0x00014f44) to cryptographically sign/verify the validation payload.

5.2. SakeClient_GenerateChallenge

  • Address: 0x00015c3c
  • Signature: uint32_t SakeClient_GenerateChallenge(int param_1)
  • Description: Called during handshake setup. Sets the challenge length to 0x14 (20 bytes) and invokes SakeUtil_ReadRandomBytes to populate the structure buffer with strong random material from /dev/urandom.

5.3. SakeUtil_ReadRandomBytes

  • Address: 0x00017518
  • Signature: uint32_t SakeUtil_ReadRandomBytes(uint32_t byteCount, int destinationBuffer)
  • Description: Opens /dev/urandom in read-only mode ("r") using standard libc fopen. Iterates byteCount times, calling fgetc to read raw hardware-derived entropy, and copies the stream to destinationBuffer. Closes the stream on completion or read failure.

5.4. SakeKeyDB_FindEntryByType

  • Address: 0x000151fa
  • Signature: int* SakeKeyDB_FindEntryByType(int* dbPtr, char deviceType)
  • Description: Iterates through the loaded database structure. It starts at index 0 and steps through memory by multiplying the current index by entry size 0x51 (81 bytes). Compares the first byte of each entry with deviceType. Returns a direct pointer to the matched entry or NULL if not found.

5.5. SakeCrypto_AES_ProcessBlock

  • Address: 0x00016244
  • Signature: uint64_t SakeCrypto_AES_ProcessBlock(int* cryptoState, uint* inputData, uint modeFlag)
  • Description: The core block cipher execution engine. Decides round limits and algorithm behavior based on block flags (0x80 -> 128-bit ECB/CTR; 0x100 -> 256-bit CBC). Performs high-performance round transformations via T-tables.

5.6. SakeCrypto_AES_CTR_Transform

  • Address: 0x00016be6
  • Signature: uint32_t SakeCrypto_AES_CTR_Transform(void* cipherContext, int bufferPtr, uint* stateOutput, int inputData, int outputBuffer, byte* inputKeyOrState, byte* outputByte)
  • Description: Applies AES-CTR encryption/decryption. Generates keystream blocks by calling the block cipher on the 16-byte counter block, updates the big-endian counter by 1, and XORs the data blocks.

5.7. SakeClient_InitChallengeAndSetState

  • Address: 0x00014a22
  • Signature: uint32_t SakeClient_InitChallengeAndSetState(int clientCtx, int bufferPtr)
  • Description: Generates 20-byte challenge via /dev/urandom, sets handshake state to 0x0B (challenge ready), clears buffer at +0x20. Returns 8 on success, 7 on failure.

5.8. SakeClient_GenerateAndEncryptSessionProof

  • Address: 0x00014ad0
  • Signature: void SakeClient_GenerateAndEncryptSessionProof(int clientCtx, int bufferPtr)
  • Description: Reads 16 random bytes, prepares cipher state via SakeCrypto_InitCipherState, encrypts via SakeCrypto_EncryptSignAndIncrementSequence. Error states: 0x0D (random fail), 0x0F (encrypt fail).

5.9. SakeClient_ProcessHandshakeStep4_FinalizeAndValidate

  • Address: 0x00014b58
  • Signature: void SakeClient_ProcessHandshakeStep4_FinalizeAndValidate(int clientCtx, int serverBuffer, int outputPtr)
  • Description: State 4 finalizer. Looks up key DB entry, verifies client handshake proof, encrypts/decrypts server permit, validates encrypted response, generates 1 random byte, copies 48-byte result to +0x40. Error states: 0x0C-0x13.

5.10. SakeCrypto_EncryptSignAndIncrementSequence

  • Address: 0x00014e3c
  • Signature: void SakeCrypto_EncryptSignAndIncrementSequence(int* cipherState, int plaintext, int output, int extra)
  • Description: Core encrypt+sign wrapper. Calls sakeCrypto_LowLevelPrimitive then SakeCrypto_EncryptSignMessage. On success, increments 64-bit sequence counter at param_1[2]/param_1[3]. Called from 6 locations.

5.11. SakeClient_GenerateChallengeAndPackOutput

  • Address: 0x00015a78
  • Signature: bool SakeClient_GenerateChallengeAndPackOutput(byte deviceType, uint* out1, uint* out2, uint* challengeBuf)
  • Description: Generates 20-byte challenge, copies 8 bytes to output buffer, sets device type byte at +0x10 of output.

5.12. SakeClient_VerifyHandshakeProof

  • Address: 0x00014d94
  • Signature: void SakeClient_VerifyHandshakeProof(uint* localChallenge, uint* remoteChallenge, int keyMaterial, int output)
  • Description: Copies local and remote challenges (8 bytes each) into stack buffer, calls SakeCrypto_VerifyHandshakeStep1 to validate proof.

5.13. SakeClient_VerifyHandshakeMAC

  • Address: 0x00015e3c
  • Signature: void SakeClient_VerifyHandshakeMAC(uint* challengeData, int keyBuf, int macBuf, int extra, uint* out1, uint* out2)
  • Description: Verifies handshake MAC. Checks challenge length == 20 bytes, extracts keys from structure, calls SakeCrypto_HandshakeWrapper, then SakeCrypto_Memcmp to compare computed vs expected MAC.

5.14. SakeKeyDB_LookupEntryByType

  • Address: 0x00015360
  • Signature: int SakeKeyDB_LookupEntryByType(int* dbPtr, int deviceType)
  • Description: Validates device type via SakeKeyDB_IsValidDeviceType, searches key database via SakeKeyDB_FindEntryByType, returns entry + 1 or 0 if not found.

5.15. SakeClient_DeriveSessionKeysAndFillRandom

  • Address: 0x00015b6c
  • Signature: void SakeClient_DeriveSessionKeysAndFillRandom()
  • Description: Step 3 driver. Calls SakeCrypto_HandshakeVerifyAndDeriveKeys, then SakeUtil_FillChallengeWithRandom to pad challenge to 20 bytes.

5.16. SakeClient_PerformKeyConfirmation

  • Address: 0x00015cb8
  • Signature: void SakeClient_PerformKeyConfirmation()
  • Description: Key confirmation loop. Generates random iteration count (1-4), loops that many times: reads 16 random bytes + 32 random bytes, calls SakeCrypto_VerifyMessage to confirm key material.

5.17. SakeCrypto_HandshakeVerifyAndDeriveKeys

  • Address: 0x00015ae0
  • Signature: void SakeCrypto_HandshakeVerifyAndDeriveKeys(int ctx, uint* keys, int challenge, int extra, uint* output)
  • Description: Handshake verify + key derivation. Calls SakeCrypto_HandshakeWrapper to derive keys, then SakeCrypto_VerifyMessage to validate derived material. Copies 20 bytes from challenge.

5.18. SakeUtil_GenerateRandomIterationCount

  • Address: 0x00015c68
  • Signature: void SakeUtil_GenerateRandomIterationCount(char* output)
  • Description: Reads 1 random byte from /dev/urandom, masks with & 3, adds 1. Result is random value 1-4 used as iteration count for key confirmation.

5.19. SakeSecureMessage_GetSequenceByte

  • Address: 0x0001500c
  • Signature: uint32_t SakeSecureMessage_GetSequenceByte(int msgStruct, byte* output)
  • Description: Extracts sequence byte from secure message. Reads offset at +0x20, subtracts 3, bounds-checks < 0x1D, returns byte at that index.

5.20. SakeClient_DecryptAndValidateServerPermit

  • Address: 0x0001553c
  • Signature: void SakeClient_DecryptAndValidateServerPermit(int encData, int keyData, int verifyData, uint* output)
  • Description: Decrypts server permit. Calls SakeCrypto_AES_ECB_SingleBlockEncrypt, then SakeCrypto_VerifyMessage, extracts byte fields, validates state, copies 10 bytes to output.

5.21. SakeUtil_IsChallengeBufferEmpty

  • Address: 0x00015f04
  • Signature: void SakeUtil_IsChallengeBufferEmpty(void* buffer)
  • Description: Checks if challenge buffer is empty. Verifies +0x20 == 20 bytes, clears 20-byte buffer, compares with memcmp. Returns true if buffer is zero-filled (uninitialized).

5.22. SakeUtil_FillChallengeWithRandom

  • Address: 0x00015c4e
  • Signature: uint32_t SakeUtil_FillChallengeWithRandom(int buffer)
  • Description: Fills challenge buffer with random. Checks if length < 20, sets to 20, calls SakeUtil_ReadRandomBytes to fill remaining bytes from /dev/urandom.

5.23. SakeCrypto_InitCipherState

  • Address: 0x00014dec
  • Signature: void SakeCrypto_InitCipherState(uint* state, uint mode, int key, uint* iv1, uint* iv2)
  • Description: Initializes 48-byte cipher state struct. Sets mode flag, copies 16-byte key, sets block size 0x100, zeroes sequence counters.

5.24. SakeKeyDB_IsValidDeviceType

  • Address: 0x00015f5c
  • Signature: bool SakeKeyDB_IsValidDeviceType(int deviceType)
  • Description: Returns (param_1 - 1) < 7. Checks if device type byte is in valid range 1-7.

5.25. SakeCrypto_FindAlgorithmDescriptor

  • Address: 0x00016ce8
  • Signature: int SakeCrypto_FindAlgorithmDescriptor(int id, int mode, int blockSize)
  • Description: Walks crypto algorithm descriptor table at +0x1c, matches by algorithm ID, mode, and block size. Returns matching descriptor or NULL.

5.26. SakeCrypto_ExecuteCipherVtableOp

  • Address: 0x00016dac
  • Signature: uint32_t SakeCrypto_ExecuteCipherVtableOp(int* cipherCtx, int extra, int blockSize, int mode)
  • Description: Reads cipher vtable pointer at +0x1c, dispatches to function at +0x0C (encrypt) or +0x10 (decrypt) based on mode flag. Indirect call through vtable.

5.27. SakeCrypto_DispatchBlockCipher

  • Address: 0x00016bc8
  • Signature: uint32_t SakeCrypto_DispatchBlockCipher(int ctx, int mode, int input, int output)
  • Description: Block cipher dispatcher. If mode == 1, calls SakeCrypto_AES_EncryptBlock (ECB); otherwise calls SakeCrypto_AES_TTable_EncryptBlock (T-table optimized).

5.28. SakeCrypto_AES_TTable_EncryptBlock

  • Address: 0x00016890
  • Signature: uint32_t SakeCrypto_AES_TTable_EncryptBlock(int* keySchedule, uint* input, byte* output)
  • Description: Full AES T-table encryption. Uses 4 lookup tables (Te0-Te3) at DAT_00016bb4-DAT_00016bc0, S-box at DAT_00016bc4. 10/12/14 round unrolled loop. Standard Rijndael T-table optimization.

5.29. SakeCrypto_AES_ECB_SingleBlockEncrypt

  • Address: 0x000161a4
  • Signature: void SakeCrypto_AES_ECB_SingleBlockEncrypt(int keyData, int inputBlock, int outputBlock)
  • Description: AES-ECB single block encrypt. Allocates 280-byte AES context, expands key via SakeCrypto_AES_KeyExpansion_Inferred with 128-bit block size (0x80), encrypts via SakeCrypto_DispatchBlockCipher.

5.30. SakeCrypto_AES_CTR_DecryptBlock

  • Address: 0x00016124
  • Signature: void SakeCrypto_AES_CTR_DecryptBlock(uint* keyData, int ivData, int stateCtx, int outputState, int outputInfo)
  • Description: AES-CTR decrypt block. Allocates 280-byte AES context, expands key, copies 16-byte IV/nonce, calls SakeCrypto_AES_CTR_Transform to decrypt block.

5.31. SakeCrypto_DispatchCipherOperation

  • Address: 0x00016e5c
  • Signature: int SakeCrypto_DispatchCipherOperation(int* cipherCtx, int inputLen, int dataLen, int outputLen, int* written)
  • Description: Master cipher dispatcher. Checks algorithm type at +4, mode at +0x18, block alignment, dispatches via vtable function pointers at +0x1c. Handles ECB, CBC, CTR modes.

5.32. SakeCrypto_ClearAESContext

  • Address: 0x00016220
  • Signature: void SakeCrypto_ClearAESContext(int* context)
  • Description: Clears 280-byte AES context structure via __aeabi_memclr4.

5.33. SakeCrypto_ResetAESContext

  • Address: 0x0001622e
  • Signature: void SakeCrypto_ResetAESContext(int* context)
  • Description: Resets 280-byte AES context to zero.

5.34. SakeCrypto_ComputeCMAC

  • Address: 0x00015fe4
  • Signature: void SakeCrypto_ComputeCMAC(int* buffer)
  • Description: Clears 60-byte buffer structure via __aeabi_memclr4.

5.35. SakeCrypto_ResetBuffer

  • Address: 0x00015eec
  • Signature: void SakeCrypto_ResetBuffer(int* buffer)
  • Description: Resets generic buffer via __aeabi_memclr4.

5.36. SakeKeyDB_GetEntryByte

  • Address: 0x00015328
  • Signature: byte SakeKeyDB_GetEntryByte(int entry)
  • Description: Returns single byte from key database entry at offset +4 of the structure.

5.37. SakeKeyDB_ValidateBufferState

  • Address: 0x00015ec6
  • Signature: void SakeKeyDB_ValidateBufferState(int state)
  • Description: Validates buffer state by calling SakeKeyDB_IsValidDeviceType on the state value.

5.38. SakeKeyDB_LookupAndValidateType

  • Address: 0x00015336
  • Signature: int SakeKeyDB_LookupAndValidateType(int* dbPtr, int deviceType)
  • Description: Calls SakeKeyDB_FindEntryByType then SakeKeyDB_IsValidDeviceType to validate the result.

6. End-to-End Handshake & Secure Messaging Protocol Flow

Reimplementing a compatible SAKE client requires reproducing this exact execution flow:

    Client (Android)                                     Server (Device)
           |                                                    |
           | 1. SakeClient_GenerateChallenge()                  |
           |    Generates 20-Byte Rc                            |
           |                                                    |
           | ---------- Rc (Client Challenge) ------------->    |
           |                                                    |
           |                                                    | 2. Generates 20-Byte Rs
           |                                                    |    and looks up K_root
           |                                                    |
           | <--------- Rs (Server Challenge) + Auth --------   |
           |                                                    |
           | 3. SakeKeyDB_FindEntryByType()                     |
           |    Locates 16-byte K_root (Key 2)                  |
           |                                                    |
           | 4. SakeCrypto_VerifyHandshakeStep1()               |
           |    Decrypts and validates peer proof              |
           |                                                    |
           | ---------- Authenticated Session Established ----> |
           |                                                    |
           | 5. SakeCrypto_EncryptSignMessage()                 |
           |    Applies AES-128-CTR + 16-bit CMAC               |
           |                                                    |

6.1. Reimplementation Specifications

  1. Handshake Verification Key ($K_{root}$): To derive correct handshake proof payloads, extract the pre-shared secret key from Key 2 (offset 0x17) of the device's corresponding 81-byte database row.
  2. Ciphertext Frame Format: Secure GATT payloads are packed as:
    +--------------------------------+--------------------------+---------------------+
    | Encrypted Payload (AES-CTR)    | Sequence byte (CTR LS)   | 16-bit Truncated MAC|
    |          N Bytes               |          1 Byte          |       2 Bytes       |
    +--------------------------------+--------------------------+---------------------+
    
  3. Authentication Verification: During message validation, extract the trailing 2 bytes, run the standard CMAC verification over the encrypted body with the matching sequence state, apply the $GF(2^8)$ doubling logic, and assert that the derived high 16 bits match the appended MAC bytes.

7. Permit Subsystem (SAKE_PERMIT_S)

The permit subsystem handles the creation, encryption, signing, decryption, and authentication of permits — cryptographic tokens exchanged during the handshake to establish trust between client and server.

7.1. Permit Structure (SAKE_PERMIT_S)

#pragma pack(push, 1)
typedef struct {
    uint32_t version;                     // +0x00: Permit version (SAKE_PERMIT_VERSION_E)
    uint32_t deviceType;                  // +0x04: Device type identifier (1-7)
    uint8_t  proprietaryBytes[10];        // +0x08: 10 proprietary bytes (device-specific)
    uint16_t reserved;                    // +0x12: Reserved/padding
} SAKE_PERMIT_S;                          // Total: 0x14 (20 bytes)
#pragma pack(pop)

7.2. Permit Error Strings

The following error strings are embedded in .rodata and thrown during permit operations:

Address String Context
0x00019a59 "Attempt to dereference null SAKE_PERMIT_VERSION_E" Null version check
0x00019e98 "The session key could not be used to receive the permit" Decryption failure
0x00019ed0 "The permit could not be secured for sending" Encrypt+sign failure
0x00019efc "The permit could not be padded for sending" Padding failure
0x00019f27 "The permit received is not padded" Padding validation
0x00019f49 "The permit received is invalid" General validation
0x00019f68 "The permit received is issued to a different device type" Device type mismatch

7.3. Permit Functions

7.3.1. SakePermit_InitStruct

  • Address: 0x00015494
  • Signature: void SakePermit_InitStruct(SAKE_PERMIT_S* permit, uint32_t version)
  • Description: Initializes a 20-byte SAKE_PERMIT_S structure. Sets version at +0x00, zeroes deviceType, proprietaryBytes, and reserved. Called by Sake_Permit_Init JNI.

7.3.2. SakePermit_EncryptAndSign

  • Address: 0x000154a4
  • Signature: void SakePermit_EncryptAndSign(SAKE_PERMIT_S* permit, int keyData, int macKey, int output)
  • Description: Encrypts and signs a permit for transmission:
    1. Extracts version (byte 0), deviceType (byte 1), and 10 proprietaryBytes (bytes 2-11) from the permit.
    2. Calls SakeCrypto_ComputeCMAC to compute MAC over the 12-byte payload.
    3. Calls SakeCrypto_VerifyHandshakeStep1 to AES-ECB encrypt the permit block.

7.3.3. SakeClient_DecryptAndValidateServerPermit

  • Address: 0x0001553c
  • Signature: void SakeClient_DecryptAndValidateServerPermit(int encData, int keyData, int macKey, uint* output)
  • Description: Decrypts and validates a server permit:
    1. AES-ECB decrypts the encrypted permit via SakeCrypto_AES_ECB_SingleBlockEncrypt.
    2. Validates MAC via SakeCrypto_ComputeCMAC.
    3. Extracts version (byte 0), deviceType (byte 1), and 10 proprietaryBytes (bytes 2-11).
    4. Validates deviceType is in range 1-7 via SakeKeyDB_IsValidDeviceType.
    5. Copies 10 proprietary bytes to output at +2.

7.3.4. SakeClient_GetServerPermit

  • Address: 0x00014d74
  • Signature: SAKE_PERMIT_S* SakeClient_GetServerPermit(SAKE_CLIENT_S* client)
  • Description: Returns pointer to server permit at client + 0x1c if isSecureLinkEstablished at +0x70 is set. Returns NULL otherwise.

7.3.5. SakeServer_GetClientPermit

  • Address: 0x00015a54
  • Signature: SAKE_PERMIT_S* SakeServer_GetClientPermit(SAKE_SERVER_S* server)
  • Description: Returns pointer to client permit at server + 0x1c if isSecureLinkEstablished at +0xa0 is set. Returns NULL otherwise.

7.4. Secure Link Functions

7.4.1. SakeClient_SecureForSending

  • Address: 0x00014d50
  • Signature: uint32_t SakeClient_SecureForSending(SAKE_CLIENT_S* client)
  • Description: If isSecureLinkEstablished(+0x70) is set, calls SakeCrypto_EncryptSignAndIncrementSequence at client + 0x40 to encrypt an outgoing message.

7.4.2. SakeClient_UnsecureAfterReceiving

  • Address: 0x00014d62
  • Signature: uint32_t SakeClient_UnsecureAfterReceiving(SAKE_CLIENT_S* client)
  • Description: If isSecureLinkEstablished(+0x70) is set, calls SakeCrypto_PerformEncryptDecrypt at client + 0x40 to decrypt an incoming message.

7.4.3. SakeServer_SecureForSending

  • Address: 0x00015a30
  • Signature: uint32_t SakeServer_SecureForSending(SAKE_SERVER_S* server)
  • Description: If isSecureLinkEstablished(+0xa0) is set, calls SakeCrypto_EncryptSignAndIncrementSequence at server + 0x70 to encrypt an outgoing message.

7.4.4. SakeServer_UnsecureAfterReceiving

  • Address: 0x00015a42
  • Signature: uint32_t SakeServer_UnsecureAfterReceiving(SAKE_SERVER_S* server)
  • Description: If isSecureLinkEstablished(+0xa0) is set, calls SakeCrypto_PerformEncryptDecrypt at server + 0x70 to decrypt an incoming message.

7.5. Server State Machine (Permit Flow)

The server handshake state machine processes permits through these states:

7.5.1. SakeServer_ProcessState1_InitOrReset (State 1)

  • Address: 0x00015620
  • Description: Checks if challenge buffer is empty. If empty, gets device type from +0x68, calls SakeServer_SetDefaultState to initialize. Routes to state 3 (exchange) or state 6 (finalize).

7.5.2. SakeServer_ProcessState2_Exchange (State 2)

  • Address: 0x00015692
  • Description: Parses client challenge via SakeServer_ParseClientChallenge. Looks up key DB entry. Derives handshake keys via SakeServer_DeriveHandshakeKeys. Generates server challenge.

7.5.3. SakeServer_ProcessState3_DeriveAndVerify (State 3)

  • Address: 0x000157a4
  • Description: Performs key confirmation via SakeClient_PerformKeyConfirmation. Verifies key derivation via SakeServer_VerifyKeyDerivation. Verifies handshake proof. Initializes cipher state at +0x38. Encrypts+signs permit via SakeCrypto_EncryptSignAndIncrementSequence.

7.5.4. SakeServer_ProcessState4_DecryptPermit (State 4)

  • Address: 0x000158d4
  • Description: Checks challenge length == 20. Looks up key DB. Decrypts via SakeCrypto_PerformEncryptDecrypt. Validates server permit via SakeClient_DecryptAndValidateServerPermit. Copies secure link state to +0x70. Sets isSecureLinkEstablished at +0xa0.

7.5.5. SakeServer_ProcessState6_Finalize (State 6)

  • Address: 0x00015710
  • Description: Reads 16 random bytes. Initializes cipher state at +0x38. Encrypts+signs via SakeCrypto_EncryptSignAndIncrementSequence. Error states: 0x0D (random fail), 0x0F (encrypt fail).

7.5.6. SakeServer_DeriveHandshakeKeys

  • Address: 0x00015dc0
  • Description: Generates server challenge via SakeClient_GenerateChallenge. Calls SakeCrypto_HandshakeWrapper to derive session keys from client+server challenges and key material.

7.5.7. SakeServer_ParseClientChallenge

  • Address: 0x00015aae
  • Description: Extracts 8 bytes challenge (+0), device type byte (+0x10). Validates device type 1-7 via SakeKeyDB_IsValidDeviceType. Returns 1 on success, 0 on failure.

7.5.8. SakeServer_VerifyKeyDerivation

  • Address: 0x00015bd4
  • Description: Checks challenge length == 20. Calls SakeCrypto_HandshakeVerifyAndDeriveKeys. Then SakeCrypto_Memcmp to verify derived key matches expected.

7.5.9. SakeServer_SetDefaultState

  • Address: 0x00015eb8
  • Description: Sets device type (+0), state (+1). Fills challenge buffer with random bytes via SakeUtil_FillChallengeWithRandom. Sets buffer length to 2 at +0x20.

8. Reconstructed Struct Layouts (Ghidra-Verified)

All structs below have been created in Ghidra's datatype system and applied to function parameters.

8.1. SAKE_CLIENT_S (120 bytes, Ghidra-verified)

typedef struct {
    uint32_t currentState;                // +0x00: Current state machine index (1-8)
    uint32_t lowSequenceNumber;           // +0x04: Incremented per message (Counter Low)
    uint32_t highSequenceNumber;          // +0x08: Counter High for 64-bit sequence tracking
    uint8_t  localChallenge[20];          // +0x0C: Locally generated 20-byte random challenge (Rc)
    uint8_t  remoteChallenge[20];         // +0x20: Received 20-byte remote challenge (Rs)
    uint32_t deviceType;                  // +0x34: Device type identifier
    void*    cryptoVtablePtr;             // +0x38: Points to the cryptosystem descriptor vtable (0x0001de04)
    uint32_t dataLength;                  // +0x3C: Working buffer length
    SAKE_SECURE_LINK_S secureLink;        // +0x40: Secure link cipher state (48 bytes)
    uint8_t  isSecureLinkEstablished;     // +0x70: Flag: 1 = secure link active
    uint32_t handshakeStatus;             // +0x74: Error/Status flags (2 = Error, 8 = Success)
} SAKE_CLIENT_S;                          // Total: 0x78 (120 bytes)

8.2. SAKE_SERVER_S (168 bytes, Ghidra-verified)

typedef struct {
    uint32_t currentState;                // +0x00: Current state machine index
    uint8_t  localChallenge[20];          // +0x04: Server challenge
    uint8_t  remoteChallenge[20];         // +0x18: Client challenge
    SAKE_PERMIT_S clientPermit;           // +0x1C: Client permit (20 bytes, overlaps with remoteChallenge)
    uint32_t deviceType;                  // +0x30: Client device type
    void*    cryptoVtablePtr;             // +0x34: Crypto vtable pointer
    SAKE_SECURE_LINK_S cipherState;       // +0x38: Cipher state for handshake
    uint8_t  keyDatabase[48];             // +0x68: Key database reference
    SAKE_SECURE_LINK_S secureLink;        // +0x70: Secure link cipher state
    uint8_t  isSecureLinkEstablished;     // +0xA0: Flag: 1 = secure link active
    uint32_t lastError;                   // +0xA4: Last error code
} SAKE_SERVER_S;                          // Total: 0xA8 (168 bytes)

8.3. SAKE_PERMIT_S (Final)

#pragma pack(push, 1)
typedef struct {
    uint32_t version;                     // +0x00: Permit version (SAKE_PERMIT_VERSION_E)
    uint32_t deviceType;                  // +0x04: Device type identifier (1-7)
    uint8_t  proprietaryBytes[10];        // +0x08: 10 proprietary bytes (device-specific)
    uint16_t reserved;                    // +0x12: Reserved/padding
} SAKE_PERMIT_S;                          // Total: 0x14 (20 bytes)
#pragma pack(pop)

8.4. SAKE_SECURE_LINK_S (48 bytes, Ghidra-verified)

typedef struct {
    uint32_t mode;                        // +0x00: Cipher mode flag (0 = decrypt, 1 = encrypt)
    uint32_t reserved1;                   // +0x04: Reserved
    uint32_t counterLow;                  // +0x08: Low 32 bits of 64-bit sequence counter
    uint32_t counterHigh;                 // +0x0C: High 32 bits of 64-bit sequence counter
    uint32_t blockSize;                   // +0x10: Block size (0x100 = 256-bit, 0x80 = 128-bit)
    uint32_t flags;                       // +0x14: Operation flags
    uint8_t  key[16];                     // +0x18: 16-byte session key
    uint32_t iv1;                         // +0x28: IV/nonce part 1
    uint32_t iv2;                         // +0x2C: IV/nonce part 2
} SAKE_SECURE_LINK_S;                     // Total: 0x30 (48 bytes)

8.5. SAKE_SECURE_MESSAGE_S (8 bytes, Ghidra-verified)

typedef struct {
    uint8_t* pBytes;                      // +0x00: Pointer to message byte array
    uint32_t byteCount;                   // +0x04: Number of bytes in message
} SAKE_SECURE_MESSAGE_S;

8.6. Permit Wire Format

When a permit is encrypted for transmission over BLE:

+------------------------------------------+
| Encrypted Permit Block (AES-ECB)         |
|  12 bytes: version(1) + devType(1) +     |
|            proprietaryBytes(10)           |
+------------------------------------------+
| MAC Tag (AES-CMAC, truncated to 16-bit)  |
|  2 bytes                                  |
+------------------------------------------+

9. Ghidra Datatype System (Created & Applied)

All structs below have been created in Ghidra's datatype system and applied to function parameters across the binary.

Struct Name Size Fields Applied To
SAKE_PERMIT_S 20 bytes version, deviceType, proprietaryBytes[10], reserved SakePermit_InitStruct, SakePermit_EncryptAndSign, SakeClient_DecryptAndValidateServerPermit, SakeClient_GetServerPermit, SakeServer_GetClientPermit
SAKE_SECURE_MESSAGE_S 8 bytes pBytes, byteCount SakeCrypto_EncryptSignMessage, sakeCrypto_DecryptAuthenticate_HighLevel, SakeCrypto_PerformEncryptDecrypt, SakeSecureMessage_GetSequenceByte, SakeCrypto_ComputeMACAndAppend, SakeClient_HandshakeHandler, SakeServer_HandshakeHandler
SAKE_SECURE_LINK_S 48 bytes mode, counterLow, counterHigh, blockSize, flags, key[16], iv1, iv2 SakeCrypto_EncryptSignMessage, sakeCrypto_DecryptAuthenticate_HighLevel, SakeCrypto_PerformEncryptDecrypt, SakeCrypto_InitCipherState, SakeCrypto_EncryptMessage_HighLevel, SakeCrypto_AES_CTR_DecryptBlock
SAKE_CLIENT_S 120 bytes currentState, lowSequenceNumber, highSequenceNumber, localChallenge[20], remoteChallenge[20], deviceType, cryptoVtablePtr, dataLength, secureLink, isSecureLinkEstablished, handshakeStatus SakeClient_HandshakeHandler, SakeClient_SecureForSending, SakeClient_UnsecureAfterReceiving, SakeClient_GetServerPermit
SAKE_SERVER_S 168 bytes currentState, localChallenge[20], remoteChallenge[20], clientPermit, deviceType, cryptoVtablePtr, cipherState, keyDatabase[48], secureLink, isSecureLinkEstablished, lastError SakeServer_HandshakeHandler, SakeServer_SecureForSending, SakeServer_UnsecureAfterReceiving, SakeServer_GetClientPermit, all server state handlers

10. JNI Interface Functions (Permit-Related)

JNI Function Address Internal Call Description
Sake_Permit_Init 0x000142f4 SakePermit_InitStruct Allocates and initializes 20-byte permit
Sake_Permit_EncryptAndSign 0x00014360 SakePermit_EncryptAndSign Encrypts permit for transmission
Sake_Permit_DecryptAndAuthenticate 0x00014372 SakeClient_DecryptAndValidateServerPermit Decrypts and validates received permit
Sake_Client_GetServerPermit 0x000145e0 SakeClient_GetServerPermit Returns server permit pointer
Sake_Server_GetClientPermit 0x000148f8 SakeServer_GetClientPermit Returns client permit pointer
SAKE_CLIENT_S_serverPermit_get 0x00014488 JNI getter for server permit
SAKE_CLIENT_S_serverPermit_set 0x0001446e JNI setter for server permit
SAKE_SERVER_S_clientPermit_get 0x000146f8 JNI getter for client permit
SAKE_SERVER_S_clientPermit_set 0x000146de JNI setter for client permit
SAKE_PERMIT_S_version_get 0x00014280 JNI getter for permit version
SAKE_PERMIT_S_version_set 0x00014268 JNI setter for permit version
SAKE_PERMIT_S_deviceType_get 0x000142ac JNI getter for permit device type
SAKE_PERMIT_S_deviceType_set 0x00014294 JNI setter for permit device type
SAKE_PERMIT_S_pProprietaryBytes_get 0x000142d4 JNI getter for proprietary bytes pointer
SAKE_PERMIT_S_pProprietaryBytes_set 0x000142be JNI setter for proprietary bytes pointer
SAKE_PERMIT_PROPRIETARY_BYTE_COUNT_get 0x00014260 Returns constant 10
new_SAKE_PERMIT_S 0x000142dc malloc(0x14) Allocates new permit struct
delete_SAKE_PERMIT_S 0x000142ec free Frees permit struct

11. SecureMessage Subsystem (Runtime Encryption/Decryption)

The SecureMessage subsystem is the data plane — it handles encrypting and decrypting application-level messages AFTER the handshake establishes a secure link. This is what the Java application actually uses to send/receive data over BLE.

10.1. Secure Message Wire Format

+---------------------------------------------------+
| Encrypted Payload (AES-128-CTR)                   |
|   N bytes (original data length)                  |
+---------------------------------------------------+
| Sequence Byte (CTR counter low byte)              |
|   1 byte                                          |
+---------------------------------------------------+
| Truncated MAC (AES-CMAC, 16-bit)                  |
|   2 bytes                                         |
+---------------------------------------------------+
| Total overhead: 3 bytes per message               |
+---------------------------------------------------+

10.2. Message Size Constraints

  • MIN: MIN_SAKE_SECURE_MESSAGE_BYTE_COUNT (JNI constant at 0x0001401c)
  • MAX: MAX_SAKE_SECURE_MESSAGE_BYTE_COUNT (JNI constant at 0x00014022)
  • Effective max payload: 30 bytes (0x1E) — checked at SakeCrypto_EncryptSignMessage
  • Effective min payload: 3 bytes — checked at sakeCrypto_DecryptAuthenticate_HighLevel
  • Max wire size: 33 bytes (30 payload + 3 overhead)

10.3. Encrypt Flow (Sake_SecureMessage_EncryptAndSign)

JNI Layer
  └─ SakeCrypto_EncryptSignMessage @ 0x00015028
       ├─ Check dataLen < 0x1E (30 bytes)
       ├─ Set outputLen = dataLen + 3
       ├─ SakeCrypto_EncryptMessage_HighLevel @ 0x00016078
       │    ├─ SakeCrypto_ClearAESContext (280-byte context)
       │    ├─ SakeCrypto_AES_ProcessBlock (mode 0x80 = 128-bit)
       │    ├─ Copy 16-byte IV/nonce
       │    └─ SakeCrypto_AES_CTR_Transform (encrypt payload)
       ├─ SakeCrypto_ComputeMACAndAppend @ 0x000150c8
       │    ├─ Copy 16-byte key
       │    ├─ Copy encrypted payload to MAC buffer
       │    └─ SakeCrypto_ComputeCMAC (compute AES-CMAC)
       ├─ Write sequence byte at (output + dataLen)
       └─ Write 2-byte MAC at (output + dataLen + 1)

10.4. Decrypt Flow (Sake_SecureMessage_DecryptAndAuthenticate)

JNI Layer
  └─ sakeCrypto_DecryptAuthenticate_HighLevel @ 0x0001512c
       ├─ Check dataSize 3-32 bytes
       ├─ Set outputLen = dataSize - 3
       ├─ SakeCrypto_ComputeMACAndAppend @ 0x000150c8
       │    ├─ Copy 16-byte key
       │    ├─ Copy encrypted payload to MAC buffer
       │    └─ SakeCrypto_ComputeCMAC (compute AES-CMAC)
       ├─ Compare computed MAC (16-bit) with stored MAC
       └─ If match: SakeCrypto_AES_CTR_DecryptBlock @ 0x00016124
            ├─ SakeCrypto_ClearAESContext
            ├─ SakeCrypto_AES_KeyExpansion_Inferred
            └─ SakeCrypto_AES_CTR_Transform (decrypt payload)

10.5. SecureMessage Functions

10.5.1. SakeCrypto_EncryptSignMessage

  • Address: 0x00015028
  • Signature: void SakeCrypto_EncryptSignMessage(int cryptoContext, byte data, int outputBuffer, int sequenceNumber, int outputState)
  • Description: The master encrypt+sign function. Checks dataLen < 0x1E, sets outputLen = dataLen + 3. Calls SakeCrypto_EncryptMessage_HighLevel for AES-CTR encryption, then SakeCrypto_ComputeMACAndAppend for MAC. Appends sequence byte at -3 offset and 2-byte MAC at -2 offset.

10.5.2. sakeCrypto_DecryptAuthenticate_HighLevel

  • Address: 0x0001512c
  • Signature: void sakeCrypto_DecryptAuthenticate_HighLevel(int inputBuffer, int encryptedData, uint dataSize, int outputState)
  • Description: The master decrypt+authenticate function. Checks dataSize 3-32, sets outputLen = dataSize - 3. Calls SakeCrypto_ComputeMACAndAppend to verify MAC, compares 16-bit tag. If valid, calls SakeCrypto_AES_CTR_DecryptBlock to decrypt.

10.5.3. SakeCrypto_ComputeMACAndAppend

  • Address: 0x000150c8
  • Signature: void SakeCrypto_ComputeMACAndAppend(int messageContext, int messageData, int outputBuffer, int processFlag, int outputStateInfo)
  • Description: Computes AES-CMAC over the encrypted payload. Copies 16-byte key, copies payload to MAC buffer, calls SakeCrypto_ComputeCMAC to compute MAC. Returns 16-bit truncated MAC.

10.5.4. SakeCrypto_EncryptMessage_HighLevel

  • Address: 0x00016078
  • Signature: void SakeCrypto_EncryptMessage_HighLevel(uint* cryptoContext, int data, int outputBuffer, int sequenceNumber, int outputState)
  • Description: AES-CTR encryption wrapper. Clears 280-byte AES context, processes block with mode 0x80 (128-bit), copies 16-byte IV/nonce, calls SakeCrypto_AES_CTR_Transform to encrypt, resets context.

10.5.5. SakeCrypto_PerformEncryptDecrypt

  • Address: 0x00014f44
  • Signature: void SakeCrypto_PerformEncryptDecrypt(int* cipherState, int messageBuffer, int outputBuffer)
  • Description: Handshake-level encrypt/decrypt. Extracts sequence byte via SakeSecureMessage_GetSequenceByte, updates 64-bit counter (low at +0x10, high at +0x14), calls sakeCrypto_LowLevelPrimitive to build CTR block, then sakeCrypto_DecryptAuthenticate_HighLevel. Increments sequence on success.

10.5.6. sakeCrypto_LowLevelPrimitive

  • Address: 0x00014ed0
  • Signature: void sakeCrypto_LowLevelPrimitive(byte* output, int keyData, uint seqLow, int seqHigh, uint mode, uint* key1, uint* key2)
  • Description: Constructs 16-byte AES-CTR counter block from 64-bit sequence number. Big-endian encoding of sequence into counter bytes. Copies key material at offsets +5 and +9. Zeroes unused bytes.

10.5.7. SakeSecureMessage_GetSequenceByte

  • Address: 0x0001500c
  • Signature: uint32_t SakeSecureMessage_GetSequenceByte(int msgStruct, byte* output)
  • Description: Extracts sequence byte from secure message. Reads offset at +0x20, subtracts 3, bounds-checks < 0x1D, returns byte at that index.

10.5.8. SakeCrypto_HandshakeWrapper

  • Address: 0x00015d4c
  • Signature: void SakeCrypto_HandshakeWrapper(uint* challenge1, uint* challenge2, int keyData, int macKey, uint* output)
  • Description: Handshake key derivation MAC. Copies 8 bytes from each challenge buffer, copies 16-byte key, calls SakeCrypto_ComputeCMAC to compute MAC over 32-byte (0x20) combined data. Returns 8-byte MAC.

10.5.9. SakeCrypto_InitCipherAndAllocateState

  • Address: 0x00016ffc
  • Signature: int SakeCrypto_InitCipherAndAllocateState(int* cipherCtx, int mode, int extra)
  • Description: Validates cipher context, calls SakeCrypto_ExecuteCipherVtableOp, allocates 0x24-byte state via calloc, zeroes first 16 bytes. Returns error code or 0.

10.5.10. SakeCrypto_ProcessCipherBlock

  • Address: 0x00016f64
  • Signature: void SakeCrypto_ProcessCipherBlock(int* cipherCtx, int input, int output, int blockSize, int mode, int flags, int* written)
  • Description: Validates context, calls SakeCrypto_DispatchCipherOperation, then SakeCrypto_GetCipherOutputSize. Adds output size to written count.

10.5.11. SakeCrypto_GetCipherOutputSize

  • Address: 0x00016f24
  • Signature: int SakeCrypto_GetCipherOutputSize(int* cipherCtx, int mode, int* outputSize)
  • Description: Returns 0 for mode 1 (ECB), checks mode 5 (CTR/CBC), returns error for mode 3. Sets output size based on algorithm type.

10.5.12. SakeCrypto_ValidateCipherContext

  • Address: 0x00016e0c
  • Signature: int SakeCrypto_ValidateCipherContext(int* cipherCtx)
  • Description: Checks algorithm type at +4, validates mode flags. Returns 0 if valid or error code DAT_00016f60.

12. JNI Interface Functions (SecureMessage-Related)

JNI Function Address Internal Call Description
Sake_SecureMessage_EncryptAndSign 0x00014072 SakeCrypto_EncryptSignMessage Encrypts + signs message
Sake_SecureMessage_DecryptAndAuthenticate 0x0001408e sakeCrypto_DecryptAuthenticate_HighLevel Decrypts + verifies message
Sake_SecureMessage_GetSequenceNumber 0x00014064 SakeSecureMessage_GetSequenceByte Extracts sequence byte
SAKE_SECURE_MESSAGE_S_pBytes_get 0x0001403a JNI getter for message bytes
SAKE_SECURE_MESSAGE_S_pBytes_set 0x00014028 JNI setter for message bytes
SAKE_SECURE_MESSAGE_S_byteCount_get 0x00014048 JNI getter for byte count
SAKE_SECURE_MESSAGE_S_byteCount_set 0x00014040 JNI setter for byte count
new_SAKE_SECURE_MESSAGE_S 0x0001404e malloc Allocates new secure message
delete_SAKE_SECURE_MESSAGE_S 0x0001405e free Frees secure message
MIN_SAKE_SECURE_MESSAGE_BYTE_COUNT_get 0x0001401c Returns minimum message size
MAX_SAKE_SECURE_MESSAGE_BYTE_COUNT_get 0x00014022 Returns maximum message size

13. SAKE_SECURE_MESSAGE_S Structure

#pragma pack(push, 1)
typedef struct {
    uint8_t* pBytes;                      // +0x00: Pointer to message byte array
    uint32_t byteCount;                   // +0x04: Number of bytes in message
} SAKE_SECURE_MESSAGE_S;
#pragma pack(pop)

12.1. Secure Link State (Embedded in Client/Server)

The secure link cipher state is a 48-byte structure embedded at:

  • Client: SAKE_CLIENT_S + 0x40
  • Server: SAKE_SERVER_S + 0x70
typedef struct {
    uint32_t mode;                        // +0x00: Cipher mode flag (0 = decrypt, 1 = encrypt)
    uint32_t sequenceCounterLow;          // +0x04: Low 32 bits of 64-bit sequence
    uint32_t sequenceCounterHigh;         // +0x08: High 32 bits of 64-bit sequence
    uint32_t blockSize;                   // +0x0C: Block size (0x100 = 256-bit, 0x80 = 128-bit)
    uint32_t reserved;                    // +0x10: Reserved
    uint8_t  iv[16];                      // +0x14: Initialization vector / nonce
    uint8_t  key[16];                     // +0x24: Session key
    uint8_t  macKey[16];                  // +0x34: MAC key
} SAKE_SECURE_LINK_S;                     // Total: 0x30 (48 bytes)

14. Complete Function Call Graph (SecureMessage Path)

Sake_SecureMessage_EncryptAndSign (JNI)
  └─ SakeCrypto_EncryptSignMessage
       ├─ SakeCrypto_EncryptMessage_HighLevel
       │    ├─ SakeCrypto_ClearAESContext
       │    ├─ SakeCrypto_AES_ProcessBlock (mode 0x80)
       │    └─ SakeCrypto_AES_CTR_Transform
       │         └─ SakeCrypto_AES_EncryptBlock
       └─ SakeCrypto_ComputeMACAndAppend
            └─ SakeCrypto_ComputeCMAC (AES-CMAC)

Sake_SecureMessage_DecryptAndAuthenticate (JNI)
  └─ sakeCrypto_DecryptAuthenticate_HighLevel
       ├─ SakeCrypto_ComputeMACAndAppend
       │    └─ SakeCrypto_ComputeCMAC (AES-CMAC)
       └─ SakeCrypto_AES_CTR_DecryptBlock
            ├─ SakeCrypto_ClearAESContext
            ├─ SakeCrypto_AES_KeyExpansion_Inferred
            └─ SakeCrypto_AES_CTR_Transform
                 └─ SakeCrypto_AES_EncryptBlock

SakeCrypto_PerformEncryptDecrypt (handshake)
  ├─ SakeSecureMessage_GetSequenceByte
  ├─ sakeCrypto_LowLevelPrimitive (build CTR block)
  └─ sakeCrypto_DecryptAuthenticate_HighLevel
       ├─ SakeCrypto_ComputeMACAndAppend
       └─ SakeCrypto_AES_CTR_DecryptBlock

15. Key Database Management

The key database stores pre-shared keys for device authentication. It uses a CRC32-protected binary format.

15.1. Database Format

+------------------------------------------+
| Header (6 bytes)                         |
|  +0x00: CRC32 (4 bytes, big-endian)      |
|  +0x04: Device type (1 byte)             |
|  +0x05: Entry count (1 byte)             |
+------------------------------------------+
| Entry 0 (81 bytes = 0x51)                |
|  +0x06: Entry type (1 byte)              |
|  +0x07: Key 1 (16 bytes)                 |
|  +0x17: Key 2 (16 bytes)                 |
|  +0x27: Key 3 (16 bytes)                 |
|  +0x37: Key 4 (16 bytes)                 |
|  +0x47: Key 5 (16 bytes)                 |
+------------------------------------------+
| Entry 1 (81 bytes)                       |
|  ...                                     |
+------------------------------------------+

15.2. Key Database Functions

15.2.1. SakeKeyDB_CreateDatabase

  • Address: 0x0001538a
  • Signature: uint SakeKeyDB_CreateDatabase(int* dbPtr, int deviceType, int size, uint type)
  • Description: Creates a new key database. Validates device type, checks type > 5, sets size and type in header, initializes entry at +4, sets entry count to 0, calls SakeKeyDB_ResetAndRecomputeCRC.

15.2.2. SakeKeyDB_OpenDatabase

  • Address: 0x00015290
  • Signature: void SakeKeyDB_OpenDatabase(int* dbPtr, int dataBuffer, uint size)
  • Description: Opens an existing key database. Validates device type at +4, checks size > 5, validates CRC32 via SakeKeyDB_ValidateCRC, iterates entries to validate each device type. Sets dbPtr and capacity on success.

15.2.3. SakeKeyDB_AddEntry

  • Address: 0x000153ce
  • Signature: uint SakeKeyDB_AddEntry(int* dbPtr, int data, int size)
  • Description: Adds a new entry to the key database. Validates device type, checks capacity (entryCount+1)*0x51+6 <= capacity. Copies 5 keys (16 bytes each) at entry+0x10, +0x20, +0x30, +0x40, +0x50. Increments entry count, recomputes CRC.

15.2.4. SakeKeyDB_CalculateEntryOffset

  • Address: 0x0001548a
  • Signature: int SakeKeyDB_CalculateEntryOffset(int keyValue)
  • Description: Returns keyValue * 0x51 + 6. Each entry is 81 bytes, header is 6 bytes.

15.2.5. SakeKeyDB_ResetAndRecomputeCRC

  • Address: 0x00015226
  • Signature: void SakeKeyDB_ResetAndRecomputeCRC(int* dbPtr)
  • Description: Calls SakeKeyDB_ComputeCRC32 over (entryCount * 0x51 + 2) bytes starting at +4. Writes 4-byte CRC to database header bytes 0-3 (big-endian).

15.2.6. SakeKeyDB_ComputeCRC32

  • Address: 0x000174ec
  • Signature: uint SakeKeyDB_ComputeCRC32(uint initial, byte* data, int length)
  • Description: Standard CRC32 computation using table at DAT_00017514. Initial value ~0, XORs each byte with low byte of accumulator, looks up in 256-entry table, shifts right 8. Returns ~result.

15.2.7. SakeKeyDB_ValidateCRC

  • Address: 0x00015258
  • Signature: bool SakeKeyDB_ValidateCRC(int* dbPtr)
  • Description: Validates CRC32 of key database. Computes CRC via SakeKeyDB_ComputeCRC32, compares with stored 4-byte CRC at header bytes 0-3 (big-endian). Returns true if match.

16. UserMessage Subsystem

The UserMessage subsystem handles unencrypted application-level messages — what the app uses before encryption is established.

16.1. SAKE_USER_MESSAGE_S Structure (36 bytes, Ghidra-verified)

typedef struct {
    uint8_t  pBytes[29];                  // +0x00: Message byte buffer (max 29 bytes)
    uint32_t byteCount;                   // +0x20: Number of bytes in message
} SAKE_USER_MESSAGE_S;                    // Total: 0x24 (36 bytes)

16.2. Size Constraints

  • MAX: MAX_SAKE_USER_MESSAGE_BYTE_COUNT = 0x1D (29 bytes)
  • Struct size: 0x24 (36 bytes) — allocated via calloc(1, 0x24)

16.3. UserMessage JNI Functions

JNI Function Address Description
SakeJNI_GetMaxUserMessageByteCount 0x000140a0 Returns 0x1D (29 bytes)
SakeJNI_SetUserMessageBytes 0x000140a6 Copies 29 bytes to pBytes buffer
SakeJNI_GetUserMessageBytes 0x000140b8 Returns pointer to pBytes buffer
SakeJNI_SetUserMessageByteCount 0x000140be Sets byteCount at +0x20
SakeJNI_GetUserMessageByteCount 0x000140c6 Returns byteCount at +0x20
SakeJNI_AllocUserMessage 0x000140cc Allocates 36 bytes via calloc
SakeJNI_FreeUserMessage 0x000140dc Frees UserMessage struct

17. Error Handling Infrastructure

17.1. Error Code Range

  • INVALID: 0 (error state)
  • NO_ERROR: 1 (success state)
  • Valid range: 1-19 (0x13 = LAST)
  • NULL pointer: Returns error code 2

17.2. Error Strings (Embedded in .rodata)

Address String Context
0x00019a24 "Attempt to dereference null SAKE_DEVICE_TYPE_E const" Null device type
0x00019a59 "Attempt to dereference null SAKE_PERMIT_VERSION_E" Null permit version
0x00019a8b "Attempt to dereference null SAKE_DEVICE_TYPE_E" Null device type
0x00019aba "Attempt to dereference null SAKE_CLIENT_STATE_E" Null client state
0x00019aea "Attempt to dereference null SAKE_CHALLENGE_S" Null challenge
0x00019b17 "Attempt to dereference null SAKE_INITIALIZATION_VECTOR_S" Null IV
0x00019b50 "Attempt to dereference null SAKE_SECURE_LINK_S" Null secure link
0x00019b7f "Attempt to dereference null SAKE_SERVER_STATE_E" Null server state
0x00019baf "java/lang/OutOfMemoryError" JNI OOM exception
0x00019c7d "java/lang/UnknownError" JNI unknown error
0x00019c94 "No error has occurred" No error message
0x00019e98 "The session key could not be used to receive the permit" Permit decrypt fail
0x00019ed0 "The permit could not be secured for sending" Permit encrypt fail
0x00019efc "The permit could not be padded for sending" Padding fail
0x00019f27 "The permit received is not padded" Padding validation
0x00019f49 "The permit received is invalid" General validation
0x00019f68 "The permit received is issued to a different device type" Device type mismatch
0x00019fa1 "The handshake error is invalid" Invalid error code

17.3. Error Functions

17.3.1. SakeError_IsValidCode

  • Address: 0x0001490c
  • Signature: bool SakeError_IsValidCode(int errorCode)
  • Description: Returns (errorCode - 1) < 0x13. Error codes 1-19 are valid. 0 = invalid, 1 = no error.

17.3.2. SakeError_GetStringForCode

  • Address: 0x00014918
  • Signature: char* SakeError_GetStringForCode(int errorCode)
  • Description: Looks up error string from table at DAT_00014930. Returns pointer to string for error code (1-19).

17.3.3. SakeError_ParseStringToCode

  • Address: 0x00014938
  • Signature: uint SakeError_ParseStringToCode(char* errorString)
  • Description: Iterates error table (1-19), compares string via strcmp. Returns matching error code or 0 if not found.

17.3.4. SakeJNI_HandleKeyDatabaseError

  • Address: 0x00014180
  • Signature: void SakeJNI_HandleKeyDatabaseError(int* errorContext, uint errorCode)
  • Description: Error handler. Iterates error table at DAT_000141d0, calls vtable functions at +0x44, +0x18, +0x38 to report errors to Java layer.

17.3.5. SakeJNI_ClientGetLastError

  • Address: 0x000145ee
  • Signature: uint SakeJNI_ClientGetLastError(JNIEnv* env, jobject obj, SAKE_CLIENT_S* client)
  • Description: Returns lastError at client+0x74. Returns 2 if client pointer is NULL.

17.3.6. SakeJNI_ServerGetLastError

  • Address: 0x00014906
  • Signature: uint SakeJNI_ServerGetLastError(JNIEnv* env, jobject obj, SAKE_SERVER_S* server)
  • Description: Returns lastError at server+0xA4. Returns 2 if server pointer is NULL.

17.4. Handshake Error JNI Functions

JNI Function Address Description
SakeJNI_GetHandshakeErrorInvalid 0x00013f90 Returns 0 (INVALID)
SakeJNI_GetHandshakeNoError 0x00013f94 Returns 1 (NO_ERROR)
SakeJNI_GetHandshakeErrorLast 0x00013f98 Returns 0x13 (LAST)
SakeJNI_HandshakeErrorIsValid 0x00013f9c Validates error code 1-19
SakeJNI_HandshakeErrorToString 0x00013fa8 Converts error code to Java string
SakeJNI_HandshakeErrorFromString 0x00013fca Parses Java string to error code

18. Complete JNI API Surface

This section maps every JNI export to its internal implementation, based on decompilation and the extracted Android Java code.

18.1. Client Lifecycle

JNI Function Address Internal Call Java Method Description
Sake_Client_Init 0x000145a8 Sake_Client_Init Sets currentState=1, copies key DB pointer, clears isSecureLinkEstablished, sets handshakeStatus=1
Sake_Client_Destroy 0x000145b0 __aeabi_memclr8 Sake_Client_Destroy Zeroes 120 bytes (0x78) of client struct

18.2. Client Handshake

JNI Function Address Internal Call Java Method Description
Sake_Client_Handshake 0x000145b6 SakeClient_HandshakeHandler Sake_Client_Handshake Master handshake state machine. Routes to states 1-8
Sake_Client_GetLastError 0x000145ee Sake_Client_GetLastError Returns lastError at client+0x74

18.3. Client Secure Messaging

JNI Function Address Internal Call Java Method Description
Sake_Client_SecureForSending 0x000145c0 SakeClient_SecureForSending Sake_Client_SecureForSending Encrypts outgoing message
Sake_Client_UnsecureAfterReceiving 0x000145d0 SakeClient_UnsecureAfterReceiving Sake_Client_UnsecureAfterReceiving Decrypts incoming message
Sake_Client_GetServerPermit 0x000145e0 SakeClient_GetServerPermit Sake_Client_GetServerPermit Returns server permit pointer

18.4. Server Lifecycle

JNI Function Address Internal Call Java Method Description
Sake_Server_Init 0x000148c0 Sake_Server_Init Sets currentState=1, copies key DB pointer, clears isSecureLinkEstablished, sets lastError=1
Sake_Server_Destroy 0x000148c8 __aeabi_memclr8 Sake_Server_Destroy Zeroes 168 bytes (0xA8) of server struct

18.5. Server Handshake

JNI Function Address Internal Call Java Method Description
Sake_Server_Handshake 0x000148ce SakeServer_HandshakeHandler Sake_Server_Handshake Master handshake state machine. Routes to states 1-6
Sake_Server_GetLastError 0x00014906 Sake_Server_GetLastError Returns lastError at server+0xA4

18.6. Server Secure Messaging

JNI Function Address Internal Call Java Method Description
Sake_Server_SecureForSending 0x000148d8 SakeServer_SecureForSending Sake_Server_SecureForSending Encrypts outgoing message
Sake_Server_UnsecureAfterReceiving 0x000148e8 SakeServer_UnsecureAfterReceiving Sake_Server_UnsecureAfterReceiving Decrypts incoming message
Sake_Server_GetClientPermit 0x000148f8 SakeServer_GetClientPermit Sake_Server_GetClientPermit Returns client permit pointer

18.7. Key Database

JNI Function Address Internal Call Java Method Description
Sake_KeyDatabase_Open 0x00014132 SakeKeyDB_OpenDatabase Sake_KeyDatabase_Open Opens existing key database
Sake_KeyDatabase_GetLocalDeviceType 0x00014142 SakeKeyDB_GetEntryByte Sake_KeyDatabase_GetLocalDeviceType Returns device type from header
Sake_KeyDatabase_HasRemoteDeviceType 0x0001415c SakeKeyDB_LookupAndValidateType Sake_KeyDatabase_HasRemoteDeviceType Checks if device type exists
Sake_KeyDatabase_GetRemoteDeviceKeyByType 0x000141d4 SakeKeyDB_LookupEntryByType Sake_KeyDatabase_GetRemoteDeviceKeyByType Gets key entry by device type
Sake_KeyDatabase_Create 0x000141f8 SakeKeyDB_CreateDatabase Sake_KeyDatabase_Create Creates new key database
Sake_KeyDatabase_AddRemoteDeviceKey 0x00014224 SakeKeyDB_AddEntry Sake_KeyDatabase_AddRemoteDeviceKey Adds key entry to database
Sake_KeyDatabase_Clear 0x0001424c SakeKeyDB_ResetAndRecomputeCRC Sake_KeyDatabase_Clear Clears all entries, recomputes CRC
Sake_KeyDatabase_GetSizeInBytes 0x00014252 SakeKeyDB_CalculateEntryOffset Sake_KeyDatabase_GetSizeInBytes Returns database size in bytes

18.8. SecureMessage

JNI Function Address Internal Call Java Method Description
Sake_SecureMessage_EncryptAndSign 0x00014072 SakeCrypto_EncryptSignMessage Sake_SecureMessage_EncryptAndSign Encrypts + signs message
Sake_SecureMessage_DecryptAndAuthenticate 0x0001408e sakeCrypto_DecryptAuthenticate_HighLevel Sake_SecureMessage_DecryptAndAuthenticate Decrypts + verifies message
Sake_SecureMessage_GetSequenceNumber 0x00014064 SakeSecureMessage_GetSequenceByte Sake_SecureMessage_GetSequenceNumber Extracts sequence byte

18.9. Permit

JNI Function Address Internal Call Java Method Description
Sake_Permit_Init 0x000142f4 SakePermit_InitStruct Sake_Permit_Init Initializes 20-byte permit
Sake_Permit_EncryptAndSign 0x00014360 SakePermit_EncryptAndSign Sake_Permit_EncryptAndSign Encrypts permit for transmission
Sake_Permit_DecryptAndAuthenticate 0x00014372 SakeClient_DecryptAndValidateServerPermit Sake_Permit_DecryptAndAuthenticate Decrypts and validates permit

18.10. Utility

JNI Function Address Internal Call Java Method Description
AsVoidPtr 0x00013f8a AsVoidPtr Returns pointer as-is (type cast)
cdata 0x00013e96 JNI NewByteArray + GetByteArrayElements cdata Copies raw bytes to Java byte array
memmove 0x00013efe __aeabi_memmove memmove Moves bytes from Java array to native pointer

18.11. Java Class: NativeSakeLib (Extracted from classes.dex)

package com.medtronic.minimed.sake;

public class NativeSakeLib {
    // Utility
    public static SWIGTYPE_p_void asvoidptr(SWIGTYPE_p_unsigned_char ptr);
    public static byte[] cdata(SWIGTYPE_p_void ptr, int size);
    public static void memmove(SWIGTYPE_p_void dest, byte[] src);

    // Key Database
    public static boolean Sake_KeyDatabase_Open(SAKE_KEY_DATABASE_S db, SWIGTYPE_p_unsigned_char data, long size);

    // Server
    public static boolean Sake_Server_Init(SAKE_SERVER_S server, SAKE_KEY_DATABASE_S db);
    public static void Sake_Server_Destroy(SAKE_SERVER_S server);
    public static SAKE_HANDSHAKE_STATUS_E Sake_Server_Handshake(SAKE_SERVER_S server, SAKE_SECURE_MESSAGE_S input, SAKE_SECURE_MESSAGE_S output);
    public static boolean Sake_Server_SecureForSending(SAKE_SERVER_S server, SAKE_USER_MESSAGE_S userMsg, SAKE_SECURE_MESSAGE_S secureMsg);
    public static boolean Sake_Server_UnsecureAfterReceiving(SAKE_SERVER_S server, SAKE_SECURE_MESSAGE_S secureMsg, SAKE_USER_MESSAGE_S userMsg);
    public static boolean Sake_Server_SetPasskey(SAKE_SERVER_S server, SAKE_SERVER_MEMORY_S memory, SAKE_PASSKEY_S passkey);
}

18.12. Missing Functions (Not in Binary)

The following functions are referenced in the Java code but NOT present in libandroid-sake-lib_v210.so:

  • Sake_Passkey_FromInteger — Passkey creation (may be in a different library)
  • Sake_Server_SetPasskey — Server passkey setting (may be in a different library)
  • SAKE_PASSKEY_S — Passkey struct (not defined in this binary)
  • SAKE_SERVER_MEMORY_S — Server memory struct (not defined in this binary)

These may be implemented in a companion library or in a different version of the SAKE library.


19. JNI Struct Accessors (Complete)

Every JNI getter/setter function has been renamed with descriptive names.

19.1. SAKE_CLIENT_S Accessors

JNI Function Address Field Description
SakeJNI_Client_SetCurrentState 0x00014384 +0x00 Sets currentState
SakeJNI_Client_GetCurrentState 0x0001439c +0x00 Gets currentState
SakeJNI_Client_SetClientChallenge 0x000143b0 +0x0C Sets localChallenge[20]
SakeJNI_Client_GetClientChallenge 0x000143cc +0x0C Gets localChallenge[20]
SakeJNI_Client_SetServerChallenge 0x000143e4 +0x20 Sets remoteChallenge[20]
SakeJNI_Client_GetServerChallenge 0x00014400 +0x20 Gets remoteChallenge[20]
SakeJNI_Client_SetClientIV 0x00014418 +0x0C Sets client IV
SakeJNI_Client_GetClientIV 0x00014430 +0x0C Gets client IV
SakeJNI_Client_SetServerIV 0x00014444 +0x20 Sets server IV
SakeJNI_Client_GetServerIV 0x0001445c +0x20 Gets server IV
SakeJNI_Client_SetServerPermit 0x0001446e +0x1C Sets serverPermit
SakeJNI_Client_GetServerPermit 0x00014488 +0x1C Gets serverPermit
SakeJNI_Client_SetServerDeviceType 0x00014490 +0x34 Sets deviceType
SakeJNI_Client_GetServerDeviceType 0x000144a8 +0x34 Gets deviceType
SakeJNI_Client_SetKeyDatabase 0x000144ba +0x38 Sets keyDatabase
SakeJNI_Client_GetKeyDatabase 0x000144c8 +0x38 Gets keyDatabase
SakeJNI_Client_SetSecureLink 0x000144d0 +0x40 Sets secureLink[48]
SakeJNI_Client_GetSecureLink 0x00014520 +0x40 Gets secureLink[48]
SakeJNI_Client_SetIsSecureLinkEstablished 0x00014570 +0x70 Sets isSecureLinkEstablished
SakeJNI_Client_GetIsSecureLinkEstablished 0x00014580 +0x70 Gets isSecureLinkEstablished
SakeJNI_Client_SetLastError 0x00014586 +0x74 Sets lastError
SakeJNI_Client_GetLastError 0x0001458e +0x74 Gets lastError
SakeJNI_AllocClient 0x00014592 Allocates 120 bytes
SakeJNI_FreeClient 0x000145a2 Frees client struct

19.2. SAKE_SERVER_S Accessors

JNI Function Address Field Description
SakeJNI_Server_SetCurrentState 0x000145f4 +0x00 Sets currentState
SakeJNI_Server_GetCurrentState 0x0001460c +0x00 Gets currentState
SakeJNI_Server_SetClientChallenge 0x00014620 +0x04 Sets localChallenge[20]
SakeJNI_Server_GetClientChallenge 0x0001463c +0x04 Gets localChallenge[20]
SakeJNI_Server_SetServerChallenge 0x00014654 +0x18 Sets remoteChallenge[20]
SakeJNI_Server_GetServerChallenge 0x00014670 +0x18 Gets remoteChallenge[20]
SakeJNI_Server_SetClientIV 0x00014688 +0x04 Sets client IV
SakeJNI_Server_GetClientIV 0x000146a0 +0x04 Gets client IV
SakeJNI_Server_SetServerIV 0x000146b4 +0x18 Sets server IV
SakeJNI_Server_GetServerIV 0x000146cc +0x18 Gets server IV
SakeJNI_Server_SetClientPermit 0x000146de +0x1C Sets clientPermit
SakeJNI_Server_GetClientPermit 0x000146f8 +0x1C Gets clientPermit
SakeJNI_Server_SetClientDeviceType 0x00014700 +0x30 Sets deviceType
SakeJNI_Server_GetClientDeviceType 0x00014718 +0x30 Gets deviceType
SakeJNI_Server_SetTemporarySecureLink 0x0001472c +0x38 Sets cipherState
SakeJNI_Server_GetTemporarySecureLink 0x0001477c +0x38 Gets cipherState
SakeJNI_Server_SetKeyDatabase 0x000147cc +0x68 Sets keyDatabase
SakeJNI_Server_GetKeyDatabase 0x000147da +0x68 Gets keyDatabase
SakeJNI_Server_SetSecureLink 0x000147e4 +0x70 Sets secureLink[48]
SakeJNI_Server_GetSecureLink 0x00014834 +0x70 Gets secureLink[48]
SakeJNI_Server_SetIsSecureLinkEstablished 0x00014884 +0xA0 Sets isSecureLinkEstablished
SakeJNI_Server_GetIsSecureLinkEstablished 0x00014894 +0xA0 Gets isSecureLinkEstablished
SakeJNI_Server_SetLastError 0x0001489a +0xA4 Sets lastError
SakeJNI_Server_GetLastError 0x000148a4 +0xA4 Gets lastError
SakeJNI_AllocServer 0x000148aa Allocates 168 bytes
SakeJNI_FreeServer 0x000148ba Frees server struct

19.3. SAKE_PERMIT_S Accessors

JNI Function Address Field Description
SakeJNI_Permit_SetVersion 0x00014268 +0x00 Sets version
SakeJNI_Permit_GetVersion 0x00014280 +0x00 Gets version
SakeJNI_Permit_SetDeviceType 0x00014294 +0x04 Sets deviceType
SakeJNI_Permit_GetDeviceType 0x000142ac +0x04 Gets deviceType
SakeJNI_Permit_SetProprietaryBytes 0x000142be +0x08 Sets proprietaryBytes[10]
SakeJNI_Permit_GetProprietaryBytes 0x000142d4 +0x08 Gets proprietaryBytes[10]
SakeJNI_Permit_GetProprietaryByteCount 0x00014260 Returns constant 10

19.4. SAKE_SECURE_MESSAGE_S Accessors

JNI Function Address Field Description
SakeJNI_SecureMessage_SetBytes 0x00014028 +0x00 Sets pBytes pointer
SakeJNI_SecureMessage_GetBytes 0x0001403a +0x00 Gets pBytes pointer
SakeJNI_SecureMessage_SetByteCount 0x00014040 +0x04 Sets byteCount
SakeJNI_SecureMessage_GetByteCount 0x00014048 +0x04 Gets byteCount
SakeJNI_SecureMessage_GetMinByteCount 0x0001401c Returns MIN constant
SakeJNI_SecureMessage_GetMaxByteCount 0x00014022 Returns MAX constant

19.5. SAKE_KEY_DATABASE_S Accessors

JNI Function Address Field Description
SakeJNI_KeyDatabase_SetBytes 0x00014100 +0x00 Sets pBytes pointer
SakeJNI_KeyDatabase_GetBytes 0x00014108 +0x00 Gets pBytes pointer
SakeJNI_KeyDatabase_SetByteCapacity 0x0001410e +0x04 Sets byteCapacity
SakeJNI_KeyDatabase_GetByteCapacity 0x00014116 +0x04 Gets byteCapacity
SakeJNI_KeyDatabase_GetCRCByteCount 0x000140e2 Returns CRC byte count
SakeJNI_KeyDatabase_GetDeviceTypeByteCount 0x000140e8 Returns device type byte count
SakeJNI_KeyDatabase_GetRemoteDeviceCountByteCount 0x000140ee Returns remote device count byte count
SakeJNI_KeyDatabase_GetHeaderByteCount 0x000140f4 Returns header byte count
SakeJNI_KeyDatabase_GetRemoteDeviceKeyCount 0x000140fa Returns remote device key count

20. Crypto Helper Functions

Function Address Description
SakeCrypto_LookupAlgorithmByID 0x00016c90 Looks up crypto algorithm descriptor by ID from table
SakeCrypto_CleanupCipherContext 0x00016d34 Frees allocated state, calls vtable cleanup, zeroes context
SakeCrypto_InitCipherContext 0x00016d70 Clears context, calls vtable init, sets descriptor pointer
SakeCrypto_ProcessAndVerifyPayload 0x00017358 Processes decrypted payload with cipher, verifies MAC
SakeCrypto_DeriveKeyMaterial 0x000173ec Derives 16-byte key material using algorithm ID 2
SakeCrypto_InitCipherAndAllocateState 0x00016ffc Validates context, allocates 0x24-byte state
SakeCrypto_ProcessCipherBlock 0x00016f64 Processes cipher block, adds output size to written count
SakeCrypto_GetCipherOutputSize 0x00016f24 Returns output size based on algorithm type
SakeCrypto_ValidateCipherContext 0x00016e0c Validates algorithm type and mode flags

21. ARM Unwinding / C++ Runtime Infrastructure

The remaining functions are standard ARM exception handling / C++ runtime infrastructure inserted by the NDK compiler. They are NOT SAKE-specific.

21.1. ARM Unwinding Functions

Function Address Description
ARM_Unwind_ParseTableEntry 0x0001756c Parses ARM exception table entry
ARM_Unwind_ProcessInstructions 0x000175bc Processes ARM exception handling instructions
ARM_Unwind_SetRegister 0x00017828 Sets register value in exception context
ARM_Unwind_GetRegister 0x000178d4 Gets register value from exception context
ARM_Unwind_RestoreRegisters 0x00017980 Restores registers from stack frame
ARM_Unwind_ExecuteFrame 0x00017b18 Executes unwind frame, processes exception handler chain
ARM_Unwind_StepFrame 0x00017bb8 Steps through unwind frame, processes handler chain
ARM_Unwind_ForcedUnwind 0x00017cd4 Performs forced unwinding
ARM_Unwind_GetRegionStart 0x00017d3c Gets region start address
ARM_Unwind_GetLanguageSpecificData 0x00017d70 Gets language-specific data area pointer
ARM_Unwind_CheckPhase 0x00017db0 Checks unwind phase
ARM_Unwind_SaveCoreRegisters 0x00017dc4 Saves R0-R12, SP, LR to context
ARM_Unwind_SaveVFPRegisters 0x00017ddc Saves D0-D15 VFP registers
ARM_Unwind_SaveVFPRegisters_D 0x00017de4 Saves D0-D15 VFP registers (variant)
ARM_Unwind_SaveVFPRegisters_D16 0x00017dec Saves D0-D15 VFP registers (D16 variant)
ARM_Unwind_CopyContext 0x00017df4 Copies unwind context
ARM_Unwind_GetWord 0x00017e3c Gets word from unwind context
ARM_Unwind_SetWord 0x00017e70 Sets word in unwind context
ARM_Unwind_SetRegPair 0x00017eb8 Sets register pair in unwind context
ARM_Unwind_GetRegPair 0x00017ef0 Gets register pair from unwind context
ARM_Unwind_IsPhase1 0x00017f2c Checks if in phase 1 unwinding
ARM_Unwind_NextFrame 0x00017f32 Advances to next frame
ARM_Unwind_PopRegisters 0x00017f54 Pops registers from stack
ARM_Unwind_GetCFA 0x00017f68 Gets Canonical Frame Address
ARM_Unwind_SetCFA 0x00017f88 Sets Canonical Frame Address
ARM_Unwind_FlushCache 0x00017fa6 Flushes instruction cache
ARM_Unwind_DecodeULEB128 0x00017fe6 Decodes unsigned LEB128 value
ARM_Unwind_DecodeSLEB128 0x00018054 Decodes signed LEB128 value
ARM_Unwind_ReadHeader 0x00018088 Reads unwind table header
ARM_Unwind_FindFrameInfo 0x00018128 Finds frame info for address
ARM_Unwind_SearchPhase1 0x0001819e Searches for handler in phase 1
ARM_Unwind_RaiseException 0x00018350 Raises an exception
ARM_Unwind_Backtrace 0x000183b0 Performs backtrace
ARM_Unwind_ForcedUnwind2 0x000183e4 Performs forced unwinding (variant)
ARM_Unwind_DeleteException 0x00018444 Deletes exception object
ARM_Unwind_Resume 0x00018488 Resumes execution after exception
ARM_Unwind_Resume2 0x00018658 Resumes execution (variant)

21.2. C++ Runtime Functions

Function Address Description
ARM_Cxx_CallTerminate 0x00018b04 Calls terminate handler
ARM_Cxx_TerminateHandler 0x00018b0c Default terminate handler
Stub_NOP 0x00013e60 Empty NOP stub

22. Binary Statistics

Metric Value
Total functions 337
FUN_* remaining 0
Functions renamed ~120+
EOL comments added ~80+
Ghidra structs created 6
Struct types applied 25+ functions
Sections documented 22
SAKE-specific functions ~150
ARM/NDK infrastructure ~187

22.1. Coverage Summary

Subsystem Status Functions
Client Handshake 100% All states, all helpers
Server Handshake 100% All states, all helpers
AES-128 (ECB/CTR/CMAC) 100% All primitives
Key Database 100% Create, open, add, CRC, lookup
Permit Subsystem 100% Init, encrypt, decrypt, validate
SecureMessage 100% Encrypt, decrypt, MAC, sequence
UserMessage 100% Struct, JNI accessors
Error Handling 100% Error codes, strings, handlers
JNI API Surface 100% All 80+ JNI exports mapped
Struct Accessors 100% All getter/setter functions
ARM Unwinding 100% All NDK infrastructure renamed
Ghidra Datatypes 100% All structs created and applied

23. Device Type Table & Enums

23.1. Device Type Table

Located at 0x0001dcf0 (SAKE_DEVICE_TYPE_TABLE), this is an array of 7 pointers to device type name strings:

Index Enum Value String Address String Value
0 INSULIN_PUMP (1) 0x00019fc0 "InsulinPump"
1 GLUCOSE_SENSOR (2) 0x00019fcc "GlucoseSensor"
2 BLOOD_GLUCOSE_METER (3) 0x00019fda "BloodGlucoseMeter"
3 MOBILE_APPLICATION (4) 0x00019fec "MobileApplication"
4 CARELINK_UPLOAD_APPLICATION (5) 0x00019ffe "CareLinkUploadApplication"
5 FIRMWARE_UPDATE_APPLICATION (6) 0x0001a018 "FirmwareUpdateApplication"
6 DIAGNOSTIC_APPLICATION (7) 0x0001a032 "DiagnosticApplication"

Error string: "InvalidDeviceType" at 0x0001a048

23.2. All Enums (Ghidra-Verified)

Enum Size Values
SAKE_CLIENT_STATE_E 4 bytes INIT_OR_RESET=1, EXCHANGE=2, DERIVATION=3, AUTHENTICATION=4, INTERMEDIATE_5=5, CHALLENGE_INIT=6, SESSION_PROOF=7, SUCCESS=8
SAKE_SERVER_STATE_E 4 bytes INIT_OR_RESET=1, EXCHANGE=2, DERIVE_AND_VERIFY=3, DECRYPT_PERMIT=4, INTERMEDIATE_5=5, FINALIZE=6, INTERMEDIATE_7=7, SUCCESS=8
SAKE_HANDSHAKE_ERROR_E 4 bytes INVALID=0, NO_ERROR=1, HANDSHAKE_MISUSED=2, SYNC_MESSAGE_INVALID=3, CHALLENGE_GENERATION_FAILED=4, CHALLENGE_INVALID=5, SYNC_RESPONSE_GENERATION_FAILED=6, SYNC_RESPONSE_INVALID=7, REMOTE_DEVICE_TYPE_UNSUPPORTED=8, CHALLENGE_RESPONSE_INVALID=9, CHALLENGE_RESPONSE_GENERATION_FAILED=10, CHALLENGE_RESPONSE_RANDOMIZATION_FAILED=11, SESSION_KEY_DERIVATION_FAILED=12, SESSION_KEY_RANDOMIZATION_FAILED=13, SESSION_KEY_PERMIT_RECEIVE_FAILED=14, PERMIT_SEND_SECURITY_FAILED=15, PERMIT_SEND_PADDING_FAILED=16, PERMIT_RECEIVED_NOT_PADDED=17, PERMIT_RECEIVED_INVALID=18, PERMIT_RECEIVED_DEVICE_TYPE_MISMATCH=19
SAKE_DEVICE_TYPE_E 4 bytes INSULIN_PUMP=1, GLUCOSE_SENSOR=2, BLOOD_GLUCOSE_METER=3, MOBILE_APPLICATION=4, CARELINK_UPLOAD_APPLICATION=5, FIRMWARE_UPDATE_APPLICATION=6, DIAGNOSTIC_APPLICATION=7
SAKE_PERMIT_VERSION_E 4 bytes VERSION_1=1, VERSION_2=2, VERSION_3=3
SAKE_CIPHER_MODE_E 4 bytes DECRYPT=0, ENCRYPT=1
SAKE_AES_BLOCK_MODE_E 4 bytes ECB_128=128, CBC_128=192, CTR_128=256

23.3. Enum Application to Struct Fields

Struct Field Enum
SAKE_CLIENT_S dwCurrentState SAKE_CLIENT_STATE_E
SAKE_CLIENT_S dwHandshakeStatus SAKE_HANDSHAKE_ERROR_E
SAKE_CLIENT_S dwDeviceType SAKE_DEVICE_TYPE_E
SAKE_SERVER_S dwCurrentState SAKE_SERVER_STATE_E
SAKE_SERVER_S dwLastError SAKE_HANDSHAKE_ERROR_E
SAKE_SERVER_S dwDeviceType SAKE_DEVICE_TYPE_E
SAKE_PERMIT_S dwVersion SAKE_PERMIT_VERSION_E
SAKE_PERMIT_S dwDeviceType SAKE_DEVICE_TYPE_E
SAKE_SECURE_LINK_S dwMode SAKE_CIPHER_MODE_E

23.4. Enum Application to Function Prototypes

Function Parameter Enum
SakeClient_HandshakeHandler return SAKE_HANDSHAKE_ERROR_E
SakeClient_HandshakeHandler statePtr SAKE_CLIENT_STATE_E*
SakeServer_HandshakeHandler return SAKE_HANDSHAKE_ERROR_E
SakeServer_HandshakeHandler statePtr SAKE_SERVER_STATE_E*
SakeClient_InitChallengeAndSetState return SAKE_CLIENT_STATE_E
SakeError_IsValidCode errorCode SAKE_HANDSHAKE_ERROR_E
SakeError_GetStringForCode errorCode SAKE_HANDSHAKE_ERROR_E
SakeKeyDB_IsValidDeviceType deviceType SAKE_DEVICE_TYPE_E

24. v260 New Subsystem: Passkey / SRP-6a Secure Authentication

Added in 2.6.0 / v260. The classes in v210 README §18.12 (flagged "Missing Functions") are now implemented: SAKE_PASSKEY_S, SAKE_CLIENT_MEMORY_S, SAKE_SERVER_MEMORY_S, Sake_Passkey_FromInteger, Sake_Client_SetPasskey, Sake_Server_SetPasskey. The v260 build embeds a full SRP-6a implementation plus a self-contained fixed-width bignum math library and SHA-256/HMAC hash layer, added to support a passkey-based pairing step.

All functions below have been renamed in the Ghidra database (libandroid-sake-lib_v260.so). ~126 functions of the passkey/SRP/bignum call-graph were named this session.

24.1. New Data Types (Ghidra-created)

Struct Size Purpose
SAKE_PASSKEY_S 0x14 (20) pBytes[16], byteCount @0x10
SAKE_TRANSCRIPT_HASH_S 0x90 (144) Running transcript hash state (2× SHA-256 ctx A/B @+0x70/+0x80)
SAKE_SRP_CLIENT_S 0x314 (788) SRP client session (SAKE_CLIENT_MEMORY_S + 0x90)
SAKE_SRP_SERVER_S 0x310 (784) SRP server session (SAKE_SERVER_MEMORY_S + 0x90)
SAKE_CLIENT_MEMORY_S 0x3B8 (952) transcript[0x90] + srp_client[0x314] + passkey@0x3A4
SAKE_SERVER_MEMORY_S 0x3B4 (948) transcript[0x90] + srp_server[0x310] + passkey@0x3A0
SAKE_MP_INT 0x10 (16) bignum: used, alloc, sign, *dp (28-bit limbs)
SAKE_PERMIT_S 0x14 (20) version, deviceType, proprietaryBytes[10], reserved
SAKE_SECURE_MESSAGE_S 0x08 (8) pBytes, byteCount

24.2. SRP Struct Field Layout

// SAKE_SRP_CLIENT_S / SAKE_SRP_SERVER_S (share most of the layout)
typedef struct {
    uint8_t  passkeyBytes[16];     // +0x00   copy of SAKE_PASSKEY_S.pBytes
    uint32_t byteCount;            // +0x10   passkey length (<= 0x10)
    uint8_t  reserved[0x140];      // +0x14   0x150-0x140 header region (incl. A/B/verifier slots)
    uint8_t  modulusArea[0x1AC];   // +0x150/0x154  bignum + SRP params (PN, Pg, Px, scalars)
    uint8_t  flags;                // +0x2FC/0x300  error/state flag byte
    uint32_t state;                // +0x300/0x304  current SRP step (1..5)
    uint32_t substate;             // +0x304/0x308  sub-step / progress bitmap
    uint32_t errorCode;            // +0x308/0x30C  SRP error code
    uint32_t result;               // +0x30C/0x310  last operation result
} SAKE_SRP_CLIENT_S;               // server version omits the trailing word (0x310)

The transcript hash is a chained SHA-256 over the exchanged messages (client identity, public A/B, proofs). SRP errors are stored into flags+state/substate fields and mirrored into the output message's +0x50 length slot to signal failure.

24.3. Passkey Message Wire Format

+----------------------------------------------------------------------------+
| SAKE_PASSKEY_S (20 bytes)                                                   |
|   pBytes[0..15] : up to 16-byte passkey (big-endian when from integer)      |
|   byteCount     : actual byte length (<= 16)                                |
+----------------------------------------------------------------------------+

SakePasskey_FromInteger(value) materializes a 4-byte big-endian passkey (byteCount = 4). Memory structs embed the passkey at a fixed offset:

  • Client: SAKE_CLIENT_MEMORY_S.passkey @ 0x3A4
  • Server: SAKE_SERVER_MEMORY_S.passkey @ 0x3A0

24.4. JNI Surface (new in v260)

JNI Export Addr Internal
Sake_Passkey_FromInteger 0x0001563e SakePasskey_FromInteger @0x16e84
Sake_Client_SetPasskey 0x00015b6a SakeClient_SetPasskey @0x1612e
Sake_Server_SetPasskey 0x0001605a SakeServer_SetPasskey @0x17036
SAKE_PASSKEY_S_pBytes_get/set 0x11e8f/0x11ed5
SAKE_PASSKEY_S_byteCount_get/set 0x11dfd/0x11e46
SAKE_PASSKEY_BYTE_CAPACITY_get 0x11db2 const 0x10
SAKE_CLIENT_MEMORY_S_passkey/srp/transcript_get/set 0x110cb-0x1124c
SAKE_SERVER_MEMORY_S_passkey/srp/transcript_get/set 0x1226b-0x123ec
SAKE_CLIENT_S_pMemory_get/set 0x11673/0x116b9
SAKE_SERVER_S_pMemory_get/set 0x12925/0x1296b

24.5. SRP State Machine

SRPClient_Step (0x19184) and SRPServer_Step (0x197f0) are step machines driven by a message-in/message-out protocol. state 1..5 and the associated error codes are stored in the SRP struct's state/substate/errorCode fields.

State Client action Server action
1 Emit pub A (from random a, passkey-seeded) Emit pub B (verifier v = g^x mod N + random b)
2 Consume B, copy into +0x94/server +0x24 Consume A
3 Compute shared secret Compute shared secret
4 Verify proof M / emit success (byteCount=1, out[0]=4) Verify proof, expose session key
5 Done (SRP_IsDoneMessage) Done

Support functions:

  • SRPClient_Init (0x190c8) / SRPServer_Init (0x19568) — set up modulus/generator, import passkey
  • SRPClient_GetSessionKey (0x194e0) / SRPServer_GetSessionKey (0x19a68)
  • SRP_ComputeSharedSecret (0x1ad3c) — SRP-6a shared-secret arithmetic
  • SRP_VerifyProof (0x1b12c) — constant-time proof compare (memcmp + cmov)
  • SRP_HashToBignum (0x1a74c) — H() to bignum conversion
  • SRP_ExportPublicValue (0x1abb0) / BN_ExportFixed (0x1b0a8)
  • SRP_FillOutputRandom (0x19a9e) — error-stuffing random output

24.6. Hash / HMAC Layer (embedded)

Function Addr Description
SHA256_Init 0x1a168 Initialize SHA-256 ctx (IVs)
SHA256_Update 0x19f88 Absorb bytes (buffered, 64-byte blocks)
SHA256_CompressBlock 0x1a1d4 64-byte block transform
SHA256_Final 0x1a0b4 Finalize digest
ByteswapWordArray 0x1a0a4 Word endian swap
HMAC_SetKey 0x19b1a HMAC key setup (ipad/opad)
HMAC_Update 0x19c14 HMAC absorb
HMAC_Final 0x19c88 HMAC digest
PRF_OneShotHMAC 0x19d20 One-shot HMAC
PRF_ExpandCounterLoop 0x19db4 PRF expansion (counter)
SakeCrypto_DeriveKeyMaterialHF 0x19eac HMAC-based key derivation
SakeTranscript_Init/Update/Final 0x16b6c/0x16b96/0x16bb6 Transcript chaining

24.7. Bignum Library (28-bit limbs)

Self-contained fixed-width big-integer engine used by SRP. Structure SAKE_MP_INT { int used; int alloc; int sign; uint *dp; }.

Core ops (all @ addresses in 0x1bXXX-0x1dXXX):

Function Description
BN_Init / BN_Grow / BN_Clear / BN_Dup allocation & lifecycle
BN_Copy / BN_Zero / BN_SetSmallDigit value ops
BN_Clamp / BN_BitLength / BN_ByteLength / BN_Compare(Mag/Signed/Digit) introspection
BN_SAddAbs / BN_SSubAbs / BN_AddSigned / BN_SubSigned add/sub (schoolbook)
BN_MulSchoolbook / BN_MulKaratsuba / BN_MulDigit / BN_MultiplyDispatch multiply
BN_SquareDispatch / BN_SqrSmall / BN_SqrKaratsuba squaring
BN_ShiftLeftMul2d / BN_ShiftRightDiv2d / BN_Mod2dMask shifts/masks
BN_DivideLong / BN_ModReduce / BN_ModExp division / reduction / modular exponentiation
BN_ModExpWinSetup / BN_ModExpWinReduce / BN_ModExpPathA/B windowed modexp stages
BN_ImportBytesBE / BN_ExportBytesBE / BN_ExportFixed / SRP_ExportPublicValue serialization
BN_HalveDiv2 / BN_DoubleMul2 / BN_ReduceBy2k / BN_SetBit auxiliary math

24.8. Handshake State Handlers (updated in v260)

Client (SAKE_CLIENT_S *client, pMemory @ +0x74):

Function Addr State
ClientHandshake_State1Init 0x1616c 1/6/10
ClientHandshake_State2Exchange 0x161b0 2
ClientHandshake_State3Derive 0x162a2 3
ClientHandshake_State4Challenge 0x16264 7
ClientHandshake_State5Validate 0x1637c 8
ClientHandshake_State6Auth 0x163a8 4
ClientHandshake_State7FinalizeEncrypt 0x16438 5
ClientHandshake_State8Error 0x1641c 9

Server (SAKE_SERVER_S *server, pMemory @ +0xA4):

Function Addr State
ServerHandshake_State1Init 0x17076 1/6/10
ServerHandshake_State3Derive 0x17220 3
ServerHandshake_State4Auth 0x173dc 4
ServerHandshake_State5DecryptPermit 0x17530 5
ServerHandshake_State7Challenge 0x171b6 7
ServerHandshake_State8Validate 0x173a8 8

Crypto glue reused with SRP: SakeCrypto_DeriveSessionKey (0x166f8), SakeCrypto_GenerateProof (0x166ac), SakeCrypto_InitCipherStateHF (0x16780), SakeCrypto_DecryptValidateServerPermitHF (0x16f58), SakeCrypto_VerifyMessageHF (0x17cb0), SakeCrypto_ComputeCMACHF (0x17d64).


25. Usage: Passkey-Based SRP-6a Authentication

25.1. When This Applies

The passkey/SRP path is a protocol-version 2 handshake. Both the client and the server structs carry a protocolVersion field (SAKE_CLIENT_S + 0x04, SAKE_SERVER_S + 0x04); nonzero (observed value 2) selects the passkey + SRP flow. The kernel activates it from the Java layer by allocating a Memory container, setting the passkey, and handing the container to the handshake state machine. Version-1 (legacy) handshakes skip SRP entirely and use the v210 challenge/CMAC flow.

25.2. Call Sequence (Java / JNI)

This is the intended usage order reconstructed from the JNI exports and their callers:

// 1. Allocate a passkey and fill it (big-endian 4-byte from an int, or raw bytes)
SWIGTYPE_p_SAKE_PASSKEY_S passkey = SakeJNI.new_SAKE_PASSKEY_S();
SakeJNI.SAKE_PASSKEY_S_pBytes_set(passkey, raw16Bytes);   // or:
Sake_Passkey_FromInteger(passkey, secretInt);              // byteCount = 4
// SakeJNI.SAKE_PASSKEY_S_byteCount_set(passkey, len);      // optional: <= 16

// 2. Allocate a per-session Memory container
SWIGTYPE_p_SAKE_CLIENT_MEMORY_S mem = SakeJNI.new_SAKE_CLIENT_MEMORY_S();   // 0x3b8 bytes
//   (server side uses new_SAKE_SERVER_MEMORY_S, 0x3b4 bytes)

// 3. Bind the passkey into the handshake
Sake_Client_SetPasskey(client, mem, passkey);   // stores passkey @ mem+0x3A4, sets protocolVersion/state
//   (server side: Sake_Server_SetPasskey(server, mem, passkey); passkey @ mem+0x3A0)

// 4. Run the existing handshake pump (SRP steps hook in automatically)
SAKE_HANDSHAKE_STATUS_E st;
while ((st = Sake_Client_Handshake(client, inMsg, outMsg)) == HANDSHAKE_IN_PROGRESS) {
    // exchange outMsg with peer, read next inMsg
}
// on success, session keys are derived and the secure link (AES-128-CTR + 16-bit CMAC)
// at client->secureLink (+0x40, 0x30 bytes) is marked established

// 5. Normal secure messaging afterwards
Sake_Client_SecureForSending(client, userMsg, secureMsg);
Sake_Client_UnsecureAfterReceiving(client, secureMsg, userMsg);

Error surface: like the rest of SAKE, passkey failures surface as SAKE_HANDSHAKE_ERROR_E values (lastError at client+0x78 / server+0xA8, and in the output message byteCount=0). SRP-internal errors are tracked in the SRP struct's state/substate/errorCode and collapsed into these public codes by the state handlers (e.g. err 0x15 = passkey/SRP failure, 0x14 = SRP misuse).

25.3. SRP-6a Protocol Steps (mapped to binary)

Shared input:  passkey (<= 16 bytes), N (embedded SRP modulus), g = generator
Client                                          Server
  |  a = random                                  |  b = random
  |  x = H(salt || passkey)                      |  v = g^x mod N   (verifier)
  |  A = g^a mod N                               |
  |---------------------------------- A -------->|
  |                                               |  B = (k*v + g^b) mod N
  |<--------------------------------- B ---------|
  |  u = H(A || B)                                |  u = H(A || B)
  |  S = (B - k*g^x)^(a + u*x) mod N             |  S = (A*v^u)^b mod N
  |  K = PRF(S, transcript)                      |  K = PRF(S, transcript)
  |  M1 = H(A || B || K)                         |  M1 = H(A || B || K)
  |---------------------------------- M -------->|
  |                                               |  verify M1 == SRP_VerifyProof
  |<--------------------------------- M2 --------|
  |  verify M2                                   |
  |  -> session keys -> AES-CTR + CMAC secure link

Implemented by:

  • SRPClient_Init / SRPServer_Init — group setup, scalar/verifier init
  • SRP_HashToBignum (0x1a74c) — x = H(...) conversion, g^x
  • BN_ModExpBaseSecret (0x1ab24), SRP_ComputeSharedSecret (0x1ad3c) — A/B and S
  • SRP_VerifyProof (0x1b12c) — constant-time M comparison
  • SakeTranscript_* + SakeCrypto_DeriveKeyMaterialHF / SakeCrypto_DeriveSessionKey (0x166f8) — session key K + proof transcript

25.4. Security Model — NO Public-Key / Certificate Checking

This library performs no certificate validation and has no public-key infrastructure.

Verified by full binary scan: zero hits for cert / X509 / PKIX / pem, zero for RSA / ECDSA / ECC / signature / private key, and no asymmetric crypto code anywhere (the bignum library is used only for SRP modular arithmetic). The only key-checking symbol, SakeServer_VerifyKeyDerivation (0x17848), verifies SRP key derivation via memcmp — it is not a certificate or signature check.

What that means:

  • Peer authenticity = possession of the passkey. A party that knows the same passkey and passes SRP_VerifyProof is accepted as a legitimate peer. There is no cert chain, key pair, or signature to bind the exchange to a long-term identity.
  • The SAKE "permit" objects (SAKE_PERMIT_S) are symmetric tokens (version/deviceType/proprietary bytes), not signed certificates.
  • Threat boundary: if a passkey leaks, is weak, or is factory-shared across many pumps/sensors, a MITM with the passkey is indistinguishable from a real peer within this library — nothing outside adds a PKI backstop. This matches Medtronic devices where the passkey/pairing secret is provisioned out-of-band and assumed confidential.
  • SRP-6a still protects against offline dictionary attacks on the passkey (an eavesdropper cannot brute-force the secret from the transcript), which is the core reason it supersedes a plain challenge/CMAC handshake in v260.

About

No description, website, or topics provided.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors