import (
"bytes"
"encoding/base64"
- "errors"
"fmt"
"hash"
"io/ioutil"
- "log"
"os"
"strings"
"syscall"
- "cypherpunks.ru/balloon"
"github.com/agl/ed25519"
+ "github.com/pkg/errors"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/ssh/terminal"
+
+ "cypherpunks.ru/balloon"
)
const (
- // DefaultS is default Balloon space cost
+ // DefaultS default Balloon space cost
DefaultS = 1 << 20 / 32
- // DefaultT is default Balloon time cost
+ // DefaultT default Balloon time cost
DefaultT = 1 << 4
- // DefaultP is default Balloon number of jobs
+ // DefaultP default Balloon number of job
DefaultP = 2
+
+ wrapDecodeString = "base64.RawStdEncoding.DecodeString"
)
// Verifier is used to authenticate a peer
return &Verifier{S: s, T: t, P: p, ID: id}
}
-func blake2bKeyless() hash.Hash {
- h, err := blake2b.New256(nil)
+// PasswordApply applies the password: create Ed25519 keypair based on it,
+// saves public key in verifier.
+func (v *Verifier) PasswordApply(password string) (*[ed25519.PrivateKeySize]byte, error) {
+ // TODO: there is an extremely weird bug, `balloon.H` panic if I the `hash.Hash`
+ // outside the `hasher` function.
+ var err error
+ hasher := func() hash.Hash {
+ var nilHash hash.Hash
+ nilHash, err = blake2b.New256(nil)
+ return nilHash
+ }
+ r := balloon.H(hasher, []byte(password), v.ID[:], v.S, v.T, v.P)
if err != nil {
- panic(err)
+ return nil, errors.Wrap(err, wrapBlake2bNew256)
}
- return h
-}
-// PasswordApply applies the password: create Ed25519 keypair based on it,
-// saves public key in verifier.
-func (v *Verifier) PasswordApply(password string) *[ed25519.PrivateKeySize]byte {
- r := balloon.H(blake2bKeyless, []byte(password), v.ID[:], v.S, v.T, v.P)
defer SliceZero(r)
src := bytes.NewBuffer(r)
pub, prv, err := ed25519.GenerateKey(src)
if err != nil {
- log.Fatalln("Unable to generate Ed25519 keypair", err)
+ return nil, errors.Wrap(err, "ed25519.GenerateKey")
}
v.Pub = pub
- return prv
+ return prv, nil
}
// VerifierFromString parses either short or long verifier form.
}
var s, t, p int
n, err := fmt.Sscanf(ss[2], "s=%d,t=%d,p=%d", &s, &t, &p)
- if n != 3 || err != nil {
+ if err != nil {
+ return nil, errors.Wrap(err, "fmt.Sscanf")
+ }
+ if n != 3 {
return nil, errors.New("Invalid verifier parameters")
}
salt, err := base64.RawStdEncoding.DecodeString(ss[3])
if err != nil {
- return nil, err
+ return nil, errors.Wrap(err, wrapDecodeString)
}
v := Verifier{S: s, T: t, P: p}
id := new([IDSize]byte)
if len(ss) == 5 {
pub, err := base64.RawStdEncoding.DecodeString(ss[4])
if err != nil {
- return nil, err
+ return nil, errors.Wrap(err, wrapDecodeString)
}
v.Pub = new([ed25519.PublicKeySize]byte)
copy(v.Pub[:], pub)
// KeyRead reads the key either from text file (if path is specified), or
// from the terminal.
func KeyRead(path string) (string, error) {
+ const (
+ emptyString = ""
+ wrapOsStderrWrite = "os.Stderr.Write"
+ )
var p []byte
var err error
var pass string
- if path == "" {
- os.Stderr.Write([]byte("Passphrase:"))
+ if path == emptyString {
+ if _, err = os.Stderr.Write([]byte("Passphrase:")); err != nil {
+ return emptyString, errors.Wrap(err, wrapOsStderrWrite)
+ }
p, err = terminal.ReadPassword(int(uintptr(syscall.Stdin)))
- os.Stderr.Write([]byte("\n"))
+ if err != nil {
+ return emptyString, errors.Wrap(err, "terminal.ReadPassword")
+ }
+ if _, err = os.Stderr.Write([]byte("\n")); err != nil {
+ return emptyString, errors.Wrap(err, wrapOsStderrWrite)
+ }
pass = string(p)
} else {
- p, err = ioutil.ReadFile(path)
+ if p, err = ioutil.ReadFile(path); err != nil {
+ return emptyString, errors.Wrap(err, "ioutil.ReadFile")
+ }
pass = strings.TrimRight(string(p), "\n")
}
- if err != nil {
- return "", err
- }
if len(pass) == 0 {
- return "", errors.New("Empty passphrase submitted")
+ return emptyString, errors.New("Empty passphrase submitted")
}
- return pass, err
+ return pass, nil
}