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
godoor
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"
"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{}
}

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