ESPProvision iOS Library

repository·master·Indexed 41 days ago

https://github.com/espressif/esp-idf-provisioning-ios

A Swift library for provisioning ESP32, ESP32-S2, and ESP8266 devices with network credentials via BLE or SoftAP. It includes support for SRP authentication, QR code scanning, and various security schemes, with installation available via CocoaPods and Swift Package Manager (version 2.1.1+).

Tokens
3.5K
Snippets
9
Records
13
Agent score
51%

What's inside ESPProvision

  1. Obtain an ESPDevice instance

    master

    An ESPDevice is a virtual representation of an ESP32, ESP32-S2, or ESP8266 device. You can obtain an instance using the ESPProvisionManager singleton through three methods:

    1. Search for BLE devices

    Search for discoverable BLE devices matching specific criteria. Note that SoftAP search is currently not supported on iOS.

    2. Scan a QR Code

    Extract device information by scanning a valid QR code using a provided UIView for the camera preview.

    3. Manual Creation

    Create an ESPDevice manually by providing the device name, transport type, and security settings.

    // Search via BLE
    ESPProvisionManager.shared.searchESPDevices(devicePrefix:"Prefix", transport:.ble, security:.secure) { deviceList, _ in
    }
    
    // Scan via QR Code
    ESPProvisionManager.shared.scanQRCode(scanView: scannerView) { espDevice, _ in
    }
    
    // Manual creation
    ESPProvisionManager.shared.createESPDevice(deviceName: deviceName, transport: transport, security: security) { espDevice, _ in
    }
  2. Build different app variants for Wi-Fi and BLE

    master

    The repository provides multiple Xcode project schemes depending on the transport layer (Wi-Fi or BLE) and the security scheme (0 or 1) you wish to test.

    Security Scheme 1 Requirement

    When using Security Scheme 1 (for either BLE or Wi-Fi), you must ensure that the Proof of Possession key matches between:

    1. The ESP-IDF example configuration: Example Configuration -> Proof-of-possession.
    2. The iOS app configuration: The Info.plist file located in ./EspressifProvision/EspressifProvision/.

    Available Build Variants

    XCode Project SchemeTransportSecurity SchemeESP-IDF ExampleESP-IDF Example menuconfig
    WifiSec0 DebugWi-Fi0examples/provisioning/softap_provExample Configuration -> Deselect Use Security Version 1
    WifiSec1 DebugWi-Fi1examples/provisioning/softap_provExample Configuration -> Select Use Security Version 1
    BLESec0 DebugBLE0examples/provisioning/ble_provExample Configuration -> Deselect Use Security Version 1
    BLESec1 DebugBLE1examples/provisioning/ble_provExample Configuration -> Select Use Security Version 1
    BLESec1 ReleaseBLE1examples/provisioning/ble_provExample Configuration -> Select Use Security Version 1
    WifiSec1 ReleaseWi-Fi1examples/provisioning/softap_provExample Configuration -> Select Use Security Version 1
  3. Install dependencies for ESP-IDF Provisioning iOS

    master

    To build the ESP-IDF Provisioning iOS app, you must install several system-level dependencies using Homebrew and CocoaPods.

    1. Install swiftformat

    Install swiftformat using Homebrew:

    brew install swiftformat

    2. Install protoc-gen-swift

    To handle protobuf serialization, install the swift-protobuf command line utility. Ensure the installed version is greater than 1.1.1 to match the SwiftProtobuf version in the EspressifProvision/Podfile.

    brew install swift-protobuf
    # Verify version
    protoc-gen-swift --version

    3. Install CocoaPods dependencies

    Navigate to the EspressifProvision directory and install the required pods (including SwiftProtobuf and Curve25519):

    cd EspressifProvision
    pod install
    # Install swiftformat
    brew install swiftformat
    
    # Install swift-protobuf
    brew install swift-protobuf
    
    # Install CocoaPods dependencies
    cd EspressifProvision
    pod install
  4. Provision an ESP device

    master

    The provisioning workflow follows these steps:

    1. Connect: Establish a session with the ESPDevice using .connect(delegate:completion:). If the device requires Proof of Possession (PoP) or a username (for security version 2), implement the corresponding delegate methods.
    2. Scan Wi-Fi (Optional): Once connected, you can scan for available Wi-Fi networks visible to the device using .scanWifiList.
    3. Provision: Send the chosen SSID and passphrase to the device using .provision(ssid:passPhrase:completion:).
    // 1. Connect
    espDevice.connect(delegate: self) { status in
        if status == .connected {
            // 2. Scan Wi-Fi list
            espDevice.scanWifiList { wifiList, _ in 
                // Use wifiList to let user choose
            }
            
            // 3. Provision
            espDevice.provision(ssid: "MY_SSID", passPhrase: "MY_PASSWORD") { status in
                // Handle provisioning status
            }
        }
    }
    
    // Delegate methods for security
    func getProofOfPossesion(forDevice: ESPDevice, completionHandler: @escaping (String) -> Void) {
        completionHandler(proofOfPossession)
    }
    
    func getUsername(forDevice: ESPProvision.ESPDevice, completionHandler: @escaping (String?) -> Void) {
        completionHandler(username)
    }
  5. Configure ESP Application Settings in Info.plist

    master

    The behavior of the provisioning app is controlled by specific keys in the Info.plist file under the ESP Application Setting section. You can modify these to change transport mechanisms, security modes, and feature availability.

    | Key | Accepted Values | Default | Description |
    |:----:|:-------:|:------------:|:-------------:|
    | `ESP Device Type` | `Both`, `BLE`, `SoftAP` | `Both` | Defines the transport mechanism app will use for provisioning |
    | `ESP Securtiy Mode` | `secure`, `unsecure` | `secure` | Defines whether the communication with device is encrypted or unsecure |
    | `ESP Enable Setting` | `true`, `false` | `true` | Set this as false if you want device type and security mode does not change while running the app |
    | `ESP Allow QR Code Scan` | `true`, `false` | `true` | Set false when QR code is not available or supported for device |
    | `ESP Allow Prefix Search` | `true`, `false` | `true` | Set false when search by prefix is not required for BLE devices |
  6. Perform SRP Authentication Flow

    master

    To complete an SRP authentication session using the Client class, follow these three steps:

    1. Start Authentication

    Call startAuthentication() to retrieve the client's identity and public key. This is the first step sent to the server.

    2. Process Server Challenge

    Once the server responds with a challenge, call processChallenge(salt:publicKey:). This method performs the heavy cryptographic lifting: it calculates the shared secret, derives the session key, and generates a proof (clientVerify) that the client knows the password.

    • Input: salt (the user-specific salt s) and serverPublicKey (the server's public key B).
    • Output: A tuple containing clientVerify (the proof M to send to the server) and a sessionKey (a SymmetricKey for subsequent encrypted communication).
    • Throws: AuthenticationFailure.invalidPublicKey if the server's public key is invalid.

    3. Verify Server Session

    After the server verifies your proof, it will send its own proof that it derived the same session key. Call verifySession(keyProof:) to validate this.

    • Input: serverKeyProof (the data sent by the server).
    • Throws:
      • AuthenticationFailure.missingChallenge if called before processChallenge.
      • AuthenticationFailure.keyProofMismatch if the server's proof is invalid.

    Upon successful verification, isAuthenticated becomes true and the sessionKey is officially available for use.

    // Step 1: Start
    let (username, publicKey) = client.startAuthentication()
    
    // ... Send username and publicKey to server ...
    
    // Step 2: Process Challenge
    do {
        let (clientVerify, sessionKey) = try client.processChallenge(
            salt: serverSalt,
            publicKey: serverPublicKey
        )
        
        // ... Send clientVerify to server ...
        
        // Step 3: Verify Server
        try client.verifySession(keyProof: serverKeyProof)
        
        print("Authenticated! Session key is ready.")
    } catch {
        print("Authentication failed: \(error)")
    }
  7. Configure required iOS permissions

    master

    To ensure successful provisioning via SoftAP (Wi-Fi), your app must handle the following iOS permissions:

    • Location Permission: Required since iOS 13 to access the SSID (Wi-Fi network name). This is necessary to verify if the iOS device is connected to the device's SoftAP.
    • Local Network Permission: Required since iOS 14 to communicate over the local network. This is necessary to send and receive provisioning data with SoftAP devices.
  8. Access Client SRP properties

    master

    The Client class provides several properties to inspect the state of the authentication session:

    • isAuthenticated: A boolean indicating if the session is fully authenticated (i.e., the server has verified the password and the client has verified the server's key).
    • publicKey: Returns the client's public key A as Data. A new one is generated for every session.
    • privateKey: Returns the client's private key a as Data.
    • sessionKey: Returns the derived session key K as Data. This property returns nil if isAuthenticated is false.