Added clean implementation of ECDH for curve25519

This commit is contained in:
David Vennik
2022-10-05 12:43:02 +02:00
parent ab903a4407
commit 594e3c7ccf
13 changed files with 282 additions and 42 deletions

View File

@@ -2,7 +2,6 @@ package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -189,7 +188,7 @@ func Version() string {
Patch,
)
path := filepath.Join(PathBase, "version.go")
if e = ioutil.WriteFile(path, []byte(versionFileOut),
if e = os.WriteFile(path, []byte(versionFileOut),
0666); log.E.Chk(e) {
fmt.Println(e)
}

12
go.mod
View File

@@ -1,9 +1,10 @@
module github.com/Indra-Labs/indranet
module github.com/Indra-Labs/indra
go 1.18
go 1.19
require (
github.com/cybriq/proc v0.1.33
github.com/cybriq/proc v0.2.0
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b
gopkg.in/src-d/go-git.v4 v4.13.1
)
@@ -18,9 +19,8 @@ require (
github.com/src-d/gcfg v1.4.0 // indirect
github.com/xanzy/ssh-agent v0.2.1 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

16
go.sum
View File

@@ -5,8 +5,8 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/cybriq/proc v0.1.33 h1:sGEcCjrC7oJ2skIRyOwM6KHztTV1m3Q4fEfOJhIQmSc=
github.com/cybriq/proc v0.1.33/go.mod h1:b6JDUUwfe8soxWzvAziWA/msrb73O2v6gZEQL+wHYx8=
github.com/cybriq/proc v0.2.0 h1:oemzeS1eXBX0OZZk0U5MXe+BzSvF/pQyPZ8Rt3jIc/U=
github.com/cybriq/proc v0.2.0/go.mod h1:b6JDUUwfe8soxWzvAziWA/msrb73O2v6gZEQL+wHYx8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -52,22 +52,26 @@ github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHg
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0=
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

82
pkg/ciph/gcm.go Normal file
View File

@@ -0,0 +1,82 @@
package ciph
//
// import (
// "crypto/aes"
// "crypto/cipher"
// "crypto/rand"
// "encoding/hex"
// "errors"
// "fmt"
// "io"
//
// "golang.org/x/crypto/argon2"
// )
//
// // Get returns a GCM cipher given a password string. Note that this cipher
// // must be renewed every 4gb of encrypted data as it is GCM.
// func Get(password []byte) (gcm cipher.AEAD, e error) {
// bytes := make([]byte, len(password))
// rb := make([]byte, len(password))
// copy(bytes, password)
// copy(rb, password)
// var c cipher.Block
// ark := argon2.IDKey(rb, bytes, 1, 64*1024, 4, 32)
// if c, e = aes.NewCipher(ark); log.E.Chk(e) {
// return
// }
// if gcm, e = cipher.NewGCM(c); log.E.Chk(e) {
// }
// for i := range bytes {
// bytes[i] = 0
// rb[i] = 0
// }
// return
// }
//
// // DecryptMessage attempts to decode the received message
// func DecryptMessage(creator string, ciph cipher.AEAD, data []byte) (
// msg []byte,
// e error,
// ) {
// nonceSize := ciph.NonceSize()
// msg, e = ciph.Open(nil, data[:nonceSize], data[nonceSize:], nil)
// if e != nil {
// e = errors.New(fmt.Sprintf("%s %s", creator, e.Error()))
// } else {
// log.D.Ln("decrypted message", hex.EncodeToString(data[:nonceSize]))
// }
// return
// }
//
// // EncryptMessage encrypts a message, if the nonce is given it uses that
// // otherwise it generates a new one. If there is no cipher this just returns a
// // message with the given magic prepended.
// func EncryptMessage(
// creator string,
// ciph cipher.AEAD,
// magic []byte,
// nonce, data []byte,
// ) (msg []byte, e error) {
// if ciph != nil {
// if nonce == nil {
// nonce, e = GetNonce(ciph)
// }
// msg = append(
// append(magic, nonce...),
// ciph.Seal(nil, nonce, data, nil)...,
// )
// } else {
// msg = append(magic, data...)
// }
// return
// }
//
// // GetNonce reads from a cryptographically secure random number source
// func GetNonce(ciph cipher.AEAD) (nonce []byte, e error) {
// // get a nonce for the packet, it is both message ID and salt
// nonce = make([]byte, ciph.NonceSize())
// if _, e = io.ReadFull(rand.Reader, nonce); log.E.Chk(e) {
// }
// return
// }

8
pkg/ciph/log.go Normal file
View File

@@ -0,0 +1,8 @@
package ciph
import (
"github.com/cybriq/proc"
log2 "github.com/cybriq/proc/pkg/log"
)
var log = log2.GetLogger(proc.PathBase)

113
pkg/ecdh/ecdh.go Normal file
View File

@@ -0,0 +1,113 @@
package ecdh
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/curve25519"
)
// Keysize is the size of both Privkey and Pubkey for ECDH
const Keysize = 32
// Privkey is a private key for use with ECDH
type Privkey [Keysize]byte
// Pubkey is a public key generated via curve25519.ScalarBaseMult
type Pubkey [Keysize]byte
// Keypair is pointers to a private and public key bundled into a struct
type Keypair struct {
*Privkey
*Pubkey
}
// KeySizeError indicates a provided slice of bytes is the wrong length
var KeySizeError error = fmt.Errorf("key length must be precisely %d bytes",
Keysize)
func GeneratePrivkey() (prv *Privkey, err error) {
var pk [32]byte
_, err = rand.Read(pk[:])
if log.E.Chk(err) {
return
}
pk[0] &= 248
pk[31] &= 127
pk[31] |= 64
pr := Privkey(pk)
prv = &pr
return
}
func (prv Privkey) GeneratePubkey() (pub *Pubkey) {
var dst [32]byte
curve25519.ScalarBaseMult(&dst, (*[32]byte)(&prv))
pu := Pubkey(dst)
pub = &pu
return
}
func (prv Privkey) ToBytes() (b []byte) {
b = make([]byte, Keysize)
copy(b, prv[:])
return
}
func (pub Pubkey) ToBytes() (b []byte) {
b = make([]byte, Keysize)
copy(b, pub[:])
return
}
func PrivkeyFromBytes(b []byte) (prv *Privkey, err error) {
if len(b) != Keysize {
err = KeySizeError
return
}
var k [Keysize]byte
copy(k[:], b)
prv = (*Privkey)(&k)
return
}
func PubkeyFromBytes(b []byte) (pub *Pubkey, err error) {
if len(b) != Keysize {
err = KeySizeError
return
}
var k [Keysize]byte
copy(k[:], b)
pub = (*Pubkey)(&k)
return
}
// GenerateKeypair generates a new curve25519 private/public key pair.
func GenerateKeypair() (keypair Keypair, err error) {
if keypair.Privkey, err = GeneratePrivkey(); log.E.Chk(err) {
return
}
keypair.Pubkey = keypair.Privkey.GeneratePubkey()
return
}
// KeypairFromPrivkeyBytes generates a Keypair from raw bytes
func KeypairFromPrivkeyBytes(b []byte) (keypair *Keypair, err error) {
keypair = &Keypair{}
if keypair.Privkey, err = PrivkeyFromBytes(b); log.E.Chk(err) {
return
}
keypair.Pubkey = keypair.Privkey.GeneratePubkey()
return
}
// ComputeSecret uses a Keypair and a Pubkey to generate a secret to use in
// encryption. This formula generates the same value from one private to another
// public key as the public key of the first with the private key from the
// second. Thus neither party must send a secret over the network.
func (kp Privkey) ComputeSecret(p *Pubkey) (sec []byte, err error) {
pub := [Keysize]byte(*p)
if sec, err = curve25519.X25519(kp[:], pub[:]); log.E.Chk(err) {
}
return
}

27
pkg/ecdh/ecdh_test.go Normal file
View File

@@ -0,0 +1,27 @@
package ecdh
import (
"bytes"
"testing"
)
func TestComputeSecret(t *testing.T) {
var err error
var kp1, kp2 Keypair
if kp1, err = GenerateKeypair(); log.E.Chk(err) {
t.Error(err)
}
if kp2, err = GenerateKeypair(); log.E.Chk(err) {
t.Error(err)
}
var sec1, sec2 []byte
if sec1, err = kp1.ComputeSecret(kp2.Pubkey); log.E.Chk(err) {
t.Error(err)
}
if sec2, err = kp2.ComputeSecret(kp1.Pubkey); log.E.Chk(err) {
t.Error(err)
}
if bytes.Compare(sec1, sec2) != 0 {
t.Error("secrets do not match")
}
}

8
pkg/ecdh/log.go Normal file
View File

@@ -0,0 +1,8 @@
package ecdh
import (
"github.com/cybriq/proc"
log2 "github.com/cybriq/proc/pkg/log"
)
var log = log2.GetLogger(proc.PathBase)

15
pkg/message/encryption.go Normal file
View File

@@ -0,0 +1,15 @@
package message
type Initial struct {
PubKey []byte
Nonce []byte
Message []byte
Signature []byte
}
type Subsequent struct {
Fingerprint []byte
Nonce []byte
Message []byte
Signature []byte
}

8
pkg/router/log.go Normal file
View File

@@ -0,0 +1,8 @@
package router
import (
"github.com/cybriq/proc"
log2 "github.com/cybriq/proc/pkg/log"
)
var log = log2.GetLogger(proc.PathBase)

View File

@@ -1,17 +0,0 @@
package router
import "github.com/luke-park/ecdh25519"
type Router struct {
priv *ecdh25519.PrivateKey
*ecdh25519.PublicKey
}
func New() (r *Router, err error) {
priv, err := ecdh25519.GenerateKey()
if err != nil {
return
}
r = &Router{priv: priv, PublicKey: priv.Public()}
return
}

View File

@@ -1,7 +0,0 @@
package router
import "testing"
func TestNew(t *testing.T) {
}

View File

@@ -10,11 +10,11 @@ var (
// GitRef is the gitref, as in refs/heads/branchname.
GitRef = "refs/heads/main"
// ParentGitCommit is the commit hash of the parent HEAD.
ParentGitCommit = "58afc076f1599275e82e0eb9af24c0a603d442af"
ParentGitCommit = "20fb60055de15a67fdf7ee18cde0306511f842af"
// BuildTime stores the time when the current binary was built.
BuildTime = "2022-09-29T20:50:01+02:00"
BuildTime = "2022-10-05T12:43:02+02:00"
// SemVer lists the (latest) git tag on the build.
SemVer = "v0.0.38"
SemVer = "v0.0.39"
// PathBase is the path base returned from runtime caller.
PathBase = "/home/loki/src/github.com/Indra-Labs/indra/"
// Major is the major number from the tag.
@@ -22,7 +22,7 @@ var (
// Minor is the minor number from the tag.
Minor = 0
// Patch is the patch version number from the tag.
Patch = 38
Patch = 39
)
// Version returns a pretty printed version information string.