mirror of
https://github.com/schollz/croc.git
synced 2024-11-24 08:02:33 +03:00
save the keypair in ~/.config/croc
This commit is contained in:
parent
5ca9b06eb7
commit
e084186b5e
36
connect.go
36
connect.go
@ -19,7 +19,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/schollz/messagebox/keypair"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/schollz/croc/keypair"
|
||||
"github.com/schollz/croc/randomstring"
|
||||
"github.com/schollz/peerdiscovery"
|
||||
"github.com/schollz/progressbar"
|
||||
tarinator "github.com/schollz/tarinator-go"
|
||||
@ -77,8 +79,33 @@ func NewConnection(config *AppConfig) (*Connection, error) {
|
||||
c.Yes = config.Yes
|
||||
c.rate = config.Rate
|
||||
c.Local = config.Local
|
||||
c.keypair, _ = keypair.New()
|
||||
|
||||
// make or load keypair
|
||||
homedir, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
pathToCrocConfig := path.Join(homedir, ".config", "croc", "keys")
|
||||
if _, errExists := os.Stat(pathToCrocConfig); os.IsNotExist(errExists) {
|
||||
// path/to/whatever does not exist
|
||||
os.MkdirAll(path.Join(homedir, ".config", "croc"), 0700)
|
||||
keys, _ := keypair.New()
|
||||
err = ioutil.WriteFile(pathToCrocConfig, []byte(keys.String()), 0644)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
}
|
||||
keypairBytes, err := ioutil.ReadFile(pathToCrocConfig)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
c.keypair, err = keypair.Load(string(keypairBytes))
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Your public key: %s\n", c.keypair.Public)
|
||||
|
||||
if c.Debug {
|
||||
SetLogLevel("debug")
|
||||
} else {
|
||||
@ -395,7 +422,10 @@ func (c *Connection) runClient(serverName string) error {
|
||||
return
|
||||
}
|
||||
if id == 0 {
|
||||
passphraseString := RandStringBytesMaskImprSrc(20)
|
||||
passphraseString, err := randomstring.GenerateRandomString(30)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Debugf("passphrase: [%s]", passphraseString)
|
||||
encryptedPassword, err := c.keypair.Encrypt([]byte(passphraseString), publicKeyRecipient)
|
||||
if err != nil {
|
||||
|
146
keypair/keypair.go
Normal file
146
keypair/keypair.go
Normal file
@ -0,0 +1,146 @@
|
||||
package keypair
|
||||
|
||||
import (
|
||||
crypto_rand "crypto/rand"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/mr-tron/base58/base58"
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
)
|
||||
|
||||
type KeyPair struct {
|
||||
Public string `json:"public"`
|
||||
Private string `json:"private,omitempty"`
|
||||
private *[32]byte
|
||||
public *[32]byte
|
||||
}
|
||||
|
||||
func (kp KeyPair) String() string {
|
||||
b, _ := json.Marshal(kp)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Load will load from a string
|
||||
func Load(keypairString string) (kp KeyPair, err error) {
|
||||
err = json.Unmarshal([]byte(keypairString), &kp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
kp, err = New(kp)
|
||||
return
|
||||
}
|
||||
|
||||
// New will generate a new key pair, or reload a keypair
|
||||
// from a public key or a public-private key pair.
|
||||
func New(kpLoad ...KeyPair) (kp KeyPair, err error) {
|
||||
kp = KeyPair{}
|
||||
if len(kpLoad) > 0 {
|
||||
kp.Public = kpLoad[0].Public
|
||||
kp.Private = kpLoad[0].Private
|
||||
} else {
|
||||
kp.Public, kp.Private, err = generateKeyPair()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
kp.public, err = keyToBytes(kp.Public)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(kp.Private) > 0 {
|
||||
kp.private, err = keyToBytes(kp.Private)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func generateKeyPair() (publicKey, privateKey string, err error) {
|
||||
publicKeyBytes, privateKeyBytes, err := box.GenerateKey(crypto_rand.Reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
publicKey = base58.FastBase58Encoding(publicKeyBytes[:])
|
||||
privateKey = base58.FastBase58Encoding(privateKeyBytes[:])
|
||||
return
|
||||
}
|
||||
|
||||
func keyToBytes(s string) (key *[32]byte, err error) {
|
||||
var keyBytes []byte
|
||||
keyBytes, err = base58.FastBase58Decoding(s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
key = new([32]byte)
|
||||
copy(key[:], keyBytes[:32])
|
||||
return
|
||||
}
|
||||
|
||||
// Encrypt a message for a recipient
|
||||
func (kp KeyPair) Encrypt(msg []byte, recipientPublicKey string) (encrypted []byte, err error) {
|
||||
recipient, err := New(KeyPair{Public: recipientPublicKey})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
encrypted, err = encryptWithKeyPair(msg, kp.private, recipient.public)
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt a message
|
||||
func (kp KeyPair) Decrypt(encrypted []byte, senderPublicKey string) (msg []byte, err error) {
|
||||
sender, err := New(KeyPair{Public: senderPublicKey})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
msg, err = decryptWithKeyPair(encrypted, sender.public, kp.private)
|
||||
return
|
||||
}
|
||||
|
||||
func encryptWithKeyPair(msg []byte, senderPrivateKey, recipientPublicKey *[32]byte) (encrypted []byte, err error) {
|
||||
// You must use a different nonce for each message you encrypt with the
|
||||
// same key. Since the nonce here is 192 bits long, a random value
|
||||
// provides a sufficiently small probability of repeats.
|
||||
var nonce [24]byte
|
||||
if _, err = io.ReadFull(crypto_rand.Reader, nonce[:]); err != nil {
|
||||
return
|
||||
}
|
||||
// This encrypts msg and appends the result to the nonce.
|
||||
encrypted = box.Seal(nonce[:], msg, &nonce, recipientPublicKey, senderPrivateKey)
|
||||
return
|
||||
}
|
||||
|
||||
func decryptWithKeyPair(enc []byte, senderPublicKey, recipientPrivateKey *[32]byte) (decrypted []byte, err error) {
|
||||
// The recipient can decrypt the message using their private key and the
|
||||
// sender's public key. When you decrypt, you must use the same nonce you
|
||||
// used to encrypt the message. One way to achieve this is to store the
|
||||
// nonce alongside the encrypted message. Above, we stored the nonce in the
|
||||
// first 24 bytes of the encrypted text.
|
||||
var decryptNonce [24]byte
|
||||
copy(decryptNonce[:], enc[:24])
|
||||
var ok bool
|
||||
decrypted, ok = box.Open(nil, enc[24:], &decryptNonce, senderPublicKey, recipientPrivateKey)
|
||||
if !ok {
|
||||
err = errors.New("keypair decryption failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||
// slice with the contents of the given slice followed by that many bytes and a
|
||||
// second slice that aliases into it and contains only the extra bytes. If the
|
||||
// original slice has sufficient capacity then no allocation is performed.
|
||||
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||
if total := len(in) + n; cap(in) >= total {
|
||||
head = in[:total]
|
||||
} else {
|
||||
head = make([]byte, total)
|
||||
copy(head, in)
|
||||
}
|
||||
tail = head[len(in):]
|
||||
return
|
||||
}
|
66
randomstring/randomstring.go
Normal file
66
randomstring/randomstring.go
Normal file
@ -0,0 +1,66 @@
|
||||
package randomstring
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Adapted from https://elithrar.github.io/article/generating-secure-random-numbers-crypto-rand/
|
||||
|
||||
func init() {
|
||||
assertAvailablePRNG()
|
||||
}
|
||||
|
||||
func assertAvailablePRNG() {
|
||||
// Assert that a cryptographically secure PRNG is available.
|
||||
// Panic otherwise.
|
||||
buf := make([]byte, 1)
|
||||
|
||||
_, err := io.ReadFull(rand.Reader, buf)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("crypto/rand is unavailable: Read() failed with %#v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateRandomBytes returns securely generated random bytes.
|
||||
// It will return an error if the system's secure random
|
||||
// number generator fails to function correctly, in which
|
||||
// case the caller should not continue.
|
||||
func GenerateRandomBytes(n int) ([]byte, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := rand.Read(b)
|
||||
// Note that err == nil only if we read len(b) bytes.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// GenerateRandomString returns a securely generated random string.
|
||||
// It will return an error if the system's secure random
|
||||
// number generator fails to function correctly, in which
|
||||
// case the caller should not continue.
|
||||
func GenerateRandomString(n int) (string, error) {
|
||||
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
|
||||
bytes, err := GenerateRandomBytes(n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for i, b := range bytes {
|
||||
bytes[i] = letters[b%byte(len(letters))]
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// GenerateRandomStringURLSafe returns a URL-safe, base64 encoded
|
||||
// securely generated random string.
|
||||
// It will return an error if the system's secure random
|
||||
// number generator fails to function correctly, in which
|
||||
// case the caller should not continue.
|
||||
func GenerateRandomStringURLSafe(n int) (string, error) {
|
||||
b, err := GenerateRandomBytes(n)
|
||||
return base64.URLEncoding.EncodeToString(b), err
|
||||
}
|
15
randomstring/randomstring_test.go
Normal file
15
randomstring/randomstring_test.go
Normal file
@ -0,0 +1,15 @@
|
||||
package randomstring
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRandomString(t *testing.T) {
|
||||
r, err := GenerateRandomString(20)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 20, len(r))
|
||||
fmt.Println(r)
|
||||
}
|
Loading…
Reference in New Issue
Block a user