diff --git a/README.md b/README.md
index 4ed965c..dcaeda5 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,23 @@
+/* Herradura - a Key exchange scheme in the style of Diffie-Hellman Key Exchange.
+ Copyright (C) 2017 Omar Alejandro Herrera Reyna
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ golang implementation by Russ Magee (rmagee_at_gmail.com) */
+
+--
+
This is a drop-in replacement for the golang/pkg/net facilities
(net.Dial(), net.Listen(), net.Accept() and net.Conn type) using the
experimental HerraduraKEx 'secure' key exchange algorithm, first released at
@@ -28,6 +48,6 @@ $ go install .
$ cd demo/
$ go build client.go && go build server.go
-[ in separate shell windows ]
+[ in separate shells ]
[A]$ ./server
[B]$ ./client
diff --git a/demo/client.go b/demo/client.go
index 9e641eb..8ca6bca 100644
--- a/demo/client.go
+++ b/demo/client.go
@@ -15,11 +15,13 @@ import (
// Compare to 'clientp.go' in this directory to see the equivalence.
func main() {
var cAlg string
-
+ var hAlg string
+
flag.StringVar(&cAlg, "c", "C_AES_256", "cipher [\"C_AES_256\" | \"C_TWOFISH_128\" | \"C_BLOWFISH_64\"]")
+ flag.StringVar(&hAlg, "h", "H_SHA256", "hmac [\"H_SHA256\"]")
flag.Parse()
- conn, err := hkex.Dial("tcp", "localhost:2000", cAlg)
+ conn, err := hkex.Dial("tcp", "localhost:2000", cAlg, hAlg)
if err != nil {
// handle error
fmt.Println("Err!")
diff --git a/herradurakex.go b/herradurakex.go
index 4017055..00c9db5 100644
--- a/herradurakex.go
+++ b/herradurakex.go
@@ -1,20 +1,10 @@
-/* Herradura - a Key exchange scheme in the style of Diffie-Hellman Key Exchange.
- Copyright (C) 2017 Omar Alejandro Herrera Reyna
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
- golang implementation by Russ Magee (rmagee_at_gmail.com) */
+// Package herradurakex - socket lib conforming to
+// golang.org/pkg/net Conn interface, with
+// experimental key exchange algorithm by
+// Omar Alejandro Herrera Reyna
+// (https://github.com/Caume/HerraduraKEx)
+//
+// See README.md for full license info.
package herradurakex
/* This is the core KEx algorithm. For client/server net support code,
@@ -38,7 +28,8 @@ type HerraduraKEx struct {
fa *big.Int
}
-// Return a new HerraduraKEx struct.
+// New returns a HerraduraKEx struct.
+//
// i - internal (private) random nonce
// p - public (exchanged) random nonce (typically 1/4 bitsize of i)
//
@@ -76,16 +67,16 @@ func (h *HerraduraKEx) rand() (v *big.Int) {
return v
}
-// Return max value for an n-bit big.Int
-func (h *HerraduraKEx) getMax() (v *big.Int) {
- v = big.NewInt(0)
+// getMax returns the max value for an n-bit big.Int
+func (h *HerraduraKEx) getMax() (n *big.Int) {
+ n = big.NewInt(0)
var max big.Int
for i := 0; i < h.intSz; i++ {
- max.SetBit(v, i, 1)
+ max.SetBit(n, i, 1)
}
- v = &max
- return v
+ n = &max
+ return n
}
func (h *HerraduraKEx) bitX(x *big.Int, pos int) (ret int64) {
@@ -120,8 +111,6 @@ func (h *HerraduraKEx) fscx(up, down *big.Int) (result *big.Int) {
// This is the iteration function using the result of the previous iteration
// as the first parameter and the second parameter of the first iteration.
func (h *HerraduraKEx) fscxRevolve(x, y *big.Int, passes int) (result *big.Int) {
- result = big.NewInt(0)
-
result = x
for count := 0; count < passes; count++ {
result = h.fscx(result, y)
@@ -129,13 +118,13 @@ func (h *HerraduraKEx) fscxRevolve(x, y *big.Int, passes int) (result *big.Int)
return result
}
-// Return the D (FSCX Revolved) value, input to generate FA
+// D returns the D (FSCX Revolved) value, input to generate FA
// (the value for peer KEx)
func (h *HerraduraKEx) D() *big.Int {
return h.d
}
-// Return the FA value, which must be sent to peer for KEx.
+// FA returns the FA value, which must be sent to peer for KEx.
func (h *HerraduraKEx) FA() {
h.fa = h.fscxRevolve(h.PeerD, h.b, h.intSz-h.pubSz)
h.fa.Xor(h.fa, h.a)
diff --git a/hkexchan.go b/hkexchan.go
index d9d501a..7faa296 100644
--- a/hkexchan.go
+++ b/hkexchan.go
@@ -34,17 +34,16 @@ import (
// Available ciphers for hkex.Conn
const (
- C_AES_256 = iota
- C_TWOFISH_128 // golang.org/x/crypto/twofish
- C_BLOWFISH_64 // golang.org/x/crypto/blowfish
- C_NONE_DISALLOWED
+ CAlgAES256 = iota
+ CAlgTwofish128 // golang.org/x/crypto/twofish
+ CAlgBlowfish64 // golang.org/x/crypto/blowfish
+ CAlgNoneDisallowed
)
// Available HMACs for hkex.Conn (TODO: not currently used)
const (
- H_BOGUS = iota
- H_SHA256
- H_NONE_DISALLOWED
+ HmacSHA256 = iota
+ HmacNoneDisallowed
)
/*TODO: HMAC derived from HKEx FA.*/
@@ -61,29 +60,26 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
// TODO: each cipher alg case should ensure len(keymat.Bytes())
// is >= 2*cipher.BlockSize (enough for both key and iv)
switch copts {
- case C_AES_256:
+ case CAlgAES256:
key = keymat.Bytes()[0:aes.BlockSize]
block, err = aes.NewCipher(key)
ivlen = aes.BlockSize
- iv := make([]byte, aes.BlockSize)
- iv = keymat.Bytes()[aes.BlockSize : aes.BlockSize+ivlen]
+ iv := keymat.Bytes()[aes.BlockSize : aes.BlockSize+ivlen]
ret = cipher.NewOFB(block, iv)
fmt.Printf("[cipher AES_256 (%d)]\n", copts)
break
- case C_TWOFISH_128:
+ case CAlgTwofish128:
key = keymat.Bytes()[0:twofish.BlockSize]
block, err = twofish.NewCipher(key)
ivlen = twofish.BlockSize
- iv := make([]byte, twofish.BlockSize)
- iv = keymat.Bytes()[twofish.BlockSize : twofish.BlockSize+ivlen]
+ iv := keymat.Bytes()[twofish.BlockSize : twofish.BlockSize+ivlen]
ret = cipher.NewOFB(block, iv)
fmt.Printf("[cipher TWOFISH_128 (%d)]\n", copts)
break
- case C_BLOWFISH_64:
+ case CAlgBlowfish64:
key = keymat.Bytes()[0:blowfish.BlockSize]
block, err = blowfish.NewCipher(key)
ivlen = blowfish.BlockSize
- iv := make([]byte, blowfish.BlockSize)
// N.b. Bounds enforcement of differing cipher algorithms
// ------------------------------------------------------
// cipher/aes and x/cipher/twofish appear to allow one to
@@ -93,23 +89,19 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
//
// I assume the other two check bounds and only
// copy what's needed whereas blowfish does no such check.
- iv = keymat.Bytes()[blowfish.BlockSize : blowfish.BlockSize+ivlen]
+ iv := keymat.Bytes()[blowfish.BlockSize : blowfish.BlockSize+ivlen]
ret = cipher.NewOFB(block, iv)
fmt.Printf("[cipher BLOWFISH_64 (%d)]\n", copts)
break
default:
fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts)
- block, err = nil, nil
os.Exit(1)
}
hopts := (hc.cipheropts >> 8) & 0xFF
switch hopts {
- case H_BOGUS:
- fmt.Printf("[nop H_BOGUS (%d)]\n", hopts)
- break
- case H_SHA256:
- fmt.Printf("[nop H_SHA256 (%d)]\n", hopts)
+ case HmacSHA256:
+ fmt.Printf("[nop HmacSHA256 (%d)]\n", hopts)
break
default:
fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts)
diff --git a/hkexnet.go b/hkexnet.go
index 2354cff..a37092e 100644
--- a/hkexnet.go
+++ b/hkexnet.go
@@ -31,7 +31,7 @@ import (
/*---------------------------------------------------------------------*/
-// A HKex connection - drop-in replacement for net.Conn
+// Conn is a HKex connection - a drop-in replacement for net.Conn
type Conn struct {
c net.Conn // which also implements io.Reader, io.Writer, ...
h *HerraduraKEx
@@ -41,24 +41,25 @@ type Conn struct {
w cipher.Stream
}
-// Return the cipher/hmac options value, which is sent to the peer but is
-// not itself part of the KEx.
+// ConnOpts returns the cipher/hmac options value, which is sent to the
+// peer but is not itself part of the KEx.
+//
// (Used for protocol-level negotiations after KEx such as
// cipher/HMAC algorithm options etc.)
func (c *Conn) ConnOpts() uint32 {
return c.cipheropts
}
-// Set cipher/hmac options value, which is sent to the peer as part of
-// KEx but not part of the KEx itself.
+// SetConnOpts sets the cipher/hmac options value, which is sent to the
+// peer as part of KEx but not part of the KEx itself.
//
// opts - bitfields for cipher and hmac alg. to use after KEx
func (c *Conn) SetConnOpts(copts uint32) {
c.cipheropts = copts
}
-// Return the protocol options value, which is sent to the peer but is
-// not itself part of the KEx or connection (cipher/hmac) setup.
+// Opts returns the protocol options value, which is sent to the peer
+// but is not itself part of the KEx or connection (cipher/hmac) setup.
//
// Consumers of this lib may use this for protocol-level options not part
// of the KEx or encryption info used by the connection.
@@ -66,9 +67,9 @@ func (c *Conn) Opts() uint32 {
return c.opts
}
-// Set the protocol options value, which is sent to the peer but is
-// not itself part of the KEx or connection (cipher/hmac) setup.
-
+// SetOpts sets the protocol options value, which is sent to the peer
+// but is not itself part of the KEx or connection (cipher/hmac) setup.
+//
// Consumers of this lib may use this for protocol-level options not part
// of the KEx of encryption info used by the connection.
//
@@ -77,26 +78,28 @@ func (c *Conn) SetOpts(opts uint32) {
c.opts = opts
}
-func (hc *Conn) applyConnExtensions(extensions ...string) {
+func (c *Conn) applyConnExtensions(extensions ...string) {
for _, s := range extensions {
switch s {
case "C_AES_256":
- hc.cipheropts &= (0xFFFFFF00)
- hc.cipheropts |= C_AES_256
+ fmt.Println("[extension arg = C_AES_256]")
+ c.cipheropts &= (0xFFFFFF00)
+ c.cipheropts |= CAlgAES256
break
case "C_TWOFISH_128":
fmt.Println("[extension arg = C_TWOFISH_128]")
- hc.cipheropts &= (0xFFFFFF00)
- hc.cipheropts |= C_TWOFISH_128
+ c.cipheropts &= (0xFFFFFF00)
+ c.cipheropts |= CAlgTwofish128
break
case "C_BLOWFISH_64":
fmt.Println("[extension arg = C_BLOWFISH_64]")
- hc.cipheropts &= (0xFFFFFF00)
- hc.cipheropts |= C_BLOWFISH_64
+ c.cipheropts &= (0xFFFFFF00)
+ c.cipheropts |= CAlgBlowfish64
break
case "H_SHA256":
- hc.cipheropts &= (0xFFFF00FF)
- hc.cipheropts |= (H_SHA256 << 8)
+ fmt.Println("[extension arg = H_SHA256]")
+ c.cipheropts &= (0xFFFF00FF)
+ c.cipheropts |= (HmacSHA256 << 8)
break
default:
fmt.Printf("[Dial ext \"%s\" ignored]\n", s)
@@ -147,20 +150,24 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e
}
// Close a hkex.Conn
-func (hc *Conn) Close() (err error) {
- err = hc.c.Close()
+func (c *Conn) Close() (err error) {
+ err = c.c.Close()
fmt.Println("[Conn Closing]")
return
}
/*---------------------------------------------------------------------*/
-// A hkex Listener, conforming to net.Listener - returns a hkex.Conn
+// HKExListener is a Listener conforming to net.Listener
+//
+// See go doc net.Listener
type HKExListener struct {
l net.Listener
}
-// hkex.Listen, a drop-in replacement for net.Conn.Listen
+// Listen for a connection
+//
+// See go doc net.Listen
func Listen(protocol string, ipport string) (hl HKExListener, e error) {
l, err := net.Listen(protocol, ipport)
if err != nil {
@@ -172,12 +179,16 @@ func Listen(protocol string, ipport string) (hl HKExListener, e error) {
}
// Close a hkex Listener
-func (hl *HKExListener) Close() {
- hl.l.Close()
+//
+// See go doc io.Close
+func (hl *HKExListener) Close() error {
fmt.Println("[Listener Closed]")
+ return hl.l.Close()
}
// Accept a client connection, conforming to net.Listener.Accept()
+//
+// See go doc net.Listener.Accept
func (hl *HKExListener) Accept() (hc Conn, err error) {
c, err := hl.l.Accept()
if err != nil {
@@ -211,11 +222,14 @@ func (hl *HKExListener) Accept() (hc Conn, err error) {
hc.w = hc.getStream(hc.h.fa)
return
}
-
/*---------------------------------------------------------------------*/
-func (hc Conn) Read(b []byte) (n int, err error) {
+
+// Read into a byte slice
+//
+// See go doc io.Reader
+func (c Conn) Read(b []byte) (n int, err error) {
fmt.Printf("[Decrypting...]\n")
- n, err = hc.c.Read(b)
+ n, err = c.c.Read(b)
if err != nil && err.Error() != "EOF" {
panic(err)
}
@@ -224,22 +238,28 @@ func (hc Conn) Read(b []byte) (n int, err error) {
// The StreamReader acts like a pipe, decrypting
// whatever is available and forwarding the result
// to the parameter of Read() as a normal io.Reader
- rs := &cipher.StreamReader{S: hc.r, R: db}
+ rs := &cipher.StreamReader{S: c.r, R: db}
n, err = rs.Read(b)
fmt.Printf(" ptext:%+v\n", b[:n])
return
}
-func (hc Conn) Write(b []byte) (n int, err error) {
+// Write a byte slice
+//
+// See go doc io.Writer
+func (c Conn) Write(b []byte) (n int, err error) {
fmt.Printf("[Encrypting...]\n")
fmt.Printf(" ptext:%+v\n", b)
var wb bytes.Buffer
// The StreamWriter acts like a pipe, forwarding whatever is
// written to it through the cipher, encrypting as it goes
- ws := &cipher.StreamWriter{S: hc.w, W: &wb}
- n, err = ws.Write(b)
+ ws := &cipher.StreamWriter{S: c.w, W: &wb}
+ _, err = ws.Write(b)
+ if err != nil {
+ panic(err)
+ }
fmt.Printf(" ctext:%+v\n", wb.Bytes())
- n, err = hc.c.Write(wb.Bytes())
+ n, err = c.c.Write(wb.Bytes())
return
}