Skip to content

Hiroshi/certificate pinning#1065

Draft
hiroshihorie wants to merge 8 commits intomainfrom
hiroshi/certificate-pinning
Draft

Hiroshi/certificate pinning#1065
hiroshihorie wants to merge 8 commits intomainfrom
hiroshi/certificate-pinning

Conversation

@hiroshihorie
Copy link
Copy Markdown
Member

@hiroshihorie hiroshihorie commented May 5, 2026

Summary

Adds native certificate pinning support for SDK-owned TLS traffic in the Flutter SDK.

  • Applies to SDK-owned HTTPS requests and WSS signaling on native platforms
  • Does not apply to Flutter web, WebRTC media, TURN, or application-owned token endpoints
  • Validates the peer certificate during TLS connection setup before HTTP or WSS request bytes are sent
  • Supports SPKI SHA-256 pins, exact leaf certificate bytes, and custom trusted certificate bytes
  • Adds CertificatePinningException so pinning failures can fail fast and surface clearly

Rule Behavior

Rules are selected by host. Exact hosts, single-label wildcards like *.livekit.cloud, and * are supported. Empty hosts applies a rule to every SDK-owned TLS connection.

All rules that match a host are applied. Within one check type, any configured value may match. Across check types, each configured type must pass.

That means:

  • Multiple matching SPKI rules form one accepted pin set
  • primaryPins and backupPins are both accepted
  • SPKI pins plus exact leaf certificate bytes require both checks to pass
  • trustedCertificateBytes replaces platform trusted roots for that matching host

Example: SPKI Pins

Use this when possible. It is friendlier to certificate renewal because the pin follows the public key rather than the full leaf certificate.

RoomOptions(
  networkOptions: NetworkOptions(
    certificatePinning: CertificatePinningOptions(
      rules: [
        CertificatePinningRule(
          hosts: ['*.livekit.cloud'],
          primaryPins: ['sha256/current-public-key-pin'],
          backupPins: ['sha256/next-public-key-pin'],
        ),
      ],
    ),
  ),
)

Example: Exact Leaf Certificate

Use this when the app should trust one exact leaf certificate loaded from assets. Renewing or changing the leaf certificate requires shipping updated certificate bytes unless another configured check also allows the new certificate.

CertificatePinningRule(
  hosts: ['my-project.livekit.cloud'],
  pinnedLeafCertificateBytes: [
    certificate.buffer.asUint8List(),
  ],
)

Example: Custom Trust Store

Use this for the asset-based Flutter pattern where the app ships a leaf, intermediate, or root certificate as trust material.

CertificatePinningRule(
  hosts: ['*.livekit.cloud'],
  trustedCertificateBytes: [
    certificate.buffer.asUint8List(),
  ],
)

Example: Combine Modes

Checks can be combined. This requires the TLS chain to validate against the custom trust store and the peer certificate SPKI to match one configured pin.

CertificatePinningRule(
  hosts: ['*.livekit.cloud'],
  primaryPins: ['sha256/current-public-key-pin'],
  backupPins: ['sha256/next-public-key-pin'],
  trustedCertificateBytes: [
    certificate.buffer.asUint8List(),
  ],
)

Testing

  • Added unit coverage for SPKI parsing, real X.509 parsing, wildcard matching, and merged rule semantics
  • Added native IO tests for HTTPS and WSS failure timing before request bytes are sent
  • Added native IO tests for exact leaf certificates, trusted leaf certificates, trusted CA certificates, and combined modes

Local verification:

flutter analyze --no-pub
flutter test --no-pub --reporter compact

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant