Understanding how to verify an received authenticator (pairing - step 20)

I’m hitting a wall with the Authorize App (Smart Lock 1 - 4th Generation) example (check out 9. Command usage examples). Steps 19 and 20 are throwing me off. How am I supposed to verify the authenticator, exactly?

Here’s my test code (Swift):

    @Test("Verify Authenticator")
    func verifyStep20() {
        print("\n=== STEP 20 ===")
        
        // Authorization-ID command (Step 19)
        let rxData = Data(hex: "07006156B043406A20B4FCEF7E0684F4BDF4950420DD87FE855DB76306819A5B0CBAE05C34000177383DA0D13BD22354671DE3C7DF4FD3E6BD6162D21E61A313FF043AAD58C68652E338A4BF9FC7D99857B0DB85A3A9EA4C")
        // Shared Secret (Step 8a)
        let sharedSecretHex = "915561587D86815B709EDD5819D8C6F2E883DA3C86F461F13B84228B84533E04"
        // rxData.subdata(in: 2..<34)
        let expectedAuthenticator = "6156B043406A20B4FCEF7E0684F4BDF4950420DD87FE855DB76306819A5B0CBA" // ???
        
        let key = SymmetricKey(data: Data(hex: sharedSecretHex))
               
        let authenticator = rxData.subdata(in: 2..<34)
        let authorizationData = rxData.subdata(in: 34..<38)
        let authorizationID = authorizationData.withUnsafeBytes { $0.load(as: UInt32.self) }
        let uuid = rxData.subdata(in: 38..<54)
        let nonce = rxData.subdata(in: 54..<86)
        
        print("authenticator: \(authenticator.hexString.uppercased())")
        print("authorizationID:  \(authorizationID) - \(authorizationData.hexString.uppercased())")
        print("uuid: \(uuid.hexString.uppercased())")
        print("nonce: \(nonce.hexString.uppercased())")
        
        // ???
        var verifyData = Data()
        verifyData.append(authenticator)
        verifyData.append(authorizationData)
        verifyData.append(uuid)
        verifyData.append(nonce)
        let calculatedHmac = HMAC<SHA256>.authenticationCode(for: verifyData, using: key)
                
        print("Calculated: \(Data(calculatedHmac).hexString.uppercased())")
        print("Expected:   \(expectedAuthenticator.uppercased())")
        
        #expect(Data(calculatedHmac).hexString.lowercased().starts(with: expectedAuthenticator.lowercased()), "Step 20 Logic Mismatch!")
        
        if Data(calculatedHmac).hexString.uppercased().starts(with: expectedAuthenticator.uppercased()) {
            print("✅ SUCCESS!")
        }
    }

I think I misread the docs. The pairing part works like a charm (meaning without verifying the received authenticator), but I really want to nail down how to verify it in my app. Any clues?

I’ve found the solution: In step 16a, right before the CRC, the nonceABF is included—this piece is key for verifying the authenticator:

        // ...
        let step16a = Data(hex: "06008927B9AC416A5B1E367B58BC8D59C39E7FC8D0FD6DCC8F05863D34518CBAA2210027ED7E185465737461707000000000000000000000000000000000000000000000000000A72AE5BB4726C60FBB1AA28297081DCE45F76731711E39AC2B5FF9ACBDF03844C12C")
        let nonceABF = step16a.subdata(in: 71..<103) // A72AE5BB4726C60FBB1AA28297081DCE45F76731711E39AC2B5FF9ACBDF03844

        // ...

        var verifyData = Data()
        verifyData.append(authorizationIdBuffer)
        verifyData.append(uuid)
        verifyData.append(nonceK)
        verifyData.append(nonceABF)
        //
        
        let calculatedHmac = HMAC<SHA256>.authenticationCode(for: verifyData, using: key)

        print("Calculated: \(Data(calculatedHmac).hexString.uppercased())")
        print("Expected:   \(expectedAuthenticator.uppercased())")
        
        #expect(Data(calculatedHmac).hexString.lowercased().starts(with: expectedAuthenticator.lowercased()), "Step 20 Logic Mismatch!")

        // ...

This topic was automatically closed 60 minutes after the last reply. New replies are no longer allowed.