mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-26 10:02:42 +00:00
lib: enable wasm based check validation
Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
@@ -16,4 +16,4 @@ const StaticPath = "/.within.website/x/cmd/anubis/"
|
|||||||
|
|
||||||
// DefaultDifficulty is the default "difficulty" (number of leading zeroes)
|
// DefaultDifficulty is the default "difficulty" (number of leading zeroes)
|
||||||
// that must be met by the client in order to pass the challenge.
|
// that must be met by the client in order to pass the challenge.
|
||||||
const DefaultDifficulty = 4
|
const DefaultDifficulty uint32 = 4
|
||||||
|
|||||||
+2
-2
@@ -40,7 +40,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
bind = flag.String("bind", ":8923", "network address to bind HTTP to")
|
bind = flag.String("bind", ":8923", "network address to bind HTTP to")
|
||||||
bindNetwork = flag.String("bind-network", "tcp", "network family to bind HTTP to, e.g. unix, tcp")
|
bindNetwork = flag.String("bind-network", "tcp", "network family to bind HTTP to, e.g. unix, tcp")
|
||||||
challengeDifficulty = flag.Int("difficulty", anubis.DefaultDifficulty, "difficulty of the challenge")
|
challengeDifficulty = flag.Int("difficulty", int(anubis.DefaultDifficulty), "difficulty of the challenge")
|
||||||
cookieDomain = flag.String("cookie-domain", "", "if set, the top-level domain that the Anubis cookie will be valid for")
|
cookieDomain = flag.String("cookie-domain", "", "if set, the top-level domain that the Anubis cookie will be valid for")
|
||||||
cookiePartitioned = flag.Bool("cookie-partitioned", false, "if true, sets the partitioned flag on Anubis cookies, enabling CHIPS support")
|
cookiePartitioned = flag.Bool("cookie-partitioned", false, "if true, sets the partitioned flag on Anubis cookies, enabling CHIPS support")
|
||||||
ed25519PrivateKeyHex = flag.String("ed25519-private-key-hex", "", "private key used to sign JWTs, if not set a random one will be assigned")
|
ed25519PrivateKeyHex = flag.String("ed25519-private-key-hex", "", "private key used to sign JWTs, if not set a random one will be assigned")
|
||||||
@@ -194,7 +194,7 @@ func main() {
|
|||||||
log.Fatalf("can't make reverse proxy: %v", err)
|
log.Fatalf("can't make reverse proxy: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
policy, err := libanubis.LoadPoliciesOrDefault(*policyFname, *challengeDifficulty)
|
policy, err := libanubis.LoadPoliciesOrDefault(*policyFname, uint32(*challengeDifficulty))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("can't parse policy file: %v", err)
|
log.Fatalf("can't parse policy file: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-17
@@ -1,19 +1,23 @@
|
|||||||
package lib
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -31,6 +35,7 @@ import (
|
|||||||
"github.com/TecharoHQ/anubis/internal/ogtags"
|
"github.com/TecharoHQ/anubis/internal/ogtags"
|
||||||
"github.com/TecharoHQ/anubis/lib/policy"
|
"github.com/TecharoHQ/anubis/lib/policy"
|
||||||
"github.com/TecharoHQ/anubis/lib/policy/config"
|
"github.com/TecharoHQ/anubis/lib/policy/config"
|
||||||
|
"github.com/TecharoHQ/anubis/wasm"
|
||||||
"github.com/TecharoHQ/anubis/web"
|
"github.com/TecharoHQ/anubis/web"
|
||||||
"github.com/TecharoHQ/anubis/xess"
|
"github.com/TecharoHQ/anubis/xess"
|
||||||
)
|
)
|
||||||
@@ -80,7 +85,7 @@ type Options struct {
|
|||||||
WebmasterEmail string
|
WebmasterEmail string
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadPoliciesOrDefault(fname string, defaultDifficulty int) (*policy.ParsedConfig, error) {
|
func LoadPoliciesOrDefault(fname string, defaultDifficulty uint32) (*policy.ParsedConfig, error) {
|
||||||
var fin io.ReadCloser
|
var fin io.ReadCloser
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -122,6 +127,32 @@ func New(opts Options) (*Server, error) {
|
|||||||
opts: opts,
|
opts: opts,
|
||||||
DNSBLCache: decaymap.New[string, dnsbl.DroneBLResponse](),
|
DNSBLCache: decaymap.New[string, dnsbl.DroneBLResponse](),
|
||||||
OGTags: ogtags.NewOGTagCache(opts.Target, opts.OGPassthrough, opts.OGTimeToLive),
|
OGTags: ogtags.NewOGTagCache(opts.Target, opts.OGPassthrough, opts.OGTimeToLive),
|
||||||
|
validators: map[string]Verifier{
|
||||||
|
"fast": VerifierFunc(BasicSHA256Verify),
|
||||||
|
"slow": VerifierFunc(BasicSHA256Verify),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
finfos, err := fs.ReadDir(web.Static, "static/wasm")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("[unexpected] can't read any webassembly files in the static folder: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, finfo := range finfos {
|
||||||
|
fin, err := web.Static.Open("static/wasm/" + finfo.Name())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("[unexpected] can't read static/wasm/%s: %w", finfo.Name(), err)
|
||||||
|
}
|
||||||
|
defer fin.Close()
|
||||||
|
|
||||||
|
name := strings.TrimSuffix(finfo.Name(), filepath.Ext(finfo.Name()))
|
||||||
|
|
||||||
|
runner, err := wasm.NewRunner(context.Background(), finfo.Name(), fin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't load static/wasm/%s: %w", finfo.Name(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.validators[name] = runner
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
@@ -161,13 +192,15 @@ type Server struct {
|
|||||||
opts Options
|
opts Options
|
||||||
DNSBLCache *decaymap.Impl[string, dnsbl.DroneBLResponse]
|
DNSBLCache *decaymap.Impl[string, dnsbl.DroneBLResponse]
|
||||||
OGTags *ogtags.OGTagCache
|
OGTags *ogtags.OGTagCache
|
||||||
|
|
||||||
|
validators map[string]Verifier
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
s.mux.ServeHTTP(w, r)
|
s.mux.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) challengeFor(r *http.Request, difficulty int) string {
|
func (s *Server) challengeFor(r *http.Request, difficulty uint32) string {
|
||||||
fp := sha256.Sum256(s.priv.Seed())
|
fp := sha256.Sum256(s.priv.Seed())
|
||||||
|
|
||||||
challengeData := fmt.Sprintf(
|
challengeData := fmt.Sprintf(
|
||||||
@@ -404,7 +437,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
|||||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("Internal Server Error: administrator has misconfigured Anubis. Please contact the administrator and ask them to look for the logs around \"passChallenge\".", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
templ.Handler(web.Base("Oh noes!", web.ErrorPage("Internal Server Error: administrator has misconfigured Anubis. Please contact the administrator and ask them to look for the logs around \"passChallenge\".", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lg = lg.With("check_result", cr)
|
lg = lg.With("check_result", cr, "algorithm", rule.Challenge.Algorithm)
|
||||||
|
|
||||||
nonceStr := r.FormValue("nonce")
|
nonceStr := r.FormValue("nonce")
|
||||||
if nonceStr == "" {
|
if nonceStr == "" {
|
||||||
@@ -436,33 +469,52 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
|||||||
response := r.FormValue("response")
|
response := r.FormValue("response")
|
||||||
redir := r.FormValue("redir")
|
redir := r.FormValue("redir")
|
||||||
|
|
||||||
challenge := s.challengeFor(r, rule.Challenge.Difficulty)
|
responseBytes, err := hex.DecodeString(response)
|
||||||
|
if err != nil {
|
||||||
|
s.ClearCookie(w)
|
||||||
|
lg.Debug("response doesn't parse", "err", err)
|
||||||
|
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid response format", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
nonce, err := strconv.Atoi(nonceStr)
|
challenge := s.challengeFor(r, rule.Challenge.Difficulty)
|
||||||
|
challengeBytes, err := hex.DecodeString(challenge)
|
||||||
|
if err != nil {
|
||||||
|
s.ClearCookie(w)
|
||||||
|
lg.Debug("challenge doesn't parse", "err", err)
|
||||||
|
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid internal challenge format", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nonceRaw, err := strconv.ParseUint(nonceStr, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.ClearCookie(w)
|
s.ClearCookie(w)
|
||||||
lg.Debug("nonce doesn't parse", "err", err)
|
lg.Debug("nonce doesn't parse", "err", err)
|
||||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid nonce", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid nonce", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
nonce := uint32(nonceRaw)
|
||||||
|
|
||||||
calcString := fmt.Sprintf("%s%d", challenge, nonce)
|
validator, ok := s.validators[string(rule.Challenge.Algorithm)]
|
||||||
calculated := internal.SHA256sum(calcString)
|
if !ok {
|
||||||
|
|
||||||
if subtle.ConstantTimeCompare([]byte(response), []byte(calculated)) != 1 {
|
|
||||||
s.ClearCookie(w)
|
s.ClearCookie(w)
|
||||||
lg.Debug("hash does not match", "got", response, "want", calculated)
|
lg.Debug("nonce doesn't parse", "err", err)
|
||||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid response", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusForbidden)).ServeHTTP(w, r)
|
templ.Handler(web.Base("Oh noes!", web.ErrorPage(fmt.Sprintf("Internal anubis error has been detected and you cannot proceed. Tried to look up a validator for algorithm %s but wasn't able to find one. Please contact the administrator of this instance of anubis", rule.Challenge.Algorithm), s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||||
failedValidations.Inc()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare the leading zeroes
|
ok, err = validator.Verify(r.Context(), challengeBytes, responseBytes, nonce, rule.Challenge.Difficulty)
|
||||||
if !strings.HasPrefix(response, strings.Repeat("0", rule.Challenge.Difficulty)) {
|
if err != nil {
|
||||||
s.ClearCookie(w)
|
s.ClearCookie(w)
|
||||||
lg.Debug("difficulty check failed", "response", response, "difficulty", rule.Challenge.Difficulty)
|
lg.Debug("verification error", "err", err)
|
||||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid response", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusForbidden)).ServeHTTP(w, r)
|
templ.Handler(web.Base("Oh noes!", web.ErrorPage("Your challenge failed validation. Please go back and try your challenge again", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusBadRequest)).ServeHTTP(w, r)
|
||||||
failedValidations.Inc()
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
s.ClearCookie(w)
|
||||||
|
lg.Debug("response invalid")
|
||||||
|
templ.Handler(web.Base("Oh noes!", web.ErrorPage("Your challenge failed validation. Please go back and try your challenge again", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusBadRequest)).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -197,7 +197,7 @@ func TestCheckDefaultDifficultyMatchesPolicy(t *testing.T) {
|
|||||||
fmt.Fprintln(w, "OK")
|
fmt.Fprintln(w, "OK")
|
||||||
})
|
})
|
||||||
|
|
||||||
for i := 1; i < 10; i++ {
|
for i := uint32(1); i < 10; i++ {
|
||||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||||
anubisPolicy, err := LoadPoliciesOrDefault("", i)
|
anubisPolicy, err := LoadPoliciesOrDefault("", i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ const (
|
|||||||
AlgorithmUnknown Algorithm = ""
|
AlgorithmUnknown Algorithm = ""
|
||||||
AlgorithmFast Algorithm = "fast"
|
AlgorithmFast Algorithm = "fast"
|
||||||
AlgorithmSlow Algorithm = "slow"
|
AlgorithmSlow Algorithm = "slow"
|
||||||
|
AlgorithmArgon2ID Algorithm = "argon2id"
|
||||||
|
AlgorithmSHA256 Algorithm = "sha256"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BotConfig struct {
|
type BotConfig struct {
|
||||||
@@ -101,8 +103,8 @@ func (b BotConfig) Valid() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ChallengeRules struct {
|
type ChallengeRules struct {
|
||||||
Difficulty int `json:"difficulty"`
|
Difficulty uint32 `json:"difficulty"`
|
||||||
ReportAs int `json:"report_as"`
|
ReportAs uint32 `json:"report_as"`
|
||||||
Algorithm Algorithm `json:"algorithm"`
|
Algorithm Algorithm `json:"algorithm"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +126,7 @@ func (cr ChallengeRules) Valid() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch cr.Algorithm {
|
switch cr.Algorithm {
|
||||||
case AlgorithmFast, AlgorithmSlow, AlgorithmUnknown:
|
case AlgorithmFast, AlgorithmSlow, AlgorithmArgon2ID, AlgorithmSHA256, AlgorithmUnknown:
|
||||||
// do nothing, it's all good
|
// do nothing, it's all good
|
||||||
default:
|
default:
|
||||||
errs = append(errs, fmt.Errorf("%w: %q", ErrChallengeRuleHasWrongAlgorithm, cr.Algorithm))
|
errs = append(errs, fmt.Errorf("%w: %q", ErrChallengeRuleHasWrongAlgorithm, cr.Algorithm))
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type ParsedConfig struct {
|
|||||||
|
|
||||||
Bots []Bot
|
Bots []Bot
|
||||||
DNSBL bool
|
DNSBL bool
|
||||||
DefaultDifficulty int
|
DefaultDifficulty uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewParsedConfig(orig config.Config) *ParsedConfig {
|
func NewParsedConfig(orig config.Config) *ParsedConfig {
|
||||||
@@ -36,7 +36,7 @@ func NewParsedConfig(orig config.Config) *ParsedConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseConfig(fin io.Reader, fname string, defaultDifficulty int) (*ParsedConfig, error) {
|
func ParseConfig(fin io.Reader, fname string, defaultDifficulty uint32) (*ParsedConfig, error) {
|
||||||
var c config.Config
|
var c config.Config
|
||||||
if err := json.NewDecoder(fin).Decode(&c); err != nil {
|
if err := json.NewDecoder(fin).Decode(&c); err != nil {
|
||||||
return nil, fmt.Errorf("can't parse policy config JSON %s: %w", fname, err)
|
return nil, fmt.Errorf("can't parse policy config JSON %s: %w", fname, err)
|
||||||
@@ -99,12 +99,12 @@ func ParseConfig(fin io.Reader, fname string, defaultDifficulty int) (*ParsedCon
|
|||||||
parsedBot.Challenge = &config.ChallengeRules{
|
parsedBot.Challenge = &config.ChallengeRules{
|
||||||
Difficulty: defaultDifficulty,
|
Difficulty: defaultDifficulty,
|
||||||
ReportAs: defaultDifficulty,
|
ReportAs: defaultDifficulty,
|
||||||
Algorithm: config.AlgorithmFast,
|
Algorithm: config.AlgorithmArgon2ID,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parsedBot.Challenge = b.Challenge
|
parsedBot.Challenge = b.Challenge
|
||||||
if parsedBot.Challenge.Algorithm == config.AlgorithmUnknown {
|
if parsedBot.Challenge.Algorithm == config.AlgorithmUnknown {
|
||||||
parsedBot.Challenge.Algorithm = config.AlgorithmFast
|
parsedBot.Challenge.Algorithm = config.AlgorithmArgon2ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "@xeserv/xess",
|
"name": "@techaro/anubis",
|
||||||
"version": "1.0.0-see-VERSION-file",
|
"version": "1.0.0-see-VERSION-file",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@xeserv/xess",
|
"name": "@techaro/anubis",
|
||||||
"version": "1.0.0-see-VERSION-file",
|
"version": "1.0.0-see-VERSION-file",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import { u } from "../xeact.mjs";
|
||||||
|
|
||||||
|
export default function process(
|
||||||
|
data,
|
||||||
|
difficulty = 16,
|
||||||
|
signal = null,
|
||||||
|
pc = null,
|
||||||
|
threads = (navigator.hardwareConcurrency || 1),
|
||||||
|
) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
let webWorkerURL = URL.createObjectURL(new Blob([
|
||||||
|
'(', processTask(), ')()'
|
||||||
|
], { type: 'application/javascript' }));
|
||||||
|
|
||||||
|
const module = await fetch(u("/.within.website/x/cmd/anubis/static/wasm/argon2id.wasm"))
|
||||||
|
.then(resp => WebAssembly.compileStreaming(resp));
|
||||||
|
|
||||||
|
const workers = [];
|
||||||
|
const terminate = () => {
|
||||||
|
workers.forEach((w) => w.terminate());
|
||||||
|
if (signal != null) {
|
||||||
|
// clean up listener to avoid memory leak
|
||||||
|
signal.removeEventListener("abort", terminate);
|
||||||
|
if (signal.aborted) {
|
||||||
|
console.log("PoW aborted");
|
||||||
|
reject(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (signal != null) {
|
||||||
|
signal.addEventListener("abort", terminate, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < threads; i++) {
|
||||||
|
let worker = new Worker(webWorkerURL);
|
||||||
|
|
||||||
|
worker.onmessage = (event) => {
|
||||||
|
if (typeof event.data === "number") {
|
||||||
|
pc?.(event.data);
|
||||||
|
} else {
|
||||||
|
terminate();
|
||||||
|
resolve(event.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.onerror = (event) => {
|
||||||
|
terminate();
|
||||||
|
reject(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.postMessage({
|
||||||
|
data,
|
||||||
|
difficulty,
|
||||||
|
nonce: i,
|
||||||
|
threads,
|
||||||
|
module,
|
||||||
|
});
|
||||||
|
|
||||||
|
workers.push(worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
URL.revokeObjectURL(webWorkerURL);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function processTask() {
|
||||||
|
return function () {
|
||||||
|
addEventListener('message', async (event) => {
|
||||||
|
const importObject = {
|
||||||
|
anubis: {
|
||||||
|
anubis_update_nonce: (nonce) => postMessage(nonce),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const instance = await WebAssembly.instantiate(event.data.module, importObject);
|
||||||
|
|
||||||
|
// Get exports
|
||||||
|
const {
|
||||||
|
anubis_work,
|
||||||
|
data_ptr,
|
||||||
|
result_hash_ptr,
|
||||||
|
result_hash_size,
|
||||||
|
set_data_length,
|
||||||
|
memory
|
||||||
|
} = instance.exports;
|
||||||
|
|
||||||
|
function uint8ArrayToHex(arr) {
|
||||||
|
return Array.from(arr)
|
||||||
|
.map((c) => c.toString(16).padStart(2, "0"))
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function hexToUint8Array(hexString) {
|
||||||
|
// Remove whitespace and optional '0x' prefix
|
||||||
|
hexString = hexString.replace(/\s+/g, '').replace(/^0x/, '');
|
||||||
|
|
||||||
|
// Check for valid length
|
||||||
|
if (hexString.length % 2 !== 0) {
|
||||||
|
throw new Error('Invalid hex string length');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for valid characters
|
||||||
|
if (!/^[0-9a-fA-F]+$/.test(hexString)) {
|
||||||
|
throw new Error('Invalid hex characters');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to Uint8Array
|
||||||
|
const byteArray = new Uint8Array(hexString.length / 2);
|
||||||
|
for (let i = 0; i < byteArray.length; i++) {
|
||||||
|
const byteValue = parseInt(hexString.substr(i * 2, 2), 16);
|
||||||
|
byteArray[i] = byteValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write data to buffer
|
||||||
|
function writeToBuffer(data) {
|
||||||
|
if (data.length > 1024) throw new Error("Data exceeds buffer size");
|
||||||
|
|
||||||
|
// Get pointer and create view
|
||||||
|
const offset = data_ptr();
|
||||||
|
const buffer = new Uint8Array(memory.buffer, offset, data.length);
|
||||||
|
|
||||||
|
// Copy data
|
||||||
|
buffer.set(data);
|
||||||
|
|
||||||
|
// Set data length
|
||||||
|
set_data_length(data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readFromChallenge() {
|
||||||
|
const offset = result_hash_ptr();
|
||||||
|
const buffer = new Uint8Array(memory.buffer, offset, result_hash_size());
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = event.data.data;
|
||||||
|
let difficulty = event.data.difficulty;
|
||||||
|
let nonce = event.data.nonce;
|
||||||
|
let interand = event.data.threads;
|
||||||
|
|
||||||
|
writeToBuffer(hexToUint8Array(data));
|
||||||
|
|
||||||
|
nonce = anubis_work(difficulty, nonce, interand);
|
||||||
|
const challenge = readFromChallenge();
|
||||||
|
|
||||||
|
data = uint8ArrayToHex(challenge);
|
||||||
|
|
||||||
|
postMessage({
|
||||||
|
hash: data,
|
||||||
|
difficulty,
|
||||||
|
nonce,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}.toString();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,6 @@ function processTask() {
|
|||||||
|
|
||||||
let data = event.data.data;
|
let data = event.data.data;
|
||||||
let difficulty = event.data.difficulty;
|
let difficulty = event.data.difficulty;
|
||||||
let hash;
|
|
||||||
let nonce = event.data.nonce;
|
let nonce = event.data.nonce;
|
||||||
let interand = event.data.threads;
|
let interand = event.data.threads;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import argon2id from "./algos/argon2id.mjs";
|
||||||
import fast from "./algos/fast.mjs";
|
import fast from "./algos/fast.mjs";
|
||||||
import slow from "./algos/slow.mjs";
|
import slow from "./algos/slow.mjs";
|
||||||
import sha256 from "./algos/sha256.mjs";
|
import sha256 from "./algos/sha256.mjs";
|
||||||
@@ -5,6 +6,7 @@ import { testVideo } from "./video.mjs";
|
|||||||
import { u } from "./xeact.mjs";
|
import { u } from "./xeact.mjs";
|
||||||
|
|
||||||
const algorithms = {
|
const algorithms = {
|
||||||
|
"argon2id": argon2id,
|
||||||
"fast": fast,
|
"fast": fast,
|
||||||
"slow": slow,
|
"slow": slow,
|
||||||
"sha256": sha256,
|
"sha256": sha256,
|
||||||
|
|||||||
Reference in New Issue
Block a user