Skip to content

Instantly share code, notes, and snippets.

@pcarrier
Created May 15, 2024 18:25
Show Gist options
  • Save pcarrier/f7d2e97fd5f0dc8dca542ca9b610be73 to your computer and use it in GitHub Desktop.
Save pcarrier/f7d2e97fd5f0dc8dca542ca9b610be73 to your computer and use it in GitHub Desktop.
package main
import (
"encoding/base64"
"errors"
"fmt"
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
"github.com/fxamacker/cbor/v2"
"golang.org/x/crypto/nacl/sign"
"math"
"net/http"
"time"
)
type zeroPWMessage struct {
_ struct{} `cbor:",toarray"`
Timestamp time.Time
Payload *[]byte
}
func (s server) serveZeroPW(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
reader := http.MaxBytesReader(w, r.Body, 128*1024)
var req [][]byte
if err := cbor.NewDecoder(reader).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
_ = reader.Close()
if len(req) == 0 {
http.Error(w, "Empty CBOR array", http.StatusBadRequest)
return
}
if len(req[0]) != 32 {
http.Error(w, "Invalid public key length", http.StatusBadRequest)
return
}
pubKey := (*[32]byte)(req[0])
path := fmt.Sprintf("Z/%s", base64.RawURLEncoding.EncodeToString(pubKey[:]))
switch len(req) {
case 1:
signed, err := s.store.Get(r.Context(), path, false)
if err != nil {
var e *awshttp.ResponseError
if errors.As(err, &e) {
http.Error(w, e.Error(), e.HTTPStatusCode())
} else {
internalError(w, err)
}
return
}
if _, err := w.Write(signed); err != nil {
internalError(w, err)
}
return
case 2:
signed := req[1]
destination := make([]byte, 0, len(signed)-sign.Overhead)
signedPayload, ok := sign.Open(destination, signed, pubKey)
if !ok {
http.Error(w, "Invalid signature", http.StatusBadRequest)
return
}
var msg zeroPWMessage
if err := cbor.Unmarshal(signedPayload, &msg); err != nil {
http.Error(w, fmt.Sprintf("%v (%x)", err, signedPayload), http.StatusBadRequest)
return
}
if math.Abs(time.Now().Sub(msg.Timestamp).Seconds()) > 60.0 {
http.Error(w, "Time mismatch", http.StatusBadRequest)
return
}
if msg.Payload == nil {
if err := s.store.Delete(r.Context(), path); err != nil {
internalError(w, err)
}
} else {
if len(*msg.Payload) > 64*1024 {
http.Error(w, "Payload too large", http.StatusRequestEntityTooLarge)
return
}
if err := s.store.Put(r.Context(), path, signed); err != nil {
internalError(w, err)
}
}
return
default:
http.Error(w, "Invalid CBOR array length", http.StatusBadRequest)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment