Minimize wasm runtime external dependencies, do not use JSON on verify-challenge output

This commit is contained in:
WeebDataHoarder
2025-04-01 05:29:00 +02:00
parent 8d45668d05
commit bfcb0ccada
10 changed files with 470 additions and 91 deletions

View File

@@ -4,20 +4,19 @@ import (
"crypto/sha256"
"crypto/subtle"
"encoding/binary"
"encoding/hex"
"encoding/json"
"git.gammaspectra.live/git/go-away/challenge"
"git.gammaspectra.live/git/go-away/challenge/inline"
"math/bits"
"strconv"
"strings"
)
//go:generate tinygo build -target wasip1 -buildmode=c-shared -scheduler=none -gc=leaking -o runtime.wasm runtime.go
//go:generate tinygo build -target wasip1 -buildmode=c-shared -opt=2 -scheduler=none -gc=leaking -no-debug -o runtime.wasm runtime.go
func main() {
}
func getChallenge(key []byte, params map[string]string) ([]byte, uint64) {
difficulty := uint64(5)
difficulty := uint64(20)
var err error
if diffStr, ok := params["difficulty"]; ok {
difficulty, err = strconv.ParseUint(diffStr, 10, 64)
@@ -34,22 +33,32 @@ func getChallenge(key []byte, params map[string]string) ([]byte, uint64) {
//go:wasmexport MakeChallenge
func MakeChallenge(in challenge.Allocation) (out challenge.Allocation) {
return challenge.MakeChallengeDecode(func(in challenge.MakeChallengeInput, out *challenge.MakeChallengeOutput) {
type Result struct {
Challenge string `json:"challenge"`
Difficulty uint64 `json:"difficulty"`
c, difficulty := getChallenge(in.Key, in.Parameters)
// create target
target := make([]byte, len(c))
nBits := difficulty
for i := 0; i < len(target); i++ {
var v uint8
for j := 0; j < 8; j++ {
v <<= 1
if nBits == 0 {
v |= 1
} else {
nBits--
}
}
target[i] = v
}
challenge, difficulty := getChallenge(in.Key, in.Parameters)
dst := make([]byte, inline.EncodedLen(len(c)))
dst = dst[:inline.Encode(dst, c)]
data, err := json.Marshal(Result{
Challenge: hex.EncodeToString(challenge),
Difficulty: difficulty,
})
if err != nil {
panic(err)
}
out.Data = data
out.Headers.Set("Content-Type", "text/javascript; charset=utf-8")
targetDst := make([]byte, inline.EncodedLen(len(target)))
targetDst = targetDst[:inline.Encode(targetDst, target)]
out.Data = []byte("{\"challenge\": \"" + string(dst) + "\", \"target\": \"" + string(targetDst) + "\", \"difficulty\": " + strconv.FormatUint(difficulty, 10) + "}")
out.Headers.Set("Content-Type", "application/json; charset=utf-8")
}, in)
}
@@ -58,32 +67,30 @@ func VerifyChallenge(in challenge.Allocation) (out challenge.VerifyChallengeOutp
return challenge.VerifyChallengeDecode(func(in challenge.VerifyChallengeInput) challenge.VerifyChallengeOutput {
c, difficulty := getChallenge(in.Key, in.Parameters)
type Result struct {
Hash string `json:"hash"`
Nonce uint64 `json:"nonce"`
}
var result Result
err := json.Unmarshal(in.Result, &result)
result := make([]byte, inline.DecodedLen(len(in.Result)))
n, err := inline.Decode(result, in.Result)
if err != nil {
panic(err)
}
result = result[:n]
if !strings.HasPrefix(result.Hash, strings.Repeat("0", int(difficulty))) {
// verify we used same challenge
if subtle.ConstantTimeCompare(result[:len(result)-8], c) != 1 {
return challenge.VerifyChallengeOutputFailed
}
resultBinary, err := hex.DecodeString(result.Hash)
if err != nil {
panic(err)
hash := sha256.Sum256(result)
var leadingZeroesCount int
for i := 0; i < len(hash); i++ {
leadingZeroes := bits.LeadingZeros8(hash[i])
leadingZeroesCount += leadingZeroes
if leadingZeroes < 8 {
break
}
}
buf := make([]byte, 0, len(c)+8)
buf = append(buf, c[:]...)
buf = binary.LittleEndian.AppendUint64(buf, result.Nonce)
calculated := sha256.Sum256(buf)
if subtle.ConstantTimeCompare(resultBinary, calculated[:]) != 1 {
if leadingZeroesCount < int(difficulty) {
return challenge.VerifyChallengeOutputFailed
}