diff --git a/server/main.go b/server/main.go index 60c8461..afd1e03 100644 --- a/server/main.go +++ b/server/main.go @@ -13,12 +13,14 @@ import ( "bytes" "crypto/ed25519" "database/sql" + "encoding/binary" "encoding/hex" "encoding/json" "errors" "fmt" "io/ioutil" "log" + "math" "net/http" "os" "strings" @@ -28,6 +30,8 @@ import ( _ "github.com/mattn/go-sqlite3" ) +const MAX_SIG = (1 << 256) - 1 + func must(err error) { if err != nil { log.Fatal(err) @@ -142,6 +146,25 @@ func (s *Spring83Server) getBoard(key string) (*Board, error) { }, nil } +func (s *Spring83Server) boardCount() (int, error) { + query := ` + SELECT count(*) + FROM boards + ` + row := s.db.QueryRow(query) + + var count int + err := row.Scan(&count) + if err != nil { + if err != sql.ErrNoRows { + return 0, err + } + panic(err) + } + + return count, nil +} + func (s *Spring83Server) publishBoard(w http.ResponseWriter, r *http.Request) { w.Header().Set("Spring-Version", "83") @@ -186,7 +209,36 @@ func (s *Spring83Server) publishBoard(w http.ResponseWriter, r *http.Request) { http.Error(w, "Old content", http.StatusConflict) return } - log.Printf("%s\n", mtime) + + // if the server doesn't have any board stored for , then it must + // apply another check. The key, interpreted as a 256-bit number, must be + // less than a threshold defined by the server's difficulty factor: + if curBoard == nil { + count, err := s.boardCount() + if err != nil { + log.Printf(err.Error()) + http.Error(w, "internal error", http.StatusInternalServerError) + return + } + difficultyFactor := math.Pow(float64(count)/10_000_000, 4) + w.Header().Add("Spring-Difficulty", fmt.Sprintf("%f", difficultyFactor)) + keyThreshold := MAX_SIG * (1.0 - difficultyFactor) + + // Using that difficulty factor, we can calculate the key threshold: + // + // MAX_KEY = (2**256 - 1) + // key_threshold = MAX_KEY * (1.0 - 0.52) = + // + // The server must reject PUT requests for new keys that are not less + // than + if binary.BigEndian.Uint64(key) >= uint64(keyThreshold) { + if err != nil || len(key) != 32 { + // the spec doesn't specify the proper return value in this case + http.Error(w, "Key greater than threshold", http.StatusBadRequest) + return + } + } + } var signature []byte if authorizationHeaders, ok := r.Header["Authorization"]; !ok { diff --git a/server/templates/index.html b/server/templates/index.html index eccd332..bb6f3d7 100644 --- a/server/templates/index.html +++ b/server/templates/index.html @@ -4,6 +4,8 @@ .board { border: 1px solid black; margin: 10px; + padding: 10px; + display: inline-flex; }