summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/finditer/finditer.go43
-rw-r--r--tools/hashpw/hashpw.go34
-rw-r--r--tools/hole/hole.go94
3 files changed, 143 insertions, 28 deletions
diff --git a/tools/finditer/finditer.go b/tools/finditer/finditer.go
new file mode 100644
index 0000000..fbd0363
--- /dev/null
+++ b/tools/finditer/finditer.go
@@ -0,0 +1,43 @@
+package main
+
+import (
+ "encoding/hex"
+ "fmt"
+ "sort"
+ "testing"
+ "time"
+
+ "gitlab.com/rhogenson/roseh.moe/internal/pwhash"
+)
+
+func mustHex(s string) []byte {
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ panic(err)
+ }
+ return b
+}
+
+const password = "emcee polio cardiac disclose superglue clapper cruelness stonework tingly unarmored"
+
+var salt = mustHex("a0aa6971f38827e5")
+
+var iterations int
+
+func BenchmarkHashIter(b *testing.B) {
+ for b.Loop() {
+ pwhash.HashIter(password, salt, iterations)
+ }
+}
+
+func main() {
+ const targetDuration = 5 * time.Second
+ for iterations = 8192; time.Duration(testing.Benchmark(BenchmarkHashIter).NsPerOp()) < targetDuration; iterations *= 2 {
+ }
+ lo := iterations / 2
+ hi := iterations
+ fmt.Println(lo + sort.Search(hi-lo, func(i int) bool {
+ iterations = lo + i
+ return time.Duration(testing.Benchmark(BenchmarkHashIter).NsPerOp()) > targetDuration
+ }))
+}
diff --git a/tools/hashpw/hashpw.go b/tools/hashpw/hashpw.go
new file mode 100644
index 0000000..c75fa2e
--- /dev/null
+++ b/tools/hashpw/hashpw.go
@@ -0,0 +1,34 @@
+package main
+
+import (
+ "crypto/rand"
+ "fmt"
+ "os"
+
+ "gitlab.com/rhogenson/roseh.moe/internal/pwhash"
+ "golang.org/x/term"
+)
+
+func hashpw() error {
+ fmt.Fprint(os.Stderr, "Enter password: ")
+ password, err := term.ReadPassword(int(os.Stdin.Fd()))
+ fmt.Fprintln(os.Stderr)
+ if err != nil {
+ return err
+ }
+ salt := make([]byte, pwhash.SaltSize)
+ rand.Read(salt)
+ hash, err := pwhash.Hash(string(password), salt)
+ if err != nil {
+ return err
+ }
+ fmt.Printf("%x%x\n", hash, salt)
+ return nil
+}
+
+func main() {
+ if err := hashpw(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
diff --git a/tools/hole/hole.go b/tools/hole/hole.go
index d701c40..c6946f0 100644
--- a/tools/hole/hole.go
+++ b/tools/hole/hole.go
@@ -1,36 +1,54 @@
+// The hole command is a CLI uploader tool for the roseh.moe wormhole.
+//
+// # Magic wormhole
+//
+// The name is inspired by the original "magic wormhole" library. The goal is to
+// get a file from one computer to another without a USB stick, Including cases
+// where scp might not be available. This includes transferring files to your
+// phone, for example, although a USB cord might still be more convenient when
+// moving many files. It should also work over restrictive firewalls. You should
+// be able to use the wormhole as long as you can access http://roseh.moe in
+// a browser.
+//
+// # Usage example
+//
+// The following shows a typical interaction with the tool
+//
+// $ hole file.pdf
+// https://roseh.moe/wormhole/dv-32-ethos-aztec-shako-spoils-2y-dwarf-sand-any
+// Waiting for upload...
+// <tool hangs, waiting to upload the file>
+//
+// Then, on another machine:
+//
+// $ curl https://roseh.moe/wormhole/dv-32-ethos-aztec-shako-spoils-2y-dwarf-sand-any > file.pdf
+//
+// # Web interface
+//
+// There's also a web interface available for the wormhole, available at
+// https://roseh.moe/wormhole.
package main
import (
- "crypto/rand"
- "encoding/binary"
"flag"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
- "strings"
- "gitlab.com/rhogenson/roseh.moe/internal/wordlist"
+ "gitlab.com/rhogenson/roseh.moe/internal/hole"
)
var url = flag.String("url", "https://roseh.moe", "server URL")
-func makeHole() string {
- const nWords = 10
- buf := make([]byte, 2*nWords)
- rand.Read(buf)
- words := make([]string, nWords)
- for i := range words {
- words[i] = wordlist.Words[binary.NativeEndian.Uint16(buf[2*i:])&0x1fff]
- }
- return strings.Join(words, "-")
-}
-
-func hole() error {
+func upload() error {
args := flag.Args()
- if len(args) != 1 {
- return fmt.Errorf("usage error")
+ if len(args) == 0 {
+ return fmt.Errorf("usage error: missing filename")
+ }
+ if len(args) > 1 {
+ return fmt.Errorf("usage error: too many positional arguments")
}
fileName := args[0]
f, err := os.Open(fileName)
@@ -38,18 +56,19 @@ func hole() error {
return err
}
defer f.Close()
- hole := makeHole()
+ hole := hole.New()
fmt.Println(*url + "/wormhole/" + hole)
+ fmt.Fprintln(os.Stderr, "Waiting for upload...")
resp, err := http.Get(*url + "/wormhole/" + hole + "/ready")
if err != nil {
- return err
+ return fmt.Errorf("check wormhole ready: %s", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
- return fmt.Errorf("%s", resp.Status)
+ return fmt.Errorf("check wormhole ready: status: %s", resp.Status)
}
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
- return err
+ return fmt.Errorf("check wormhole ready: read response: %s", err)
}
pipeReader, pipeWriter := io.Pipe()
multipartWriter := multipart.NewWriter(pipeWriter)
@@ -57,15 +76,15 @@ func hole() error {
defer pipeWriter.Close()
part, err := multipartWriter.CreateFormFile("file", fileName)
if err != nil {
- fmt.Fprintln(os.Stderr, err)
+ pipeWriter.CloseWithError(fmt.Errorf("create multipart reader: %s", err))
return
}
if _, err := io.Copy(part, f); err != nil {
- fmt.Fprintln(os.Stderr, err)
+ pipeWriter.CloseWithError(fmt.Errorf("write body: %s", err))
return
}
if err := multipartWriter.Close(); err != nil {
- fmt.Fprintln(os.Stderr, err)
+ pipeWriter.CloseWithError(fmt.Errorf("write trailer: %s", err))
return
}
}()
@@ -75,7 +94,7 @@ func hole() error {
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
- return fmt.Errorf("%s", resp.Status)
+ return fmt.Errorf("upload: status: %s", resp.Status)
}
io.Copy(os.Stderr, resp.Body)
fmt.Fprintln(os.Stderr)
@@ -83,9 +102,28 @@ func hole() error {
}
func main() {
+ flag.Usage = func() {
+ fmt.Fprint(os.Stderr, `Usage: hole FILE
+Create a one-time wormhole that allows a specific client to download a
+file. Prints the URL to download the file and then hangs until the file
+is downloaded.
+
+Example:
+ $ hole file.pdf
+ https://roseh.moe/wormhole/dv-32-ethos-aztec-shako-spoils-2y-dwarf-sand-any
+ Waiting for upload...
+ <tool hangs, waiting to upload the file>
+
+Then, on another machine:
+ $ curl https://roseh.moe/wormhole/dv-32-ethos-aztec-shako-spoils-2y-dwarf-sand-any > file.pdf
+
+Flags:
+`)
+ flag.PrintDefaults()
+ }
flag.Parse()
- if err := hole(); err != nil {
- fmt.Fprintln(os.Stderr, err)
+ if err := upload(); err != nil {
+ fmt.Fprintf(os.Stderr, "hole: %s\n", err)
os.Exit(1)
}
}