From 21a17e10bf489483f360a1a273372304afdff708 Mon Sep 17 00:00:00 2001 From: Anton Tolchanov Date: Sun, 14 Dec 2025 08:40:33 +0000 Subject: [PATCH] Allow passkey to be provided by a callback function Some devices only show the pairing passkey after the pairing process has started. Such cases might require passkey to be provided by the user interactively. To support that, allow AuthData to specify a function callback which will be used to populate the passkey. --- linux/hci/smp/context.go | 10 +++++----- linux/hci/smp/handler.go | 6 +++--- linux/hci/smp/transport.go | 4 ++-- smp.go | 22 +++++++++++++++++++++- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/linux/hci/smp/context.go b/linux/hci/smp/context.go index 0dbdcde..1149f0c 100644 --- a/linux/hci/smp/context.go +++ b/linux/hci/smp/context.go @@ -89,7 +89,7 @@ func (p *pairingContext) checkPasskeyConfirm() error { kax := MarshalPublicKeyX(p.scECDHKeys.public) nb := p.remoteRandom i := p.passKeyIteration - key := p.authData.Passkey + key := p.authData.GetPasskey() //this gets the bit of the passkey for the current iteration z := 0x80 | (byte)((key&(1<>uint(i)) @@ -114,7 +114,7 @@ func (p *pairingContext) checkPasskeyConfirm() error { return nil } -//todo: key should be set at the beginning +// todo: key should be set at the beginning func (p *pairingContext) generatePassKeyConfirm() ([]byte, []byte) { kbx := MarshalPublicKeyX(p.scRemotePubKey) kax := MarshalPublicKeyX(p.scECDHKeys.public) @@ -125,7 +125,7 @@ func (p *pairingContext) generatePassKeyConfirm() ([]byte, []byte) { } i := p.passKeyIteration - z := 0x80 | (byte)((p.authData.Passkey&(1<>uint(i)) + z := 0x80 | (byte)((p.authData.GetPasskey()&(1<>uint(i)) calcConf, err := smpF4(kax, kbx, nai, z) if err != nil { @@ -176,7 +176,7 @@ func (p *pairingContext) checkDHKeyCheck() error { ra := make([]byte, 16) if p.pairingType == Passkey { keyBytes := make([]byte, 4) - binary.BigEndian.PutUint32(keyBytes, uint32(p.authData.Passkey)) + binary.BigEndian.PutUint32(keyBytes, uint32(p.authData.GetPasskey())) ra[12] = keyBytes[0] ra[13] = keyBytes[1] ra[14] = keyBytes[2] @@ -230,7 +230,7 @@ func (p *pairingContext) checkLegacyConfirm() error { k := make([]byte, 16) if p.pairingType == Passkey { - k = getLegacyParingTK(p.authData.Passkey) + k = getLegacyParingTK(p.authData.GetPasskey()) } c1, err := smpC1(k, sRand, preq, pres, p.localAddrType, diff --git a/linux/hci/smp/handler.go b/linux/hci/smp/handler.go index 97d2007..f94c67c 100644 --- a/linux/hci/smp/handler.go +++ b/linux/hci/smp/handler.go @@ -151,7 +151,7 @@ func onLegacyRandom(t *transport) ([]byte, error) { //calculate STK var k []byte if t.pairing.pairingType == Passkey { - k = getLegacyParingTK(t.pairing.authData.Passkey) + k = getLegacyParingTK(t.pairing.authData.GetPasskey()) } else { k = getLegacyParingTK(0) } @@ -314,8 +314,8 @@ func continuePassKeyPairing(t *transport) { t.send(out) } -//Core spec v5.0 Vol 3, Part H, 2.3.5.1 -//Tables 2.6, 2.7, and 2.8 +// Core spec v5.0 Vol 3, Part H, 2.3.5.1 +// Tables 2.6, 2.7, and 2.8 var ioCapsTableSC = [][]int{ {JustWorks, JustWorks, Passkey, JustWorks, Passkey}, {JustWorks, NumericComp, Passkey, JustWorks, NumericComp}, diff --git a/linux/hci/smp/transport.go b/linux/hci/smp/transport.go index 0499c77..4b6e0d1 100644 --- a/linux/hci/smp/transport.go +++ b/linux/hci/smp/transport.go @@ -153,7 +153,7 @@ func (t *transport) sendDHKeyCheck() error { rb := make([]byte, 16) if t.pairing.pairingType == Passkey { keyBytes := make([]byte, 4) - binary.BigEndian.PutUint32(keyBytes, uint32(t.pairing.authData.Passkey)) + binary.BigEndian.PutUint32(keyBytes, uint32(t.pairing.authData.GetPasskey())) rb[12] = keyBytes[0] rb[13] = keyBytes[1] rb[14] = keyBytes[2] @@ -198,7 +198,7 @@ func (t *transport) sendMConfirm() error { k := make([]byte, 16) if t.pairing.pairingType == Passkey { - k = getLegacyParingTK(t.pairing.authData.Passkey) + k = getLegacyParingTK(t.pairing.authData.GetPasskey()) } c1, err := smpC1(k, r, preq, pres, diff --git a/smp.go b/smp.go index 980a199..02d8958 100644 --- a/smp.go +++ b/smp.go @@ -1,6 +1,26 @@ package ble type AuthData struct { + // Passkey is the numeric passkey for pairing. Passkey int + + // PasskeyFn is a function that returns the passkey for pairing. + // If Passkey is not set, this function will be called to + // retrieve the passkey. + PasskeyFn func() int + + // OOBData is the out-of-band data for pairing. OOBData []byte -} \ No newline at end of file +} + +// GetPasskey returns the passkey for pairing. If Passkey is not set, +// it calls PasskeyFn to populate it. +func (a *AuthData) GetPasskey() int { + if a.Passkey != 0 { + return a.Passkey + } + if a.PasskeyFn != nil { + a.Passkey = a.PasskeyFn() + } + return a.Passkey +}