Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions pkg/bluetooth/bluetooth.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"hash/crc32"
"sync"
"time"
Expand Down Expand Up @@ -323,8 +324,11 @@ func (b *Ble) loop(stop chan bool, in *input) {
case <-stop:
return
case msg := <-b.messageOutput:
if err := b.writeMessage(msg, in, stop); err != nil {
if err := b.writeMessage(msg, in, stop); err == errStopped {
return
} else if err != nil {
// The phone gets no answer and asks again.
log.Errorf("pkg bluetooth; error writing message: %s", err)
}
case cmd := <-in.cmd:
msg, err := b.readMessage(cmd, in, stop)
Expand Down Expand Up @@ -373,8 +377,8 @@ func (b *Ble) expectCommand(expected Packet, in *input, stop chan bool) error {
if err != nil {
return err
}
if !bytes.Equal(expected[:1], cmd[:1]) {
log.Fatalf("pkg bluetooth; expected command: %s. received command: %s", expected, cmd)
if len(cmd) == 0 || cmd[0] != expected[0] {
return fmt.Errorf("expected command: %x. received command: %x", expected, cmd)
}
return nil
}
Expand Down Expand Up @@ -467,8 +471,8 @@ func (b *Ble) readMessage(cmd Packet, in *input, stop chan bool) (*message.Messa
var checksum []byte

log.Trace("pkg bluetooth; Reading RTS")
if !bytes.Equal(CmdRTS[:1], cmd[:1]) {
log.Fatalf("pkg bluetooth; expected command: %x. received command: %x", CmdRTS, cmd)
if len(cmd) == 0 || cmd[0] != CmdRTS[0] {
return nil, fmt.Errorf("expected command: %x. received command: %x", CmdRTS, cmd)
}
log.Trace("pkg bluetooth; Sending CTS")

Expand Down
64 changes: 46 additions & 18 deletions pkg/pod/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pod

import (
"encoding/json"
"fmt"
"sync"
"time"

Expand Down Expand Up @@ -109,6 +110,14 @@ func (p *Pod) readSetup() (*message.Message, bool) {
}
}

// startOver hangs up on a phone whose message the pod can't use. The phone
// reconnects and sets up a new session.
func (p *Pod) startOver(format string, args ...interface{}) {
log.Errorf("pkg pod; "+format+"; hanging up", args...)
p.ble.ShutdownConnection()
go p.StartAcceptingCommands()
}

func (p *Pod) StartActivation() {

pair := &pair.Pair{}
Expand All @@ -117,14 +126,16 @@ func (p *Pod) StartActivation() {
return
}
if err := pair.ParseSP1SP2(msg); err != nil {
log.Fatalf("pkg pod; error parsing SP1SP2 %s", err)
p.startOver("error parsing SP1SP2 %s", err)
return
}
// read PDM public key and nonce
if msg, ok = p.readSetup(); !ok {
return
}
if err := pair.ParseSPS1(msg); err != nil {
log.Fatalf("pkg pod; error parsing SPS1 %s", err)
p.startOver("error parsing SPS1 %s", err)
return
}

msg, err := pair.GenerateSPS1()
Expand All @@ -138,7 +149,10 @@ func (p *Pod) StartActivation() {
if msg, ok = p.readSetup(); !ok {
return
}
pair.ParseSPS2(msg)
if err := pair.ParseSPS2(msg); err != nil {
p.startOver("error parsing SPS2 %s", err)
return
}

// send POD conf value
msg, err = pair.GenerateSPS2()
Expand All @@ -153,7 +167,8 @@ func (p *Pod) StartActivation() {
}
err = pair.ParseSP0GP0(msg)
if err != nil {
log.Fatalf("pkg pod; could not parse SP0GP0: %s", err)
p.startOver("could not parse SP0GP0: %s", err)
return
}

// send P0 constant
Expand Down Expand Up @@ -184,7 +199,8 @@ func (p *Pod) EapAka() {
}
err := session.ParseChallenge(msg)
if err != nil {
log.Fatalf("pkg pod; error parsing the EAP-AKA challenge: %s", err)
p.startOver("error parsing the EAP-AKA challenge: %s", err)
return
}

msg, err = session.GenerateChallengeResponse()
Expand All @@ -199,7 +215,8 @@ func (p *Pod) EapAka() {
log.Debugf("pkg pod; success? %x", msg.Payload) // TODO: figure out how error looks like
err = session.ParseSuccess(msg)
if err != nil {
log.Fatalf("pkg pod; error parsing the EAP-AKA Success packet: %s", err)
p.startOver("error parsing the EAP-AKA Success packet: %s", err)
return
}
p.state.CK, p.state.NoncePrefix = session.CKNoncePrefix()

Expand Down Expand Up @@ -272,27 +289,35 @@ func (p *Pod) CommandLoop(pMsg PodMsgBody) {

decrypted, err := encrypt.DecryptMessage(p.state.CK, p.state.NoncePrefix, p.state.NonceSeq, msg)
if err != nil {
log.Fatalf("pkg pod; could not decrypt message: %s", err)
p.mtx.Unlock()
p.startOver("could not decrypt message: %s", err)
return
}
p.state.NonceSeq++

if len(decrypted.Payload) < 16 {
p.mtx.Unlock()
p.startOver("decrypted payload too short: %x", decrypted.Payload)
return
}
cmd, err := command.Unmarshal(decrypted.Payload)
if err != nil {
log.Fatalf("pkg pod; could not unmarshal command: %s", err)
p.mtx.Unlock()
p.startOver("could not unmarshal command: %s", err)
return
}
cmdSeq, requestID, err := cmd.GetHeaderData()
if err != nil {
log.Fatalf("pkg pod; could not get command header data: %s", err)
p.mtx.Unlock()
p.startOver("could not get command header data: %s", err)
return
}
p.state.CmdSeq = cmdSeq

log.Debugf("pkd pod; cmd: %x", decrypted.Payload)
data = decrypted.Payload
n = len(data)
log.Debugf("pkg pod; len = %d", n)
if n < 16 {
log.Fatalf("pkg pod; decrypted. Payload too short")
}
pMsg.MsgBodyCommand = data[13 : n-5]
if data[13] == 0x1c {
pMsg.DeactivateFlag = true
Expand Down Expand Up @@ -370,16 +395,19 @@ func (p *Pod) CommandLoop(pMsg PodMsgBody) {
}
// TODO check for SEQ numbers here and the Ack flag
decrypted, err = encrypt.DecryptMessage(p.state.CK, p.state.NoncePrefix, p.state.NonceSeq, msg)
if err != nil {
log.Fatalf("pkg pod; could not decrypt message: %s", err)
}
p.state.NonceSeq++
if len(decrypted.Payload) != 0 {
log.Fatalf("pkg pod; this should be empty message with ACK header %s", spew.Sdump(msg))
if err == nil {
p.state.NonceSeq++
if len(decrypted.Payload) != 0 {
err = fmt.Errorf("not an empty ACK: %s", spew.Sdump(msg))
}
}
p.state.Save()
p.takeSnapshot()
p.mtx.Unlock()
if err != nil {
p.startOver("could not read the response ACK: %s", err)
return
}

log.Debugf("notifyingStateChange")
p.notifyStateChange()
Expand Down
85 changes: 85 additions & 0 deletions pkg/pod/pod_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package pod

import (
"path/filepath"
"sync"
"testing"
"time"

"github.com/avereha/pod/pkg/bluetooth"
"github.com/avereha/pod/pkg/message"
)

// radio carries one side's notifications to the other side, and records
// when the pod hangs up.
type radio struct {
mu sync.Mutex
peer *bluetooth.Ble
hungUp chan bool
}

func (r *radio) to() *bluetooth.Ble {
r.mu.Lock()
defer r.mu.Unlock()
return r.peer
}

func (r *radio) NotifyCmd(p bluetooth.Packet) error {
if b := r.to(); b != nil {
b.CmdWritten(p)
}
return nil
}

func (r *radio) NotifyData(p bluetooth.Packet) error {
if b := r.to(); b != nil {
b.DataWritten(p)
}
return nil
}

func (r *radio) Advertise([]byte) error { return nil }
func (r *radio) AdvertiseState(bluetooth.AdvertState) error { return nil }

func (r *radio) Disconnect() {
select {
case r.hungUp <- true:
default:
}
}

func newBle(t *testing.T, r *radio) *bluetooth.Ble {
b, err := bluetooth.New(func(*bluetooth.Ble) (bluetooth.Radio, error) { return r, nil })
if err != nil {
t.Fatal(err)
}
return b
}

// A session message the pod can't parse ends the connection, not the pod.
func TestAGarbledSessionMessageHangsUp(t *testing.T) {
toPhone, toPod := &radio{hungUp: make(chan bool, 1)}, &radio{}
podBle, phone := newBle(t, toPhone), newBle(t, toPod)
toPod.peer = podBle
toPhone.peer = phone

state := NewFreshState(filepath.Join(t.TempDir(), "state.toml"))
state.LTK = make([]byte, 16)
p := New(podBle, state)

podBle.Connected()
go p.StartAcceptingCommands()
podBle.CmdWritten(bluetooth.Packet{6, 1, 4})
phone.Connected()
phone.StartMessageLoop()

garbled := message.NewMessage(message.MessageTypeSessionEstablishment, []byte{1, 2, 3, 4}, []byte{5, 6, 7, 8})
garbled.Payload = []byte{0x2b, 0x97, 0x1c, 0x90, 0xb9, 0xdc, 0x43, 0x9b}
phone.WriteMessage(garbled)

select {
case <-toPhone.hungUp:
case <-time.After(2 * time.Second):
t.Fatal("the pod did not hang up")
}
}
Loading