Compare commits

...

12 Commits
main ... web

Author SHA1 Message Date
Zack Scholl 166b1cf6cc update 2019-11-18 07:09:39 -08:00
Zack Scholl 0f72e35268 communication seems to work 2019-11-17 16:43:30 -08:00
Zack Scholl 0b302b3f7c initiate pake 2019-11-17 16:23:31 -08:00
Zack Scholl 0753246a4b add web relay 2019-11-17 15:51:10 -08:00
Zack Scholl 59bd382bf5 add basics 2019-11-17 15:50:57 -08:00
Zack Scholl 1ae7a2ff97 allow disabling the multiplexing 2019-11-17 15:32:47 -08:00
Zack Scholl 2cf9480302 use siec and use pake v2 2019-11-17 15:21:16 -08:00
Zack Scholl 7b68bcaea2 make encryption functional 2019-11-17 15:17:06 -08:00
Zack 382ef3157a
Merge pull request #142 from rain-1/master
Update README.md
2019-11-17 13:24:39 -08:00
rain1 0f3c772ca8
Update README.md
remove link to release
2019-07-17 18:29:13 +01:00
rain1 efd8c36d2e
Update README.md 2019-07-17 17:12:21 +01:00
rain1 05a4e90208
Update README.md
remove curl | bash recommendation
2019-07-17 15:38:25 +01:00
14 changed files with 1139 additions and 130 deletions

View File

@ -12,9 +12,6 @@ Status"></a>
<a href="https://saythanks.io/to/schollz"><img src="https://img.shields.io/badge/Say%20Thanks-!-brightgreen.svg?style=flat-square" alt="Say thanks"></a>
</p>
<p align="center"><code>curl https://getcroc.schollz.com | bash</code></p>
`croc` is a tool that allows any two computers to simply and securely transfer files and folders. AFAIK, *croc* is the only CLI file-transfer tool does **all** of the following:
- allows **any two computers** to transfer data (using a relay)

7
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/denisbrodbeck/machineid v1.0.1
github.com/fatih/color v1.7.0 // indirect
github.com/gorilla/websocket v1.4.1
github.com/kalafut/imohash v1.0.0
github.com/kr/pretty v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
@ -15,17 +16,17 @@ require (
github.com/pkg/errors v0.8.1
github.com/schollz/logger v1.0.1
github.com/schollz/mnemonicode v1.0.1
github.com/schollz/pake v1.1.1
github.com/schollz/pake/v2 v2.0.2
github.com/schollz/peerdiscovery v1.4.1
github.com/schollz/progressbar/v2 v2.14.2
github.com/schollz/spinner v0.0.0-20180925172146-6bbc5f7804f9
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/testify v1.4.0
github.com/tscholl2/siec v0.0.0-20191103131401-2e0c53a9e212 // indirect
github.com/tscholl2/siec v0.0.0-20191103131401-2e0c53a9e212
github.com/urfave/cli v1.22.1
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f
golang.org/x/net v0.0.0-20191116160921-f9c825593386 // indirect
golang.org/x/sys v0.0.0-20191115151921-52ab43148777 // indirect
golang.org/x/sys v0.0.0-20191118133127-cf1e2d577169 // indirect
golang.org/x/text v0.3.2 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)

11
go.sum
View File

@ -16,6 +16,8 @@ github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMS
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/kalafut/imohash v1.0.0 h1:LgCJ+p/BwM2HKpOxFopkeddpzVCfm15EtXMroXD1SYE=
github.com/kalafut/imohash v1.0.0/go.mod h1:c3RHT80ZAp5C/aYgQI92ZlrOymqkZnRDprU87kg75HI=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@ -41,8 +43,8 @@ github.com/schollz/logger v1.0.1 h1:BuBAU+euqphM0Ny9qFVScl4RSxatis4nCHIkOxO2cUU=
github.com/schollz/logger v1.0.1/go.mod h1:P6F4/dGMGcx8wh+kG1zrNEd4vnNpEBY/mwEMd/vn6AM=
github.com/schollz/mnemonicode v1.0.1 h1:LiH5hwADZwjwnfXsaD4xgnMyTAtaKHN+e5AyjRU6WSU=
github.com/schollz/mnemonicode v1.0.1/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY=
github.com/schollz/pake v1.1.1 h1:QKeojDWzdAdtRC4m89b6HAxw/8gjqrVu7r4SAOxOFg8=
github.com/schollz/pake v1.1.1/go.mod h1:aWMxQ1jwqZRwk3StflHcdyzPR+CyW5W7+WIZD6Y3dEY=
github.com/schollz/pake/v2 v2.0.2 h1:p9y4Gocc5PWueyhhR7OH+Gwpu2xkP5BM9Pepl9krVfo=
github.com/schollz/pake/v2 v2.0.2/go.mod h1:3uXB571UYJ8Eqh2EEohXe/aO32QID+Varb4GeYA//yw=
github.com/schollz/peerdiscovery v1.4.1 h1:xtZ/D8/4eq9O6UEhRupZZiJm4BA8+u1IVUgeHo5VPm4=
github.com/schollz/peerdiscovery v1.4.1/go.mod h1:WDdk0/JVyVHVIA/bmhzTkUg32dhJ20O4tExNqV1u6sk=
github.com/schollz/progressbar/v2 v2.13.2 h1:3L9bP5KQOGEnFP8P5V8dz+U0yo5I29iY5Oa9s9EAwn0=
@ -69,8 +71,7 @@ github.com/tscholl2/siec v0.0.0-20191103131401-2e0c53a9e212/go.mod h1:KL9+ubr1JZ
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f h1:kz4KIr+xcPUsI3VMoqWfPMvtnJ6MGfiVwsWSVzphMO4=
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
@ -86,6 +87,8 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777 h1:wejkGHRTr38uaKRqECZlsCsJ1/TGxIyFbH32x5zUdu4=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191118133127-cf1e2d577169 h1:LPLFLulk2vyM7yI3CwNW64O6e8AxBmr9opfv14yI7HI=
golang.org/x/sys v0.0.0-20191118133127-cf1e2d577169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=

