diff --git a/README.md b/README.md new file mode 100644 index 0000000..b00f8eb --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# Compiling +To build the web interface from source, you will need to compile the Go code into a WebAssembly file: +``` +GOOS=js GOARCH=wasm go build -ldflags="-s -w" index.go +``` +This will create a binary file. Compress it with [LZMA](https://github.com/LZMA-JS/LZMA-JS), encode it in Base64, and paste the final result to [L198](https://github.com/Picocrypt/Web/blob/main/index.html#L198). + +You'll also need to update [`wasm_exec.js`](https://cdn.jsdelivr.net/gh/golang/go@go1.21.5/misc/wasm/wasm_exec.min.js) (replace the Go version accordingly) on [L197](https://github.com/Picocrypt/Web/blob/main/index.html#L197) to glue everything together. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9464a07 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module Picocrypt + +go 1.17 + +require ( + github.com/HACKERALERT/crypto v0.0.0-20220905152506-aa0dd62d8f67 + github.com/HACKERALERT/infectious v0.0.0-20220905152109-2c37b99f37ff +) + +require github.com/HACKERALERT/sys v0.0.0-20220905150735-46e319fb60c9 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..241eb57 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/HACKERALERT/crypto v0.0.0-20220905152506-aa0dd62d8f67 h1:4WfPIopYjvBjyDg0IET7mEj32kkihLmvFgwCOmldqK8= +github.com/HACKERALERT/crypto v0.0.0-20220905152506-aa0dd62d8f67/go.mod h1:qiHCxMDsCxX4QhXd3kDYWiNOR/DZQZ7nYO/f2OgWst0= +github.com/HACKERALERT/infectious v0.0.0-20220905152109-2c37b99f37ff h1:ertDhqhixxQJJPJIpn4wmSVs2fQ3tlSDNuiZ7jHCvEs= +github.com/HACKERALERT/infectious v0.0.0-20220905152109-2c37b99f37ff/go.mod h1:GwBVHbiXRUUciGfKWwm4GCL8FvMZBLHrQq23UXW/CU8= +github.com/HACKERALERT/sys v0.0.0-20220905150735-46e319fb60c9 h1:raqLJhvqDGk9L4dnJnO0tTV5Lyba2jhQcIHEle+o1dM= +github.com/HACKERALERT/sys v0.0.0-20220905150735-46e319fb60c9/go.mod h1:I4esFWbCYc37CXVb+3qJHJiU43NGkLf66kNV/NDnXcU= diff --git a/index.go b/index.go new file mode 100644 index 0000000..06679e4 --- /dev/null +++ b/index.go @@ -0,0 +1,199 @@ +package main + +/* + +Picocrypt v1.33 (WebAssembly Version) +Copyright (c) Evan Su +Released under a GNU GPL v3 License +https://github.com/Picocrypt/Web + +~ In cryptography we trust ~ + +*/ + +import ( + "bytes" + "crypto/rand" + "strconv" + "strings" + "syscall/js" + + "github.com/HACKERALERT/crypto/argon2" + "github.com/HACKERALERT/crypto/blake2b" + "github.com/HACKERALERT/crypto/chacha20" + "github.com/HACKERALERT/crypto/hkdf" + "github.com/HACKERALERT/crypto/sha3" + "github.com/HACKERALERT/infectious" +) + +var MiB = 1 << 20 +var GiB = 1 << 30 +var rs5, _ = infectious.NewFEC(5, 15) +var rs16, _ = infectious.NewFEC(16, 48) +var rs24, _ = infectious.NewFEC(24, 72) +var rs32, _ = infectious.NewFEC(32, 96) +var rs64, _ = infectious.NewFEC(64, 192) +var password string + +func work(din []byte, mode string) []byte { + var salt []byte + var hkdfSalt []byte + var nonce []byte + var keyHash []byte + var keyHashRef []byte + var authTag []byte + var dout []byte + + if mode == "encrypt" { + salt = make([]byte, 16) + hkdfSalt = make([]byte, 32) + nonce = make([]byte, 24) + rand.Read(salt) + rand.Read(hkdfSalt) + rand.Read(nonce) + dout = append(dout, rsEncode(rs5, []byte("v1.33"))...) + dout = append(dout, rsEncode(rs5, []byte("00000"))...) + dout = append(dout, rsEncode(rs5, make([]byte, 5))...) + dout = append(dout, rsEncode(rs16, salt)...) + dout = append(dout, rsEncode(rs32, hkdfSalt)...) + dout = append(dout, rsEncode(rs16, make([]byte, 16))...) + dout = append(dout, rsEncode(rs24, nonce)...) + dout = append(dout, make([]byte, 480)...) + } else { + errs, tmp := make([]error, 7), make([]byte, 15) + tmp, din = din[15:30], din[30:] + tmp, errs[0] = rsDecode(rs5, tmp) + c, _ := strconv.Atoi(string(tmp)) + tmp, din = din[c*3:c*3+15], din[c*3+15:] + tmp, errs[1] = rsDecode(rs5, tmp) + if tmp[0]+tmp[1]+tmp[3] > 0 { + return []byte{1} + } + salt, din = din[:48], din[48:] + salt, errs[2] = rsDecode(rs16, salt) + hkdfSalt, din = din[:96], din[96:] + hkdfSalt, errs[3] = rsDecode(rs32, hkdfSalt) + nonce, din = din[48:120], din[120:] + nonce, errs[4] = rsDecode(rs24, nonce) + keyHashRef, din = din[:192], din[192:] + keyHashRef, errs[5] = rsDecode(rs64, keyHashRef) + authTag, din = din[96:288], din[288:] + authTag, errs[6] = rsDecode(rs64, authTag) + for _, err := range errs { + if err != nil { + return []byte{2} + } + } + } + + key := argon2.IDKey([]byte(password), salt, 4, 1<<20, 4, 32) + tmp := sha3.New512() + tmp.Write(key) + keyHash = tmp.Sum(nil) + if mode == "decrypt" && !bytes.Equal(keyHash, keyHashRef) { + return []byte{3} + } + + counter := 0 + chacha, _ := chacha20.NewUnauthenticatedCipher(key, nonce) + subkey := make([]byte, 32) + hkdf := hkdf.New(sha3.New256, key, hkdfSalt, nil) + hkdf.Read(subkey) + mac, _ := blake2b.New512(subkey) + hkdf.Read(make([]byte, 32)) + + for { + var src []byte + if len(din) == 0 { + break + } else if len(din) < MiB { + src, din = din, nil + } else { + src, din = din[:MiB], din[MiB:] + } + dst := make([]byte, len(src)) + + if mode == "encrypt" { + chacha.XORKeyStream(dst, src) + mac.Write(dst) + } else { + mac.Write(src) + chacha.XORKeyStream(dst, src) + } + dout = append(dout, dst...) + + counter += MiB + if counter >= 60*GiB { + nonce = make([]byte, 24) + hkdf.Read(nonce) + chacha, _ = chacha20.NewUnauthenticatedCipher(key, nonce) + hkdf.Read(make([]byte, 16)) + counter = 0 + } + } + + if mode == "encrypt" { + buff := rsEncode(rs64, keyHash) + buff = append(buff, rsEncode(rs32, make([]byte, 32))...) + buff = append(buff, rsEncode(rs64, mac.Sum(nil))...) + for i, v := range buff { + dout[309+i] = v + } + } else { + if !bytes.Equal(mac.Sum(nil), authTag) { + return []byte{4} + } + } + + return append([]byte{0}, dout...) +} + +func rsEncode(rs *infectious.FEC, data []byte) []byte { + res := make([]byte, rs.Total()) + rs.Encode(data, func(s infectious.Share) { + res[s.Number] = s.Data[0] + }) + return res +} + +func rsDecode(rs *infectious.FEC, data []byte) ([]byte, error) { + tmp := make([]infectious.Share, rs.Total()) + for i := 0; i < rs.Total(); i++ { + tmp[i].Number = i + tmp[i].Data = []byte{data[i]} + } + res, err := rs.Decode(nil, tmp) + if err != nil { + return data[:rs.Total()/3], err + } + return res, nil +} + +func main() { + quit := make(chan struct{}, 0) + document := js.Global().Get("document") + input := document.Call("getElementById", "fin") + button := document.Call("getElementById", "work") + callback := js.FuncOf(func(v js.Value, x []js.Value) any { + var dout []byte + data := js.Global().Get("Uint8Array").New(x[0]) + din := make([]byte, data.Get("length").Int()) + js.CopyBytesToGo(din, data) + password = document.Call("getElementById", "password").Get("value").String() + filename := input.Get("files").Call("item", 0).Get("name").String() + if strings.HasSuffix(filename, ".pcv") { + dout = work(din, "decrypt") + } else { + dout = work(din, "encrypt") + } + arr := js.Global().Get("Uint8Array").New(len(dout)) + js.CopyBytesToJS(arr, dout) + js.Global().Call("download", arr) + return nil + }) + button.Set("onclick", js.FuncOf(func(v js.Value, x []js.Value) any { + input.Get("files").Call("item", 0).Call("arrayBuffer").Call("then", callback) + return nil + })) + <-quit +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..565bf60 --- /dev/null +++ b/index.html @@ -0,0 +1,202 @@ + + + + Picocrypt + + + + + + + + +
+

Click here to select a file.

+
+
+ + +

Ready.

+
+ + + \ No newline at end of file