Checkpoint 5

This commit is contained in:
Philipp Wellner 2023-07-30 12:38:47 +02:00
parent 9116635efc
commit 9b1eba127a
No known key found for this signature in database
GPG Key ID: 6968CB52112B79BC
9 changed files with 177 additions and 43 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.idea .idea
godoor godoor
godoor_server/godoor_server godoor_server/godoor_server
godoor_server/keys.json

4
Makefile Normal file
View File

@ -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'

View File

@ -1,3 +0,0 @@
#!/bin/bash
GOOS=linux GOARCH=arm64 go build && scp godoor rpi4b:

View File

@ -6,7 +6,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/joho/godotenv"
"io" "io"
"log" "log"
"net/http" "net/http"
@ -15,8 +14,12 @@ import (
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
"time"
"godoor/hash"
"github.com/joho/godotenv"
) )
import "time"
const wiegand_a = 17 const wiegand_a = 17
const wiegand_b = 18 const wiegand_b = 18
@ -54,12 +57,20 @@ type KeepDoorOpen struct {
timer *time.Timer timer *time.Timer
} }
type OpenedTimestamp struct {
Opened time.Time
Closed *time.Time
}
var config Config var config Config
var globalLock sync.Mutex var globalLock sync.Mutex
var validUids ValidUids var validUids ValidUids
var wiegand Wiegand var wiegand Wiegand
var keepDoorOpen KeepDoorOpen var keepDoorOpen KeepDoorOpen
var lastSyncedTimestamp *time.Time
var openDoorTimestamps []OpenedTimestamp
func main() { func main() {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
@ -85,6 +96,7 @@ func main() {
// cleanup // cleanup
} }
func setup() { func setup() {
log.Println("Started Setup") log.Println("Started Setup")
@ -99,6 +111,8 @@ func setup() {
} }
log.Println("HW Setup done") log.Println("HW Setup done")
http.DefaultClient.Timeout = 120 * time.Second
go func() { go func() {
for { for {
waitEvents() waitEvents()
@ -107,8 +121,6 @@ func setup() {
log.Println("Initialized longpoll event loop") log.Println("Initialized longpoll event loop")
http.DefaultClient.Timeout = 120 * time.Second
for { for {
log.Println("Start initial token population") log.Println("Start initial token population")
err := reloadTokens() err := reloadTokens()
@ -120,6 +132,8 @@ func setup() {
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
} }
go runHttpServer()
log.Println("Initial token population success") log.Println("Initial token population success")
go cardRunner(wiegand) go cardRunner(wiegand)
@ -127,13 +141,36 @@ func setup() {
log.Println("Setup completed") 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 { func OpenAndCloseDoor(w Wiegand) error {
err := w.OpenDoor() err := OpenDoor(w)
if err != nil { if err != nil {
return err return err
} }
if keepDoorOpen.until.After(time.Now().Add(5 * time.Second)) { if keepDoorOpen.until.After(time.Now()) {
fmt.Println("Door is already open") fmt.Println("Door is already open")
return nil return nil
} }
@ -142,7 +179,7 @@ func OpenAndCloseDoor(w Wiegand) error {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
err = w.CloseDoor() err = CloseDoor(w)
if err != nil { if err != nil {
return err return err
} }
@ -151,6 +188,27 @@ func OpenAndCloseDoor(w Wiegand) error {
return nil 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) { func cardRunner(w Wiegand) {
for { for {
card, err := w.GetCardUid() card, err := w.GetCardUid()
@ -159,7 +217,7 @@ func cardRunner(w Wiegand) {
} }
printCardId(card) printCardId(card)
hashedHex := hashCardUid(card) hashedHex := hash.HashCardUid(card)
log.Println(hashedHex) log.Println(hashedHex)
globalLock.Lock() globalLock.Lock()
@ -287,6 +345,9 @@ func reloadTokens() error {
updateKeepOpenDoor(*cl.KeepOpenUntil) updateKeepOpenDoor(*cl.KeepOpenUntil)
} }
t := time.Now()
lastSyncedTimestamp = &t
return nil return nil
} }
@ -300,20 +361,20 @@ func updateKeepOpenDoor(newKeepOpenTime time.Time) {
if newKeepOpenTime.After(time.Now()) { if newKeepOpenTime.After(time.Now()) {
log.Printf("Keeping door open until %v", newKeepOpenTime) log.Printf("Keeping door open until %v", newKeepOpenTime)
wiegand.OpenDoor() OpenDoor(wiegand)
timer := time.AfterFunc(time.Until(newKeepOpenTime), handleKeepDoorOpenCloseCleanup) timer := time.AfterFunc(time.Until(newKeepOpenTime), handleKeepDoorOpenCloseCleanup)
keepDoorOpen = KeepDoorOpen{ keepDoorOpen = KeepDoorOpen{
timer: timer, timer: timer,
until: newKeepOpenTime, until: newKeepOpenTime,
} }
} else { } else {
wiegand.CloseDoor() CloseDoor(wiegand)
} }
} }
func handleKeepDoorOpenCloseCleanup() { func handleKeepDoorOpenCloseCleanup() {
fmt.Println("Keep door open time is reached!") fmt.Println("Keep door open time is reached!")
wiegand.CloseDoor() CloseDoor(wiegand)
keepDoorOpen = KeepDoorOpen{} keepDoorOpen = KeepDoorOpen{}
} }

4
godoor_server/Makefile Normal file
View File

@ -0,0 +1,4 @@
dev:
GOOS=linux GOARCH=arm64 go build .
scp godoor_server workshopdoor:/tmp/
ssh workshopdoor 'mv -f /tmp/godoor_server ~/'

View File

@ -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"}}]

40
hash/godoor_test.go Normal file
View File

@ -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))
}
}

21
hash/hash.go Normal file
View File

@ -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
}

View File

@ -1,15 +1,11 @@
package main package main
import ( import (
"encoding/binary"
"encoding/hex"
"fmt" "fmt"
"golang.org/x/crypto/scrypt"
"log" "log"
"sync"
"time" "time"
)
import (
"github.com/warthog618/gpiod" "github.com/warthog618/gpiod"
) )
@ -29,41 +25,45 @@ type WiegandHW struct {
bitTimeoutTimer *time.Timer bitTimeoutTimer *time.Timer
solenoidLine *gpiod.Line solenoidLine *gpiod.Line
lock sync.RWMutex
} }
func (w *WiegandHW) OpenDoor() error { func (w *WiegandHW) OpenDoor() error {
w.lock.Lock()
defer w.lock.Unlock()
open, _ := w.isDoorOpen()
if open {
return nil
}
return w.solenoidLine.SetValue(1) return w.solenoidLine.SetValue(1)
} }
func (w *WiegandHW) CloseDoor() error { func (w *WiegandHW) CloseDoor() error {
w.lock.Lock()
defer w.lock.Unlock()
open, _ := w.isDoorOpen()
if !open {
return nil
}
return w.solenoidLine.SetValue(0) return w.solenoidLine.SetValue(0)
} }
func (w *WiegandHW) IsDoorOpen() (bool, error) { 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() i, err := w.solenoidLine.Value()
if err != nil { if err != nil {
return false, err return false, err
} }
return i == 1, nil 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) { func (w *WiegandHW) GetCardUid() (uint64, error) {
// Wait for bit timeout // Wait for bit timeout
@ -78,7 +78,7 @@ func (w *WiegandHW) GetCardUid() (uint64, error) {
} }
var card uint64 = 0 var card uint64 = 0
for i := 63; i != 0; i-- { for i := 63; i >= 0; i-- {
if w.bits[i] == true { if w.bits[i] == true {
card |= 1 << (63 - i) card |= 1 << (63 - i)
} }
@ -89,15 +89,15 @@ func (w *WiegandHW) GetCardUid() (uint64, error) {
func (w *WiegandHW) wiegandAEvent(evt gpiod.LineEvent) { func (w *WiegandHW) wiegandAEvent(evt gpiod.LineEvent) {
w.bitTimeoutTimer.Reset(w.bitTimeout) w.bitTimeoutTimer.Reset(w.bitTimeout)
w.bits[w.bitNr] = false w.bits[w.bitNr] = true
fmt.Printf("0") fmt.Printf("1")
w.bitNr += 1 w.bitNr += 1
} }
func (w *WiegandHW) wiegandBEvent(evt gpiod.LineEvent) { func (w *WiegandHW) wiegandBEvent(evt gpiod.LineEvent) {
w.bitTimeoutTimer.Reset(w.bitTimeout) w.bitTimeoutTimer.Reset(w.bitTimeout)
w.bits[w.bitNr] = true w.bits[w.bitNr] = false
fmt.Printf("1") fmt.Printf("0")
w.bitNr += 1 w.bitNr += 1
} }
@ -139,3 +139,10 @@ func (w *WiegandHW) WiegandClose() {
w.bLine.Close() w.bLine.Close()
w.solenoidLine.Close() w.solenoidLine.Close()
} }
func printCardId(card uint64) {
for i := 0; i < 7; i++ {
fmt.Printf("%02x", (card>>(8*i))&0xff)
}
fmt.Printf("\n")
}