1
html/build.ps1 Normal file
View File

@ -0,0 +1 @@
$env:GOOS="js"; $env:GOARCH="wasm"; go build -v -o main.wasm

68
html/index.html Normal file
View File

@ -0,0 +1,68 @@
<html>
<head>
</html>
<body>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
</script>
<script>
let log = msg => {
console.log(msg);
}
// websockets
var socket;
var checkErr = e => {
if (typeof e === 'error') {
throw err;
}
}
var croc = {
"key": "",
"pake": "",
};
var sharedPassphrase = "pass123"
console.log(sharedPassphrase)
const socketMessageListener = (event) => {
console.log(event);
if (event.data == "initiated") {
socket.send(JSON.stringify({ "m": "room", "ps": sharedPassphrase }))
} else if (event.data == "initpake") {
croc.pake = pakeInit(sharedPassphrase, "0");
socket.send(
messageEncode(croc.key, JSON.stringify({ "t": "pake", "b": croc.pake }))
)
} else {
msgJSON = messageDecode(croc.key, event.data);
console.log(msgJSON);
}
};
const socketOpenListener = (event) => {
log('connected to websockets');
socket.send("receive");
}
const socketCloseListener = (event) => {
if (socket) {
log('Disconnected.');
}
var ws_url = window.origin.replace("http", "ws") + '/ws';
log(`connecting to '${ws_url}'`)
socket = new WebSocket(ws_url);
socket.addEventListener('open', socketOpenListener);
socket.addEventListener('message', socketMessageListener);
socket.addEventListener('close', socketCloseListener);
};
socketCloseListener();
</script>
</body>
</html>

270
html/main.go Normal file
View File

@ -0,0 +1,270 @@
package main
//go:generate cp /usr/local/go/misc/wasm/wasm_exec.js .
// compile with
// GOOS=js GOARCH=wasm go build -o main.wasm
// to run
//
// bob = pakeInit("pass1","0");
// jane = pakeInit("pass1","1");
// jane = pakeUpdate(jane,pakePublic(bob));
// bob = pakeUpdate(bob,pakePublic(jane));
// jane = pakeUpdate(jane,pakePublic(bob));
// keyAndSalt = JSON.parse(pakeSessionKey(bob,""))
// console.log(pakeSessionKey(jane,keyAndSalt.Salt))
import (
"crypto/elliptic"
"encoding/base64"
"encoding/json"
"fmt"
"syscall/js"
"time"
"github.com/pkg/errors"
"github.com/schollz/croc/v6/src/compress"
"github.com/schollz/croc/v6/src/croc"
"github.com/schollz/croc/v6/src/crypt"
"github.com/schollz/croc/v6/src/message"
log "github.com/schollz/logger"
"github.com/schollz/pake/v2"
)
func marshal(a interface{}) string {
b, err := json.Marshal(a)
if err != nil {
log.Errorf("could not marshal: %+v, because '%s'", a, err.Error())
}
return base64.StdEncoding.EncodeToString(b)
}
// crocInit(weakPassphrase,issender)
func crocInit(this js.Value, inputs []js.Value) interface{} {
if len(inputs) != 2 {
return js.Global().Get("Error").New("need weakPassphrase,issender")
}
c := new(croc.Client)
c.FilesHasFinished = make(map[int]struct{})
c.Options = croc.Options{
IsSender: inputs[1].String() == "1",
SharedSecret: inputs[0].String(),
RelayAddress: "localhost:9009",
Debug: true,
}
return marshal(c)
}
// messageEncode(key, Message)
// returns base64 encoded, encrypts if key != nil
func messageEncode(this js.Value, inputs []js.Value) interface{} {
// initialize sender P ("1" indicates sender)
if len(inputs) != 2 {
return js.Global().Get("Error").New("need weakPassphrase, role")
}
var m message.Message
err := json.Unmarshal([]byte(inputs[1].String()), &m)
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
b, err := json.Marshal(m)
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
b = compress.Compress(b)
if inputs[0].String() != "" {
var key []byte
key, err = base64.StdEncoding.DecodeString(inputs[0].String())
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
b, err = crypt.Encrypt(b, key)
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
}
return base64.StdEncoding.EncodeToString(b)
}
// messageDecode(key, encodedMessage)
// returns base64 encoded, encrypts if key != nil
func messageDecode(this js.Value, inputs []js.Value) interface{} {
// initialize sender P ("1" indicates sender)
if len(inputs) != 2 {
return js.Global().Get("Error").New("need key, encodedmessage")
}
var key []byte
key = nil
var err error
if inputs[0].String() != "" {
key, err = base64.StdEncoding.DecodeString(inputs[0].String())
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
}
b, err := base64.StdEncoding.DecodeString(inputs[1].String())
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
m, err := message.Decode(key, b)
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
bJSON, err := json.Marshal(m)
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
return string(bJSON)
}
// pakeInit(weakPassphrase, role)
// returns: pakeBytes
func pakeInit(this js.Value, inputs []js.Value) interface{} {
// initialize sender P ("0" indicates sender)
if len(inputs) != 2 {
return js.Global().Get("Error").New("need weakPassphrase, role")
}
role := 0
if inputs[1].String() == "1" {
role = 1
}
P, err := pake.Init([]byte(inputs[0].String()), role, elliptic.P521(), 1*time.Microsecond)
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
bJSON, err := json.Marshal(P)
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
return base64.StdEncoding.EncodeToString(bJSON)
}
// pakeUpdate(pakeBytes,otherPublicPakeBytes)
func pakeUpdate(this js.Value, inputs []js.Value) interface{} {
if len(inputs) != 2 {
return js.Global().Get("Error").New("need two input")
}
var P *pake.Pake
b, err := base64.StdEncoding.DecodeString(inputs[0].String())
if err != nil {
log.Errorf("problem with %s: %s", inputs[0].String(), err)
return js.Global().Get("Error").New(err.Error())
}
err = json.Unmarshal(b, &P)
P.SetCurve(elliptic.P521())
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
qbytes, err := base64.StdEncoding.DecodeString(inputs[1].String())
if err != nil {
log.Errorf("problem with %s: %s", inputs[1].String(), err)
return js.Global().Get("Error").New(err.Error())
}
err = P.Update(qbytes)
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
bJSON, err := json.Marshal(P)
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
return base64.StdEncoding.EncodeToString(bJSON)
}
// pakePublic(pakeBytes)
func pakePublic(this js.Value, inputs []js.Value) interface{} {
var P *pake.Pake
b, err := base64.StdEncoding.DecodeString(inputs[0].String())
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
err = json.Unmarshal(b, &P)
if err != nil {
log.Error(err)
return js.Global().Get("Error").New(err.Error())
}
P.SetCurve(elliptic.P521())
return base64.StdEncoding.EncodeToString(P.Bytes())
}
// pakeSessionKey(pakeBytes,salt)
func pakeSessionKey(this js.Value, inputs []js.Value) interface{} {
if len(inputs) != 2 {
return js.Global().Get("Error").New("need two input")
}
var P *pake.Pake
b, err := base64.StdEncoding.DecodeString(inputs[0].String())
if err != nil {
err = errors.Wrap(err, "could not decode pakeBytes")
return js.Global().Get("Error").New(err.Error())
}
err = json.Unmarshal(b, &P)
P.SetCurve(elliptic.P521())
if err != nil {
return js.Global().Get("Error").New(err.Error())
}
key, err := P.SessionKey()
if err != nil {
return js.Global().Get("Error").New(err.Error())
}
type KeyAndSalt struct {
Key string
Salt string
}
var kas KeyAndSalt
var salt []byte
salt = nil
if len(inputs[1].String()) > 0 {
b, errb := base64.StdEncoding.DecodeString(inputs[1].String())
if errb != nil {
return js.Global().Get("Error").New(errb.Error())
}
salt = b
}
cryptKey, cryptSalt, err := crypt.New(key, salt)
kas.Key = base64.StdEncoding.EncodeToString(cryptKey)
kas.Salt = base64.StdEncoding.EncodeToString(cryptSalt)
b, _ = json.Marshal(kas)
log.Debugf("key: %x", cryptKey)
log.Debugf("salt: %x", cryptSalt)
return string(b)
}
func main() {
c := make(chan bool)
// fmt.Println("starting")
js.Global().Set("crocInit", js.FuncOf(crocInit))
// js.Global().Set("pakeInit", js.FuncOf(pakeInit))
// js.Global().Set("pakePublic", js.FuncOf(pakePublic))
// js.Global().Set("pakeUpdate", js.FuncOf(pakeUpdate))
// js.Global().Set("pakeSessionKey", js.FuncOf(pakeSessionKey))
// js.Global().Set("messageEncode", js.FuncOf(messageEncode))
// js.Global().Set("messageDecode", js.FuncOf(messageDecode))
fmt.Println("Initiated")
<-c
}

