From 9b1eba127a60a7cdb02acb8af89defd2d34fa92d Mon Sep 17 00:00:00 2001 From: Philipp Wellner Date: Sun, 30 Jul 2023 12:38:47 +0200 Subject: [PATCH] Checkpoint 5 --- .gitignore | 1 + Makefile | 4 ++ build.sh | 3 -- godoor.go | 83 +++++++++++++++++++++++++++++++++++------ godoor_server/Makefile | 4 ++ godoor_server/keys.json | 1 - hash/godoor_test.go | 40 ++++++++++++++++++++ hash/hash.go | 21 +++++++++++ wiegand.go | 63 +++++++++++++++++-------------- 9 files changed, 177 insertions(+), 43 deletions(-) create mode 100644 Makefile delete mode 100644 build.sh create mode 100644 godoor_server/Makefile delete mode 100644 godoor_server/keys.json create mode 100644 hash/godoor_test.go create mode 100644 hash/hash.go diff --git a/.gitignore b/.gitignore index c62119f..1074e4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea godoor godoor_server/godoor_server +godoor_server/keys.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..84d39f4 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +dev: + GOOS=linux GOARCH=arm64 go build . + scp godoor workshopdoor:/tmp/ + ssh workshopdoor 'mv -f /tmp/godoor ~/ && sudo systemctl restart godoor' diff --git a/build.sh b/build.sh deleted file mode 100644 index 879b55f..0000000 --- a/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -GOOS=linux GOARCH=arm64 go build && scp godoor rpi4b: diff --git a/godoor.go b/godoor.go index 8292eb2..c3a8d36 100644 --- a/godoor.go +++ b/godoor.go @@ -6,7 +6,6 @@ import ( "context" "encoding/json" "fmt" - "github.com/joho/godotenv" "io" "log" "net/http" @@ -15,8 +14,12 @@ import ( "strings" "sync" "syscall" + "time" + + "godoor/hash" + + "github.com/joho/godotenv" ) -import "time" const wiegand_a = 17 const wiegand_b = 18 @@ -54,12 +57,20 @@ type KeepDoorOpen struct { timer *time.Timer } +type OpenedTimestamp struct { + Opened time.Time + Closed *time.Time +} + var config Config var globalLock sync.Mutex var validUids ValidUids var wiegand Wiegand var keepDoorOpen KeepDoorOpen +var lastSyncedTimestamp *time.Time +var openDoorTimestamps []OpenedTimestamp + func main() { ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) @@ -85,6 +96,7 @@ func main() { // cleanup } + func setup() { log.Println("Started Setup") @@ -99,6 +111,8 @@ func setup() { } log.Println("HW Setup done") + http.DefaultClient.Timeout = 120 * time.Second + go func() { for { waitEvents() @@ -107,8 +121,6 @@ func setup() { log.Println("Initialized longpoll event loop") - http.DefaultClient.Timeout = 120 * time.Second - for { log.Println("Start initial token population") err := reloadTokens() @@ -120,6 +132,8 @@ func setup() { time.Sleep(10 * time.Second) } + go runHttpServer() + log.Println("Initial token population success") go cardRunner(wiegand) @@ -127,13 +141,36 @@ func setup() { log.Println("Setup completed") } +func runHttpServer() { + http.HandleFunc("/lastsync", func(w http.ResponseWriter, r *http.Request) { + e := json.NewEncoder(w) + e.Encode(map[string]any{ + "last_synced": lastSyncedTimestamp, + }) + }) + http.HandleFunc("/opened", func(w http.ResponseWriter, r *http.Request) { + e := json.NewEncoder(w) + e.Encode(map[string]any{ + "open_timestamps": openDoorTimestamps, + }) + }) + http.HandleFunc("/isopen", func(w http.ResponseWriter, r *http.Request) { + e := json.NewEncoder(w) + open, _ := wiegand.IsDoorOpen() + e.Encode(map[string]any{ + "open": open, + }) + }) + http.ListenAndServe(":3334", nil) +} + func OpenAndCloseDoor(w Wiegand) error { - err := w.OpenDoor() + err := OpenDoor(w) if err != nil { return err } - if keepDoorOpen.until.After(time.Now().Add(5 * time.Second)) { + if keepDoorOpen.until.After(time.Now()) { fmt.Println("Door is already open") return nil } @@ -142,7 +179,7 @@ func OpenAndCloseDoor(w Wiegand) error { time.Sleep(5 * time.Second) - err = w.CloseDoor() + err = CloseDoor(w) if err != nil { return err } @@ -151,6 +188,27 @@ func OpenAndCloseDoor(w Wiegand) error { return nil } +func OpenDoor(w Wiegand) error { + open, _ := w.IsDoorOpen() + if open { + return nil + } + w.OpenDoor() + openDoorTimestamps = append(openDoorTimestamps, OpenedTimestamp{Opened: time.Now(), Closed: nil}) + return nil +} + +func CloseDoor(w Wiegand) error { + open, _ := w.IsDoorOpen() + if !open { + return nil + } + w.CloseDoor() + t := time.Now() + openDoorTimestamps[len(openDoorTimestamps)-1].Closed = &t + return nil +} + func cardRunner(w Wiegand) { for { card, err := w.GetCardUid() @@ -159,7 +217,7 @@ func cardRunner(w Wiegand) { } printCardId(card) - hashedHex := hashCardUid(card) + hashedHex := hash.HashCardUid(card) log.Println(hashedHex) globalLock.Lock() @@ -287,6 +345,9 @@ func reloadTokens() error { updateKeepOpenDoor(*cl.KeepOpenUntil) } + t := time.Now() + lastSyncedTimestamp = &t + return nil } @@ -300,20 +361,20 @@ func updateKeepOpenDoor(newKeepOpenTime time.Time) { if newKeepOpenTime.After(time.Now()) { log.Printf("Keeping door open until %v", newKeepOpenTime) - wiegand.OpenDoor() + OpenDoor(wiegand) timer := time.AfterFunc(time.Until(newKeepOpenTime), handleKeepDoorOpenCloseCleanup) keepDoorOpen = KeepDoorOpen{ timer: timer, until: newKeepOpenTime, } } else { - wiegand.CloseDoor() + CloseDoor(wiegand) } } func handleKeepDoorOpenCloseCleanup() { fmt.Println("Keep door open time is reached!") - wiegand.CloseDoor() + CloseDoor(wiegand) keepDoorOpen = KeepDoorOpen{} } diff --git a/godoor_server/Makefile b/godoor_server/Makefile new file mode 100644 index 0000000..2ea0f70 --- /dev/null +++ b/godoor_server/Makefile @@ -0,0 +1,4 @@ +dev: + GOOS=linux GOARCH=arm64 go build . + scp godoor_server workshopdoor:/tmp/ + ssh workshopdoor 'mv -f /tmp/godoor_server ~/' diff --git a/godoor_server/keys.json b/godoor_server/keys.json deleted file mode 100644 index ea6600c..0000000 --- a/godoor_server/keys.json +++ /dev/null @@ -1 +0,0 @@ -[{"token":{"uid_hash":"873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},{"token":{"uid_hash":"a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},{"token":{"uid_hash":"d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}},{"token":{"uid_hash":"d72c87d0f077c7766f2985dfab30e8955c373a13a1e93d315203939f542ff86e73ee37c31f4c4b571f4719fa8e3589f12db8dcb57ea9f56764bb7d58f64cf705"}},{"token":{"uid_hash":"873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},{"token":{"uid_hash":"a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},{"token":{"uid_hash":"d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}},{"token":{"uid_hash":"873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},{"token":{"uid_hash":"a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},{"token":{"uid_hash":"d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}}] \ No newline at end of file diff --git a/hash/godoor_test.go b/hash/godoor_test.go new file mode 100644 index 0000000..ed09494 --- /dev/null +++ b/hash/godoor_test.go @@ -0,0 +1,40 @@ +package hash + +import ( + "fmt" + "testing" +) + +func TestHash(t *testing.T) { + hash := HashCardUid(0x000000000F0AD2301) + if hash != "a6d9ba36ecb5f8e6312f40ee260ad59e9cca3c6ce073bf072df3324c0072886196e6823a7c758ab567fc53e91fbbda297a4efe0072e41316c56446ef126a5180" { + t.Errorf("WRONG Key generated: %q", hash) + } + + hash = HashCardUid(0x3CBB5AAD) + if hash != "1e7bbc1aeeff4bfe58ea8d97df7cd0f97599fa31633d3a1f57d138549b3968c20affb186fe0b85570d34718b273d269bcd28b2be59faf8122451d91a2cdc5f18" { + t.Errorf("WRONG Key generated: %q", hash) + } +} + +func TestBits(t *testing.T) { + + bits := make([]bool, 64) + trueBits := []int{0, 1, 2, 3, 8, 10, 12, 13, 15, 18, 22, 23, 31} + for _, i := range trueBits { + bits[i] = true + } + + var card uint64 = 0 + for i := 63; i >= 0; i-- { + if bits[i] == true { + card |= 1 << (63 - i) + } + } + + cardId := fmt.Sprintf("%x", card) + fmt.Println(cardId) + if card != 0xF0AD230100000000 { + panic(fmt.Errorf("WROOONG: %x", card)) + } +} diff --git a/hash/hash.go b/hash/hash.go new file mode 100644 index 0000000..331103e --- /dev/null +++ b/hash/hash.go @@ -0,0 +1,21 @@ +package hash + +import ( + "encoding/binary" + "encoding/hex" + + "golang.org/x/crypto/scrypt" +) + +func HashCardUid(card uint64) string { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, card) + + hashed, err := scrypt.Key(b, []byte("hkRXwLlQKmCJoy5qaahp"), 16384, 8, 1, 64) + if err != nil { + panic(err) // can only happen when scrypt params are garbage + } + + hashedHex := hex.EncodeToString(hashed) + return hashedHex +} diff --git a/wiegand.go b/wiegand.go index 90fddf3..1143249 100644 --- a/wiegand.go +++ b/wiegand.go @@ -1,15 +1,11 @@ package main import ( - "encoding/binary" - "encoding/hex" "fmt" - "golang.org/x/crypto/scrypt" "log" + "sync" "time" -) -import ( "github.com/warthog618/gpiod" ) @@ -29,41 +25,45 @@ type WiegandHW struct { bitTimeoutTimer *time.Timer solenoidLine *gpiod.Line + lock sync.RWMutex } func (w *WiegandHW) OpenDoor() error { + w.lock.Lock() + defer w.lock.Unlock() + + open, _ := w.isDoorOpen() + if open { + return nil + } + return w.solenoidLine.SetValue(1) } func (w *WiegandHW) CloseDoor() error { + w.lock.Lock() + defer w.lock.Unlock() + + open, _ := w.isDoorOpen() + if !open { + return nil + } + return w.solenoidLine.SetValue(0) } func (w *WiegandHW) IsDoorOpen() (bool, error) { + w.lock.RLock() + defer w.lock.RUnlock() + return w.isDoorOpen() +} +func (w *WiegandHW) isDoorOpen() (bool, error) { i, err := w.solenoidLine.Value() if err != nil { return false, err } return i == 1, nil } -func printCardId(card uint64) { - for i := 0; i < 7; i++ { - fmt.Printf("%02x", (card>>(8*i))&0xff) - } - fmt.Printf("\n") -} - -func hashCardUid(card uint64) string { - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, card) - hashed, err := scrypt.Key(b, []byte(config.uidSalt), 16384, 8, 1, 64) - if err != nil { - panic(err) // can only happen when scrypt params are garbage - } - - hashedHex := hex.EncodeToString(hashed) - return hashedHex -} func (w *WiegandHW) GetCardUid() (uint64, error) { // Wait for bit timeout @@ -78,7 +78,7 @@ func (w *WiegandHW) GetCardUid() (uint64, error) { } var card uint64 = 0 - for i := 63; i != 0; i-- { + for i := 63; i >= 0; i-- { if w.bits[i] == true { card |= 1 << (63 - i) } @@ -89,15 +89,15 @@ func (w *WiegandHW) GetCardUid() (uint64, error) { func (w *WiegandHW) wiegandAEvent(evt gpiod.LineEvent) { w.bitTimeoutTimer.Reset(w.bitTimeout) - w.bits[w.bitNr] = false - fmt.Printf("0") + w.bits[w.bitNr] = true + fmt.Printf("1") w.bitNr += 1 } func (w *WiegandHW) wiegandBEvent(evt gpiod.LineEvent) { w.bitTimeoutTimer.Reset(w.bitTimeout) - w.bits[w.bitNr] = true - fmt.Printf("1") + w.bits[w.bitNr] = false + fmt.Printf("0") w.bitNr += 1 } @@ -139,3 +139,10 @@ func (w *WiegandHW) WiegandClose() { w.bLine.Close() w.solenoidLine.Close() } + +func printCardId(card uint64) { + for i := 0; i < 7; i++ { + fmt.Printf("%02x", (card>>(8*i))&0xff) + } + fmt.Printf("\n") +}