drivers/sesame/protocol_test.go
raw ยท 4359 bytes
package sesame
import (
"bytes"
"crypto/aes"
"encoding/hex"
"testing"
)
func unhex(s string) []byte {
b, e := hex.DecodeString(s)
if e != nil {
panic(e)
}
return b
}
func TestCMACRFC4493(t *testing.T) {
key := unhex("2b7e151628aed2a6abf7158809cf4f3c")
for _, v := range [][2]string{{"", "bb1d6929e95937287fa37d129b756746"}, {"6bc1bee22e409f96e93d7e117393172a", "070a16b46b4d4144f79bdd9dd04a287c"}, {"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411", "dfa66747de9ae63030ca32611497c827"}} {
got, e := cmac(key, unhex(v[0]))
if e != nil || hex.EncodeToString(got) != v[1] {
t.Fatalf("CMAC mismatch %x %v", got, e)
}
}
}
func TestCCMIndependentVectors(t *testing.T) {
// Generated independently with Python cryptography/OpenSSL AESCCM, 4-byte
// tag and AAD 00. Nonzero upper counter bytes catch the SDK's stale comment.
key := unhex("000102030405060708090a0b0c0d0e0f")
token := unhex("11223344")
c, auth, e := newCipher(key, token)
if e != nil || hex.EncodeToString(auth) != "1d7c7f1d52493a30cc2ee841f461afaf" {
t.Fatal("session CMAC", e)
}
vectors := map[int]string{0: "e9edea60", 1: "6d19848f18", 16: "6d872d6cad7752dde946535210729276a0c5f8de", 17: "6d872d6cad7752dde946535210729276b241e495c5", 40: "6d872d6cad7752dde946535210729276b20637e7243ebf8f473862b5b847d68743047366a43bfe0b98f3ce1f"}
for n, want := range vectors {
p := make([]byte, n)
for i := range p {
p[i] = byte(i)
}
c.tx = 0x0102030405060708
c.rx = c.tx
got, e := c.encrypt(p)
if e != nil || hex.EncodeToString(got) != want {
t.Fatalf("n=%d: %x %v", n, got, e)
}
plain, e := c.decrypt(got)
if e != nil || !bytes.Equal(p, plain) {
t.Fatalf("decrypt: %v", e)
}
c.rx--
got[len(got)-1] ^= 1
before := c.rx
if _, e = c.decrypt(got); e == nil || c.rx != before {
t.Fatal("accepted corrupt tag or advanced counter")
}
}
if _, _, e := newCipher(key, token[:3]); e == nil {
t.Fatal("accepted bad token")
}
b, _ := aes.NewCipher(key)
if _, e := ccmOpen(b, make([]byte, 13), []byte{1}); e == nil {
t.Fatal("accepted short CCM")
}
}
func TestFragmentBoundaries(t *testing.T) {
for _, n := range []int{1, 19, 20, 38, 39, 1024} {
p := make([]byte, n)
for i := range p {
p[i] = byte(i)
}
for _, kind := range []byte{1, 2} {
fs, e := fragments(kind, p)
if e != nil {
t.Fatal(e)
}
var a assembler
var out []byte
for i, f := range fs {
k, b, e := a.feed(f)
if e != nil {
t.Fatal(e)
}
if i < len(fs)-1 && k != 0 {
t.Fatal("early completion")
}
if i == len(fs)-1 {
if k != kind {
t.Fatal("wrong kind")
}
out = b
}
}
if !bytes.Equal(p, out) {
t.Fatal("reassembly")
}
}
}
for _, p := range [][]byte{nil, {1}, {6, 1}, {0, 1}} {
var a assembler
if _, _, e := a.feed(p); e == nil {
t.Fatalf("accepted malformed %x", p)
}
}
var a assembler
a.feed([]byte{1, 0})
if _, _, e := a.feed([]byte{3, 0}); e == nil {
t.Fatal("accepted interrupted message")
}
a = assembler{}
a.feed(append([]byte{1}, make([]byte, 19)...))
for i := 0; i < 52; i++ {
a.feed(append([]byte{0}, make([]byte, 19)...))
}
if _, _, e := a.feed(append([]byte{0}, make([]byte, 19)...)); e == nil {
t.Fatal("unbounded message")
}
}
func TestMechanicalStatusAndBoundary(t *testing.T) {
var s State
if e := s.publish(81, []byte{0, 0, 0, 128, 255, 255, 0x3a}); e != nil {
t.Fatal(e)
}
if *s.Position != -1 || s.Target != nil || !s.Locked || !s.Stopped || !s.Critical || !s.BatteryCritical {
t.Fatalf("bad status %+v", s)
}
if e := s.publish(20, []byte{255, 255}); e != nil || s.Boundary == nil || *s.Boundary != -1 {
t.Fatal("-1 is a valid boundary")
}
if e := s.publish(80, []byte{90, 0, 0, 0, 0, 0}); e != nil || *s.LockPosition != 90 || *s.UnlockPosition != 0 {
t.Fatal("settings")
}
if e := s.publish(81, []byte{1}); e == nil {
t.Fatal("short status")
}
}
func TestAdvertisement(t *testing.T) {
b := append([]byte{21, 0, 1}, unhex("00112233445566778899aabbccddeeff")...)
d, ok := Advertisement(b)
if !ok || d.UUID != "00112233-4455-6677-8899-aabbccddeeff" || d.Model != "sesame_6_pro" || !d.Registered {
t.Fatal(d)
}
b[0] = 1
if _, ok := Advertisement(b); ok {
t.Fatal("accepted OS2")
}
if _, ok := Advertisement([]byte{21}); ok {
t.Fatal("short advertisement")
}
}