533
html/wasm_exec.js Normal file
View File

@ -0,0 +1,533 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
(() => {
// Map multiple JavaScript environments to a single common API,
// preferring web standards over Node.js API.
//
// Environments considered:
// - Browsers
// - Node.js
// - Electron
// - Parcel
if (typeof global !== "undefined") {
// global already exists
} else if (typeof window !== "undefined") {
window.global = window;
} else if (typeof self !== "undefined") {
self.global = self;
} else {
throw new Error("cannot export Go (neither global, window nor self is defined)");
}
if (!global.require && typeof require !== "undefined") {
global.require = require;
}
if (!global.fs && global.require) {
global.fs = require("fs");
}
if (!global.fs) {
let outputBuf = "";
global.fs = {
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
writeSync(fd, buf) {
outputBuf += decoder.decode(buf);
const nl = outputBuf.lastIndexOf("\n");
if (nl != -1) {
console.log(outputBuf.substr(0, nl));
outputBuf = outputBuf.substr(nl + 1);
}
return buf.length;
},
write(fd, buf, offset, length, position, callback) {
if (offset !== 0 || length !== buf.length || position !== null) {
throw new Error("not implemented");
}
const n = this.writeSync(fd, buf);
callback(null, n);
},
open(path, flags, mode, callback) {
const err = new Error("not implemented");
err.code = "ENOSYS";
callback(err);
},
read(fd, buffer, offset, length, position, callback) {
const err = new Error("not implemented");
err.code = "ENOSYS";
callback(err);
},
fsync(fd, callback) {
callback(null);
},
};
}
if (!global.crypto) {
const nodeCrypto = require("crypto");
global.crypto = {
getRandomValues(b) {
nodeCrypto.randomFillSync(b);
},
};
}
if (!global.performance) {
global.performance = {
now() {
const [sec, nsec] = process.hrtime();
return sec * 1000 + nsec / 1000000;
},
};
}
if (!global.TextEncoder) {
global.TextEncoder = require("util").TextEncoder;
}
if (!global.TextDecoder) {
global.TextDecoder = require("util").TextDecoder;
}
// End of polyfills for common API.
const encoder = new TextEncoder("utf-8");
const decoder = new TextDecoder("utf-8");
global.Go = class {
constructor() {
this.argv = ["js"];
this.env = {};
this.exit = (code) => {
if (code !== 0) {
console.warn("exit code:", code);
}
};
this._exitPromise = new Promise((resolve) => {
this._resolveExitPromise = resolve;
});
this._pendingEvent = null;
this._scheduledTimeouts = new Map();
this._nextCallbackTimeoutID = 1;
const mem = () => {
// The buffer may change when requesting more memory.
return new DataView(this._inst.exports.mem.buffer);
}
const setInt64 = (addr, v) => {
mem().setUint32(addr + 0, v, true);
mem().setUint32(addr + 4, Math.floor(v / 4294967296), true);
}
const getInt64 = (addr) => {
const low = mem().getUint32(addr + 0, true);
const high = mem().getInt32(addr + 4, true);
return low + high * 4294967296;
}
const loadValue = (addr) => {
const f = mem().getFloat64(addr, true);
if (f === 0) {
return undefined;
}
if (!isNaN(f)) {
return f;
}
const id = mem().getUint32(addr, true);
return this._values[id];
}
const storeValue = (addr, v) => {
const nanHead = 0x7FF80000;
if (typeof v === "number") {
if (isNaN(v)) {
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 0, true);
return;
}
if (v === 0) {
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 1, true);
return;
}
mem().setFloat64(addr, v, true);
return;
}
switch (v) {
case undefined:
mem().setFloat64(addr, 0, true);
return;
case null:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 2, true);
return;
case true:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 3, true);
return;
case false:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 4, true);
return;
}
let ref = this._refs.get(v);
if (ref === undefined) {
ref = this._values.length;
this._values.push(v);
this._refs.set(v, ref);
}
let typeFlag = 0;
switch (typeof v) {
case "string":
typeFlag = 1;
break;
case "symbol":
typeFlag = 2;
break;
case "function":
typeFlag = 3;
break;
}
mem().setUint32(addr + 4, nanHead | typeFlag, true);
mem().setUint32(addr, ref, true);
}
const loadSlice = (addr) => {
const array = getInt64(addr + 0);
const len = getInt64(addr + 8);
return new Uint8Array(this._inst.exports.mem.buffer, array, len);
}
const loadSliceOfValues = (addr) => {
const array = getInt64(addr + 0);
const len = getInt64(addr + 8);
const a = new Array(len);
for (let i = 0; i < len; i++) {
a[i] = loadValue(array + i * 8);
}
return a;
}
const loadString = (addr) => {
const saddr = getInt64(addr + 0);
const len = getInt64(addr + 8);
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
}
const timeOrigin = Date.now() - performance.now();
this.importObject = {
go: {
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
// This changes the SP, thus we have to update the SP used by the imported function.
// func wasmExit(code int32)
"runtime.wasmExit": (sp) => {
const code = mem().getInt32(sp + 8, true);
this.exited = true;
delete this._inst;
delete this._values;
delete this._refs;
this.exit(code);
},
// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
"runtime.wasmWrite": (sp) => {
const fd = getInt64(sp + 8);
const p = getInt64(sp + 16);
const n = mem().getInt32(sp + 24, true);
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
},
// func nanotime() int64
"runtime.nanotime": (sp) => {
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
},
// func walltime() (sec int64, nsec int32)
"runtime.walltime": (sp) => {
const msec = (new Date).getTime();
setInt64(sp + 8, msec / 1000);
mem().setInt32(sp + 16, (msec % 1000) * 1000000, true);
},
// func scheduleTimeoutEvent(delay int64) int32
"runtime.scheduleTimeoutEvent": (sp) => {
const id = this._nextCallbackTimeoutID;
this._nextCallbackTimeoutID++;
this._scheduledTimeouts.set(id, setTimeout(
() => {
this._resume();
while (this._scheduledTimeouts.has(id)) {
// for some reason Go failed to register the timeout event, log and try again
// (temporary workaround for https://github.com/golang/go/issues/28975)
console.warn("scheduleTimeoutEvent: missed timeout event");
this._resume();
}
},
getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
));
mem().setInt32(sp + 16, id, true);
},
// func clearTimeoutEvent(id int32)
"runtime.clearTimeoutEvent": (sp) => {
const id = mem().getInt32(sp + 8, true);
clearTimeout(this._scheduledTimeouts.get(id));
this._scheduledTimeouts.delete(id);
},
// func getRandomData(r []byte)
"runtime.getRandomData": (sp) => {
crypto.getRandomValues(loadSlice(sp + 8));
},
// func stringVal(value string) ref
"syscall/js.stringVal": (sp) => {
storeValue(sp + 24, loadString(sp + 8));
},
// func valueGet(v ref, p string) ref
"syscall/js.valueGet": (sp) => {
const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
sp = this._inst.exports.getsp(); // see comment above
storeValue(sp + 32, result);
},
// func valueSet(v ref, p string, x ref)
"syscall/js.valueSet": (sp) => {
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
},
// func valueIndex(v ref, i int) ref
"syscall/js.valueIndex": (sp) => {
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
},
// valueSetIndex(v ref, i int, x ref)
"syscall/js.valueSetIndex": (sp) => {
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
},
// func valueCall(v ref, m string, args []ref) (ref, bool)
"syscall/js.valueCall": (sp) => {
try {
const v = loadValue(sp + 8);
const m = Reflect.get(v, loadString(sp + 16));
const args = loadSliceOfValues(sp + 32);
const result = Reflect.apply(m, v, args);
sp = this._inst.exports.getsp(); // see comment above
storeValue(sp + 56, result);
mem().setUint8(sp + 64, 1);
} catch (err) {
storeValue(sp + 56, err);
mem().setUint8(sp + 64, 0);
}
},
// func valueInvoke(v ref, args []ref) (ref, bool)
"syscall/js.valueInvoke": (sp) => {
try {
const v = loadValue(sp + 8);
const args = loadSliceOfValues(sp + 16);
const result = Reflect.apply(v, undefined, args);
sp = this._inst.exports.getsp(); // see comment above
storeValue(sp + 40, result);
mem().setUint8(sp + 48, 1);
} catch (err) {
storeValue(sp + 40, err);
mem().setUint8(sp + 48, 0);
}
},
// func valueNew(v ref, args []ref) (ref, bool)
"syscall/js.valueNew": (sp) => {
try {
const v = loadValue(sp + 8);
const args = loadSliceOfValues(sp + 16);
const result = Reflect.construct(v, args);
sp = this._inst.exports.getsp(); // see comment above
storeValue(sp + 40, result);
mem().setUint8(sp + 48, 1);
} catch (err) {
storeValue(sp + 40, err);
mem().setUint8(sp + 48, 0);
}
},
// func valueLength(v ref) int
"syscall/js.valueLength": (sp) => {
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
},
// valuePrepareString(v ref) (ref, int)
"syscall/js.valuePrepareString": (sp) => {
const str = encoder.encode(String(loadValue(sp + 8)));
storeValue(sp + 16, str);
setInt64(sp + 24, str.length);
},
// valueLoadString(v ref, b []byte)
"syscall/js.valueLoadString": (sp) => {
const str = loadValue(sp + 8);
loadSlice(sp + 16).set(str);
},
// func valueInstanceOf(v ref, t ref) bool
"syscall/js.valueInstanceOf": (sp) => {
mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
},
// func copyBytesToGo(dst []byte, src ref) (int, bool)
"syscall/js.copyBytesToGo": (sp) => {
const dst = loadSlice(sp + 8);
const src = loadValue(sp + 32);
if (!(src instanceof Uint8Array)) {
mem().setUint8(sp + 48, 0);
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
setInt64(sp + 40, toCopy.length);
mem().setUint8(sp + 48, 1);
},
// func copyBytesToJS(dst ref, src []byte) (int, bool)
"syscall/js.copyBytesToJS": (sp) => {
const dst = loadValue(sp + 8);
const src = loadSlice(sp + 16);
if (!(dst instanceof Uint8Array)) {
mem().setUint8(sp + 48, 0);
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
setInt64(sp + 40, toCopy.length);
mem().setUint8(sp + 48, 1);
},
"debug": (value) => {
console.log(value);
},
}
};
}
async run(instance) {
this._inst = instance;
this._values = [ // TODO: garbage collection
NaN,
0,
null,
true,
false,
global,
this,
];
this._refs = new Map();
this.exited = false;
const mem = new DataView(this._inst.exports.mem.buffer)
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
let offset = 4096;
const strPtr = (str) => {
const ptr = offset;
const bytes = encoder.encode(str + "\0");
new Uint8Array(mem.buffer, offset, bytes.length).set(bytes);
offset += bytes.length;
if (offset % 8 !== 0) {
offset += 8 - (offset % 8);
}
return ptr;
};
const argc = this.argv.length;
const argvPtrs = [];
this.argv.forEach((arg) => {
argvPtrs.push(strPtr(arg));
});
const keys = Object.keys(this.env).sort();
argvPtrs.push(keys.length);
keys.forEach((key) => {
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
});
const argv = offset;
argvPtrs.forEach((ptr) => {
mem.setUint32(offset, ptr, true);
mem.setUint32(offset + 4, 0, true);
offset += 8;
});
this._inst.exports.run(argc, argv);
if (this.exited) {
this._resolveExitPromise();
}
await this._exitPromise;
}
_resume() {
if (this.exited) {
throw new Error("Go program has already exited");
}
this._inst.exports.resume();
if (this.exited) {
this._resolveExitPromise();
}
}
_makeFuncWrapper(id) {
const go = this;
return function () {
const event = { id: id, this: this, args: arguments };
go._pendingEvent = event;
go._resume();
return event.result;
};
}
}
if (
global.require &&
global.require.main === module &&
global.process &&
global.process.versions &&
!global.process.versions.electron
) {
if (process.argv.length < 3) {
console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
process.exit(1);
}
const go = new Go();
go.argv = process.argv.slice(2);
go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
go.exit = process.exit;
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
process.on("exit", (code) => { // Node.js exits if no event handler is pending
if (code === 0 && !go.exited) {
// deadlock, make Go print error and stack traces
go._pendingEvent = { id: 0 };
go._resume();
}
});
return go.run(result.instance);
}).catch((err) => {
console.error(err);
process.exit(1);
});
}
})();

View File

@ -17,6 +17,7 @@ import (
"github.com/schollz/croc/v6/src/models"
"github.com/schollz/croc/v6/src/tcp"
"github.com/schollz/croc/v6/src/utils"
"github.com/schollz/croc/v6/src/webrelay"
log "github.com/schollz/logger"
"github.com/urfave/cli"
)
@ -51,6 +52,7 @@ func Run() (err error) {
Flags: []cli.Flag{
cli.StringFlag{Name: "code, c", Usage: "codephrase used to connect to relay"},
cli.BoolFlag{Name: "no-local", Usage: "disable local relay when sending"},
cli.BoolFlag{Name: "no-multi", Usage: "disable multiplexing"},
cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"},
},
HelpName: "croc send",
@ -70,6 +72,18 @@ func Run() (err error) {
cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"},
},
},
{
Name: "web",
Usage: "start your own web relay (optional)",
Description: "start web relay",
HelpName: "croc web",
Action: func(c *cli.Context) error {
return startWebRelay(c)
},
Flags: []cli.Flag{
cli.StringFlag{Name: "port", Value: "9014", Usage: "port of the web relay"},
},
},
}
app.Flags = []cli.Flag{
cli.BoolFlag{Name: "remember", Usage: "save these settings to reuse next time"},
@ -136,15 +150,16 @@ func getConfigFile() string {
func send(c *cli.Context) (err error) {
setDebugLevel(c)
crocOptions := croc.Options{
SharedSecret: c.String("code"),
IsSender: true,
Debug: c.GlobalBool("debug"),
NoPrompt: c.GlobalBool("yes"),
RelayAddress: c.GlobalString("relay"),
Stdout: c.GlobalBool("stdout"),
DisableLocal: c.Bool("no-local"),
RelayPorts: strings.Split(c.String("ports"), ","),
Ask: c.GlobalBool("ask"),
SharedSecret: c.String("code"),
IsSender: true,
Debug: c.GlobalBool("debug"),
NoPrompt: c.GlobalBool("yes"),
RelayAddress: c.GlobalString("relay"),
Stdout: c.GlobalBool("stdout"),
DisableLocal: c.Bool("no-local"),
RelayPorts: strings.Split(c.String("ports"), ","),
Ask: c.GlobalBool("ask"),
NoMultiplexing: c.Bool("no-multi"),
}
b, errOpen := ioutil.ReadFile(getConfigFile())
if errOpen == nil && !c.GlobalBool("remember") {
@ -381,3 +396,12 @@ func relay(c *cli.Context) (err error) {
}
return tcp.Run(debugString, ports[0], tcpPorts)
}
func startWebRelay(c *cli.Context) (err error) {
debugString := "info"
if c.GlobalBool("debug") {
debugString = "debug"
}
return webrelay.Run(debugString, c.String("port"))
}

View File

@ -2,7 +2,6 @@ package croc
import (
"bytes"
"crypto/elliptic"
"crypto/rand"
"encoding/binary"
"encoding/json"
@ -17,7 +16,6 @@ import (
"sync"
"time"
"github.com/denisbrodbeck/machineid"
"github.com/pkg/errors"
"github.com/schollz/croc/v6/src/comm"
"github.com/schollz/croc/v6/src/compress"
@ -27,10 +25,11 @@ import (
"github.com/schollz/croc/v6/src/tcp"
"github.com/schollz/croc/v6/src/utils"
log "github.com/schollz/logger"
"github.com/schollz/pake"
"github.com/schollz/pake/v2"
"github.com/schollz/peerdiscovery"
"github.com/schollz/progressbar/v2"
"github.com/schollz/spinner"
"github.com/tscholl2/siec"
)
func init() {
@ -48,22 +47,23 @@ func Debug(debug bool) {
// Options specifies user specific options
type Options struct {
IsSender bool
SharedSecret string
Debug bool
RelayAddress string
RelayPorts []string
Stdout bool
NoPrompt bool
DisableLocal bool
Ask bool
IsSender bool
SharedSecret string
Debug bool
RelayAddress string
RelayPorts []string
Stdout bool
NoPrompt bool
NoMultiplexing bool
DisableLocal bool
Ask bool
}
// Client holds the state of the croc transfer
type Client struct {
Options Options
Pake *pake.Pake
Key crypt.Encryption
Key []byte
ExternalIP, ExternalIPConnected string
// steps involved in forming relationship
@ -147,17 +147,11 @@ func New(ops Options) (c *Client, err error) {
c.conn = make([]*comm.Comm, 16)
// use default key (no encryption, until PAKE succeeds)
c.Key, err = crypt.New(nil, nil)
if err != nil {
return
}
// initialize pake
if c.Options.IsSender {
c.Pake, err = pake.Init([]byte(c.Options.SharedSecret), 1, elliptic.P521(), 1*time.Microsecond)
c.Pake, err = pake.Init([]byte(c.Options.SharedSecret), 1, siec.SIEC255(), 1*time.Microsecond)
} else {
c.Pake, err = pake.Init([]byte(c.Options.SharedSecret), 0, elliptic.P521(), 1*time.Microsecond)
c.Pake, err = pake.Init([]byte(c.Options.SharedSecret), 0, siec.SIEC255(), 1*time.Microsecond)
}
if err != nil {
return
@ -292,6 +286,10 @@ func (c *Client) transferOverLocalRelay(options TransferOptions, errchan chan<-
log.Debug("exchanged header message")
c.Options.RelayAddress = "localhost"
c.Options.RelayPorts = strings.Split(banner, ",")
if c.Options.NoMultiplexing {
log.Debug("no multiplexing")
c.Options.RelayPorts = []string{c.Options.RelayPorts[0]}
}
c.ExternalIP = ipaddr
errchan <- c.transfer(options)
}
@ -309,7 +307,7 @@ func (c *Client) Send(options TransferOptions) (err error) {
}
fmt.Fprintf(os.Stderr, "Code is: %s\nOn the other computer run\n\ncroc %s%s\n", c.Options.SharedSecret, otherRelay, c.Options.SharedSecret)
if c.Options.Ask {
machid, _ := machineid.ID()
machid := "machineid.ID()"
fmt.Fprintf(os.Stderr, "\rYour machine ID is '%s'\n", machid)
}
// // c.spinner.Suffix = " waiting for recipient..."
@ -369,6 +367,10 @@ func (c *Client) Send(options TransferOptions) (err error) {
c.conn[0] = conn
c.Options.RelayPorts = strings.Split(banner, ",")
if c.Options.NoMultiplexing {
log.Debug("no multiplexing")
c.Options.RelayPorts = []string{c.Options.RelayPorts[0]}
}
c.ExternalIP = ipaddr
log.Debug("exchanged header message")
errchan <- c.transfer(options)
@ -470,6 +472,10 @@ func (c *Client) Receive() (err error) {
c.conn[0].Send([]byte("handshake"))
c.Options.RelayPorts = strings.Split(banner, ",")
if c.Options.NoMultiplexing {
log.Debug("no multiplexing")
c.Options.RelayPorts = []string{c.Options.RelayPorts[0]}
}
log.Debug("exchanged header message")
fmt.Fprintf(os.Stderr, "\rsecuring channel...")
return c.transfer(TransferOptions{})
@ -551,7 +557,7 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
// c.spinner.Stop()
if !c.Options.NoPrompt || c.Options.Ask || senderInfo.Ask {
if c.Options.Ask || senderInfo.Ask {
machID, _ := machineid.ID()
machID := "machineid.ID()"
fmt.Fprintf(os.Stderr, "\rYour machine id is '%s'.\nAccept %s (%s) from '%s'? (y/n) ", machID, fname, utils.ByteCountDecimal(totalSize), senderInfo.MachineID)
} else {
fmt.Fprintf(os.Stderr, "\rAccept %s (%s)? (y/n) ", fname, utils.ByteCountDecimal(totalSize))
@ -648,10 +654,11 @@ func (c *Client) processMessageSalt(m message.Message) (done bool, err error) {
if err != nil {
return true, err
}
c.Key, err = crypt.New(key, m.Bytes)
c.Key, _, err = crypt.New(key, m.Bytes)
if err != nil {
return true, err
}
log.Debugf("key = %+x", c.Key)
if c.ExternalIPConnected == "" {
// it can be preset by the local relay
c.ExternalIPConnected = m.Message
@ -742,7 +749,7 @@ func (c *Client) processMessage(payload []byte) (done bool, err error) {
func (c *Client) updateIfSenderChannelSecured() (err error) {
if c.Options.IsSender && c.Step1ChannelSecured && !c.Step2FileInfoTransfered {
var b []byte
machID, _ := machineid.ID()
machID := "machineid.ID()"
b, err = json.Marshal(SenderInfo{
FilesToTransfer: c.FilesToTransfer,
MachineID: machID,
@ -835,7 +842,7 @@ func (c *Client) recipientGetFileReady(finished bool) (err error) {
}
c.TotalSent = 0
machID, _ := machineid.ID()
machID := "machineid.ID()"
bRequest, _ := json.Marshal(RemoteFileRequest{
CurrentFileChunkRanges: c.CurrentFileChunkRanges,
FilesToTransferCurrentNum: c.FilesToTransferCurrentNum,
@ -1040,7 +1047,7 @@ func (c *Client) receiveData(i int) {
break
}
data, err = c.Key.Decrypt(data)
data, err = crypt.Decrypt(data, c.Key)
if err != nil {
panic(err)
}
@ -1126,10 +1133,11 @@ func (c *Client) sendData(i int) {
posByte := make([]byte, 8)
binary.LittleEndian.PutUint64(posByte, pos)
dataToSend, err := c.Key.Encrypt(
dataToSend, err := crypt.Encrypt(
compress.Compress(
append(posByte, data[:n]...),
),
c.Key,
)
if err != nil {
panic(err)

View File

@ -20,7 +20,7 @@ func TestCroc(t *testing.T) {
go tcp.Run("debug", "8083")
go tcp.Run("debug", "8084")
go tcp.Run("debug", "8085")
time.Sleep(1 * time.Second)
time.Sleep(3 * time.Second)
log.Debug("setting up sender")
sender, err := New(Options{
@ -36,6 +36,7 @@ func TestCroc(t *testing.T) {
if err != nil {
panic(err)
}
time.Sleep(3 * time.Second)
log.Debug("setting up receiver")
receiver, err := New(Options{

View File

@ -5,56 +5,37 @@ import (
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"
"golang.org/x/crypto/pbkdf2"
)
// Encryption is the basic type for storing
// the key, passphrase and salt
type Encryption struct {
key []byte
passphrase []byte
salt []byte
}
// New generates a new Encryption, using the supplied passphrase and
// an optional supplied salt.
// Passing nil passphrase will not use decryption.
func New(passphrase []byte, salt []byte) (e Encryption, err error) {
if passphrase == nil {
e = Encryption{nil, nil, nil}
// New generates a new key based on a passphrase and salt
func New(passphrase []byte, usersalt []byte) (key []byte, salt []byte, err error) {
if len(passphrase) < 1 {
err = fmt.Errorf("need more than that for passphrase")
return
}
e.passphrase = passphrase
if salt == nil {
e.salt = make([]byte, 8)
if usersalt == nil {
salt = make([]byte, 8)
// http://www.ietf.org/rfc/rfc2898.txt
// Salt.
rand.Read(e.salt)
rand.Read(salt)
} else {
e.salt = salt
salt = usersalt
}
e.key = pbkdf2.Key([]byte(passphrase), e.salt, 100, 32, sha256.New)
key = pbkdf2.Key([]byte(passphrase), salt, 100, 32, sha256.New)
return
}
// Salt returns the salt bytes
func (e Encryption) Salt() []byte {
return e.salt
}
// Encrypt will generate an Encryption, prefixed with the IV
func (e Encryption) Encrypt(plaintext []byte) (encrypted []byte, err error) {
if e.passphrase == nil {
encrypted = plaintext
return
}
// Encrypt will encrypt using the pre-generated key
func Encrypt(plaintext []byte, key []byte) (encrypted []byte, err error) {
// generate a random iv each time
// http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
// Section 8.2
ivBytes := make([]byte, 12)
rand.Read(ivBytes)
b, err := aes.NewCipher(e.key)
b, err := aes.NewCipher(key)
if err != nil {
return
}
@ -67,13 +48,9 @@ func (e Encryption) Encrypt(plaintext []byte) (encrypted []byte, err error) {
return
}
// Decrypt an Encryption
func (e Encryption) Decrypt(encrypted []byte) (plaintext []byte, err error) {
if e.passphrase == nil {
plaintext = encrypted
return
}
b, err := aes.NewCipher(e.key)
// Decrypt using the pre-generated key
func Decrypt(encrypted []byte, key []byte) (plaintext []byte, err error) {
b, err := aes.NewCipher(key)
if err != nil {
return
}

View File

@ -6,55 +6,42 @@ import (
"github.com/stretchr/testify/assert"
)
func BenchmarkEncryptionNew(b *testing.B) {
func BenchmarkEncrypt(b *testing.B) {
bob, _, _ := New([]byte("password"), nil)
for i := 0; i < b.N; i++ {
bob, _ := New([]byte("password"), nil)
bob.Encrypt([]byte("hello, world"))
Encrypt([]byte("hello, world"), bob)
}
}
func BenchmarkEncryption(b *testing.B) {
bob, _ := New([]byte("password"), nil)
func BenchmarkDecrypt(b *testing.B) {
key, _, _ := New([]byte("password"), nil)
msg := []byte("hello, world")
enc, _ := Encrypt(msg, key)
b.ResetTimer()
for i := 0; i < b.N; i++ {
bob.Encrypt([]byte("hello, world"))
Decrypt(enc, key)
}
}
func TestEncryption(t *testing.T) {
bob, err := New([]byte("password"), nil)
key, salt, err := New([]byte("password"), nil)
assert.Nil(t, err)
jane, err := New([]byte("password"), bob.Salt())
msg := []byte("hello, world")
enc, err := Encrypt(msg, key)
assert.Nil(t, err)
enc, err := bob.Encrypt([]byte("hello, world"))
dec, err := Decrypt(enc, key)
assert.Nil(t, err)
dec, err := jane.Decrypt(enc)
assert.Nil(t, err)
assert.Equal(t, dec, []byte("hello, world"))
assert.Equal(t, msg, dec)
jane2, err := New([]byte("password"), nil)
// check reusing the salt
key2, _, err := New([]byte("password"), salt)
dec, err = Decrypt(enc, key2)
assert.Nil(t, err)
dec, err = jane2.Decrypt(enc)
assert.Equal(t, msg, dec)
// check reusing the salt
key2, _, err = New([]byte("wrong password"), salt)
dec, err = Decrypt(enc, key2)
assert.NotNil(t, err)
assert.NotEqual(t, dec, []byte("hello, world"))
jane3, err := New([]byte("passwordwrong"), bob.Salt())
assert.Nil(t, err)
dec, err = jane3.Decrypt(enc)
assert.NotNil(t, err)
assert.NotEqual(t, dec, []byte("hello, world"))
}
func TestNoEncryption(t *testing.T) {
bob, err := New(nil, nil)
assert.Nil(t, err)
jane, err := New(nil, nil)
assert.Nil(t, err)
enc, err := bob.Encrypt([]byte("hello, world"))
assert.Nil(t, err)
dec, err := jane.Decrypt(enc)
assert.Nil(t, err)
assert.Equal(t, dec, []byte("hello, world"))
assert.Equal(t, enc, []byte("hello, world"))
assert.NotEqual(t, msg, dec)
}

View File

@ -23,7 +23,7 @@ func (m Message) String() string {
}
// Send will send out
func Send(c *comm.Comm, key crypt.Encryption, m Message) (err error) {
func Send(c *comm.Comm, key []byte, m Message) (err error) {
mSend, err := Encode(key, m)
if err != nil {
return
@ -34,21 +34,25 @@ func Send(c *comm.Comm, key crypt.Encryption, m Message) (err error) {
}
// Encode will convert to bytes
func Encode(key crypt.Encryption, m Message) (b []byte, err error) {
func Encode(key []byte, m Message) (b []byte, err error) {
b, err = json.Marshal(m)
if err != nil {
return
}
b = compress.Compress(b)
b, err = key.Encrypt(b)
if key != nil {
b, err = crypt.Encrypt(b, key)
}
return
}
// Decode will convert from bytes
func Decode(key crypt.Encryption, b []byte) (m Message, err error) {
b, err = key.Decrypt(b)
if err != nil {
return
func Decode(key []byte, b []byte) (m Message, err error) {
if key != nil {
b, err = crypt.Decrypt(b, key)
if err != nil {
return
}
}
b = compress.Decompress(b)
err = json.Unmarshal(b, &m)

135
src/webrelay/webrelay.go Normal file
View File

@ -0,0 +1,135 @@
package webrelay
import (
"bytes"
"encoding/base64"
"encoding/json"
"net/http"
"strings"
"github.com/gorilla/websocket"
"github.com/schollz/croc/v6/src/comm"
"github.com/schollz/croc/v6/src/models"
"github.com/schollz/croc/v6/src/tcp"
log "github.com/schollz/logger"
)
func Run(debugString, port string) (err error) {
log.SetLevel(debugString)
http.HandleFunc("/ws", handlews)
http.Handle("/", http.FileServer(http.Dir("html")))
log.Infof("running on port %s", port)
return http.ListenAndServe(":"+port, nil)
}
var upgrader = websocket.Upgrader{} // use default options
func handlews(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Debug("upgrade:", err)
return
}
log.Debugf("connected: %+v", c.RemoteAddr())
defer c.Close()
_, message, err := c.ReadMessage()
if err != nil {
log.Debug("read:", err)
return
}
log.Debugf("recv: %s", message)
if bytes.Equal(message, []byte("receive")) {
// start receiving
log.Debug("initiating reciever")
err = receive(c)
if err != nil {
log.Error(err)
}
}
return
}
type Bundle struct {
Message string `json:"m,omitempty"`
PayloadString string `json:"ps,omitempty"`
PayloadBytes []byte `json:"pb,omitempty"`
}
func receive(conn *websocket.Conn) (err error) {
conn.WriteMessage(websocket.TextMessage, []byte("initiated"))
var com *comm.Comm
var banner, externalIP, connectPort string
for {
var message []byte
_, message, err = conn.ReadMessage()
if err != nil {
log.Debug("read:", err)
return
}
var bu Bundle
errBundle := json.Unmarshal(message, &bu)
if errBundle == nil {
if bu.Message == "room" {
relayAddress := models.DEFAULT_RELAY
relayAddress = "localhost:9009"
com, banner, externalIP, err = tcp.ConnectToTCPServer(relayAddress, bu.PayloadString)
if err != nil {
log.Error(err)
return
}
log.Debugf("comm: %+v", com)
log.Debugf("banner: %+v", banner)
log.Debugf("externalIP: %+v", externalIP)
log.Debugf("err: %+v", err)
err = com.Send([]byte("ips?"))
if err != nil {
log.Error(err)
return
}
var data []byte
data, err = com.Receive()
if err != nil {
log.Error(err)
return
}
log.Debugf("ips data: %s", data)
err = com.Send([]byte("handshake"))
if err != nil {
log.Error(err)
return
}
connectPort = strings.Split(banner, ",")[0]
log.Debugf("connecting on %s", connectPort)
err = conn.WriteMessage(websocket.TextMessage, []byte("initpake"))
if err != nil {
log.Error(err)
return
}
}
}
b, errBase64 := base64.StdEncoding.DecodeString(string(message))
if errBase64 == nil {
log.Debug("parsing base64 bytes")
err = com.Send(b)
if err != nil {
log.Error(err)
return
}
b, err = com.Receive()
if err != nil {
log.Error(err)
return
}
err = conn.WriteMessage(websocket.TextMessage, []byte(base64.StdEncoding.EncodeToString(b)))
if err != nil {
log.Error(err)
return
}
}
}
return
}