-Cleaned up lib code with gometalinter.v1

-Added -h opt to demo client (hmac)
This commit is contained in:
Russ Magee 2018-01-12 22:13:01 -08:00
parent 5493921e9f
commit 1817627234
5 changed files with 109 additions and 86 deletions

View file

@ -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 <https://www.gnu.org/licenses/>.
golang implementation by Russ Magee (rmagee_at_gmail.com) */
--
This is a drop-in replacement for the golang/pkg/net facilities This is a drop-in replacement for the golang/pkg/net facilities
(net.Dial(), net.Listen(), net.Accept() and net.Conn type) using the (net.Dial(), net.Listen(), net.Accept() and net.Conn type) using the
experimental HerraduraKEx 'secure' key exchange algorithm, first released at experimental HerraduraKEx 'secure' key exchange algorithm, first released at
@ -28,6 +48,6 @@ $ go install .
$ cd demo/ $ cd demo/
$ go build client.go && go build server.go $ go build client.go && go build server.go
[ in separate shell windows ] [ in separate shells ]
[A]$ ./server [A]$ ./server
[B]$ ./client [B]$ ./client

View file

@ -15,11 +15,13 @@ import (
// Compare to 'clientp.go' in this directory to see the equivalence. // Compare to 'clientp.go' in this directory to see the equivalence.
func main() { func main() {
var cAlg string 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(&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() flag.Parse()
conn, err := hkex.Dial("tcp", "localhost:2000", cAlg) conn, err := hkex.Dial("tcp", "localhost:2000", cAlg, hAlg)
if err != nil { if err != nil {
// handle error // handle error
fmt.Println("Err!") fmt.Println("Err!")

View file

@ -1,20 +1,10 @@
/* Herradura - a Key exchange scheme in the style of Diffie-Hellman Key Exchange. // Package herradurakex - socket lib conforming to
Copyright (C) 2017 Omar Alejandro Herrera Reyna // golang.org/pkg/net Conn interface, with
// experimental key exchange algorithm by
This program is free software: you can redistribute it and/or modify // Omar Alejandro Herrera Reyna
it under the terms of the GNU General Public License as published by // (https://github.com/Caume/HerraduraKEx)
the Free Software Foundation, either version 3 of the License, or //
(at your option) any later version. // See README.md for full license info.
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 <https://www.gnu.org/licenses/>.
golang implementation by Russ Magee (rmagee_at_gmail.com) */
package herradurakex package herradurakex
/* This is the core KEx algorithm. For client/server net support code, /* This is the core KEx algorithm. For client/server net support code,
@ -38,7 +28,8 @@ type HerraduraKEx struct {
fa *big.Int fa *big.Int
} }
// Return a new HerraduraKEx struct. // New returns a HerraduraKEx struct.
//
// i - internal (private) random nonce // i - internal (private) random nonce
// p - public (exchanged) random nonce (typically 1/4 bitsize of i) // 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 v
} }
// Return max value for an n-bit big.Int // getMax returns the max value for an n-bit big.Int
func (h *HerraduraKEx) getMax() (v *big.Int) { func (h *HerraduraKEx) getMax() (n *big.Int) {
v = big.NewInt(0) n = big.NewInt(0)
var max big.Int var max big.Int
for i := 0; i < h.intSz; i++ { for i := 0; i < h.intSz; i++ {
max.SetBit(v, i, 1) max.SetBit(n, i, 1)
} }
v = &max n = &max
return v return n
} }
func (h *HerraduraKEx) bitX(x *big.Int, pos int) (ret int64) { 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 // This is the iteration function using the result of the previous iteration
// as the first parameter and the second parameter of the first 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) { func (h *HerraduraKEx) fscxRevolve(x, y *big.Int, passes int) (result *big.Int) {
result = big.NewInt(0)
result = x result = x
for count := 0; count < passes; count++ { for count := 0; count < passes; count++ {
result = h.fscx(result, y) 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 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) // (the value for peer KEx)
func (h *HerraduraKEx) D() *big.Int { func (h *HerraduraKEx) D() *big.Int {
return h.d 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() { func (h *HerraduraKEx) FA() {
h.fa = h.fscxRevolve(h.PeerD, h.b, h.intSz-h.pubSz) h.fa = h.fscxRevolve(h.PeerD, h.b, h.intSz-h.pubSz)
h.fa.Xor(h.fa, h.a) h.fa.Xor(h.fa, h.a)

View file

@ -34,17 +34,16 @@ import (
// Available ciphers for hkex.Conn // Available ciphers for hkex.Conn
const ( const (
C_AES_256 = iota CAlgAES256 = iota
C_TWOFISH_128 // golang.org/x/crypto/twofish CAlgTwofish128 // golang.org/x/crypto/twofish
C_BLOWFISH_64 // golang.org/x/crypto/blowfish CAlgBlowfish64 // golang.org/x/crypto/blowfish
C_NONE_DISALLOWED CAlgNoneDisallowed
) )
// Available HMACs for hkex.Conn (TODO: not currently used) // Available HMACs for hkex.Conn (TODO: not currently used)
const ( const (
H_BOGUS = iota HmacSHA256 = iota
H_SHA256 HmacNoneDisallowed
H_NONE_DISALLOWED
) )
/*TODO: HMAC derived from HKEx FA.*/ /*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()) // TODO: each cipher alg case should ensure len(keymat.Bytes())
// is >= 2*cipher.BlockSize (enough for both key and iv) // is >= 2*cipher.BlockSize (enough for both key and iv)
switch copts { switch copts {
case C_AES_256: case CAlgAES256:
key = keymat.Bytes()[0:aes.BlockSize] key = keymat.Bytes()[0:aes.BlockSize]
block, err = aes.NewCipher(key) block, err = aes.NewCipher(key)
ivlen = aes.BlockSize 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) ret = cipher.NewOFB(block, iv)
fmt.Printf("[cipher AES_256 (%d)]\n", copts) fmt.Printf("[cipher AES_256 (%d)]\n", copts)
break break
case C_TWOFISH_128: case CAlgTwofish128:
key = keymat.Bytes()[0:twofish.BlockSize] key = keymat.Bytes()[0:twofish.BlockSize]
block, err = twofish.NewCipher(key) block, err = twofish.NewCipher(key)
ivlen = twofish.BlockSize 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) ret = cipher.NewOFB(block, iv)
fmt.Printf("[cipher TWOFISH_128 (%d)]\n", copts) fmt.Printf("[cipher TWOFISH_128 (%d)]\n", copts)
break break
case C_BLOWFISH_64: case CAlgBlowfish64:
key = keymat.Bytes()[0:blowfish.BlockSize] key = keymat.Bytes()[0:blowfish.BlockSize]
block, err = blowfish.NewCipher(key) block, err = blowfish.NewCipher(key)
ivlen = blowfish.BlockSize ivlen = blowfish.BlockSize
iv := make([]byte, blowfish.BlockSize)
// N.b. Bounds enforcement of differing cipher algorithms // N.b. Bounds enforcement of differing cipher algorithms
// ------------------------------------------------------ // ------------------------------------------------------
// cipher/aes and x/cipher/twofish appear to allow one to // 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 // I assume the other two check bounds and only
// copy what's needed whereas blowfish does no such check. // 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) ret = cipher.NewOFB(block, iv)
fmt.Printf("[cipher BLOWFISH_64 (%d)]\n", copts) fmt.Printf("[cipher BLOWFISH_64 (%d)]\n", copts)
break break
default: default:
fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts) fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts)
block, err = nil, nil
os.Exit(1) os.Exit(1)
} }
hopts := (hc.cipheropts >> 8) & 0xFF hopts := (hc.cipheropts >> 8) & 0xFF
switch hopts { switch hopts {
case H_BOGUS: case HmacSHA256:
fmt.Printf("[nop H_BOGUS (%d)]\n", hopts) fmt.Printf("[nop HmacSHA256 (%d)]\n", hopts)
break
case H_SHA256:
fmt.Printf("[nop H_SHA256 (%d)]\n", hopts)
break break
default: default:
fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts) fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts)

View file

@ -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 { type Conn struct {
c net.Conn // which also implements io.Reader, io.Writer, ... c net.Conn // which also implements io.Reader, io.Writer, ...
h *HerraduraKEx h *HerraduraKEx
@ -41,24 +41,25 @@ type Conn struct {
w cipher.Stream w cipher.Stream
} }
// Return the cipher/hmac options value, which is sent to the peer but is // ConnOpts returns the cipher/hmac options value, which is sent to the
// not itself part of the KEx. // peer but is not itself part of the KEx.
//
// (Used for protocol-level negotiations after KEx such as // (Used for protocol-level negotiations after KEx such as
// cipher/HMAC algorithm options etc.) // cipher/HMAC algorithm options etc.)
func (c *Conn) ConnOpts() uint32 { func (c *Conn) ConnOpts() uint32 {
return c.cipheropts return c.cipheropts
} }
// Set cipher/hmac options value, which is sent to the peer as part of // SetConnOpts sets the cipher/hmac options value, which is sent to the
// KEx but not part of the KEx itself. // peer as part of KEx but not part of the KEx itself.
// //
// opts - bitfields for cipher and hmac alg. to use after KEx // opts - bitfields for cipher and hmac alg. to use after KEx
func (c *Conn) SetConnOpts(copts uint32) { func (c *Conn) SetConnOpts(copts uint32) {
c.cipheropts = copts c.cipheropts = copts
} }
// Return the protocol options value, which is sent to the peer but is // Opts returns the protocol options value, which is sent to the peer
// not itself part of the KEx or connection (cipher/hmac) setup. // 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 // Consumers of this lib may use this for protocol-level options not part
// of the KEx or encryption info used by the connection. // of the KEx or encryption info used by the connection.
@ -66,9 +67,9 @@ func (c *Conn) Opts() uint32 {
return c.opts return c.opts
} }
// Set the protocol options value, which is sent to the peer but is // SetOpts sets the protocol options value, which is sent to the peer
// not itself part of the KEx or connection (cipher/hmac) setup. // 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 // Consumers of this lib may use this for protocol-level options not part
// of the KEx of encryption info used by the connection. // of the KEx of encryption info used by the connection.
// //
@ -77,26 +78,28 @@ func (c *Conn) SetOpts(opts uint32) {
c.opts = opts c.opts = opts
} }
func (hc *Conn) applyConnExtensions(extensions ...string) { func (c *Conn) applyConnExtensions(extensions ...string) {
for _, s := range extensions { for _, s := range extensions {
switch s { switch s {
case "C_AES_256": case "C_AES_256":
hc.cipheropts &= (0xFFFFFF00) fmt.Println("[extension arg = C_AES_256]")
hc.cipheropts |= C_AES_256 c.cipheropts &= (0xFFFFFF00)
c.cipheropts |= CAlgAES256
break break
case "C_TWOFISH_128": case "C_TWOFISH_128":
fmt.Println("[extension arg = C_TWOFISH_128]") fmt.Println("[extension arg = C_TWOFISH_128]")
hc.cipheropts &= (0xFFFFFF00) c.cipheropts &= (0xFFFFFF00)
hc.cipheropts |= C_TWOFISH_128 c.cipheropts |= CAlgTwofish128
break break
case "C_BLOWFISH_64": case "C_BLOWFISH_64":
fmt.Println("[extension arg = C_BLOWFISH_64]") fmt.Println("[extension arg = C_BLOWFISH_64]")
hc.cipheropts &= (0xFFFFFF00) c.cipheropts &= (0xFFFFFF00)
hc.cipheropts |= C_BLOWFISH_64 c.cipheropts |= CAlgBlowfish64
break break
case "H_SHA256": case "H_SHA256":
hc.cipheropts &= (0xFFFF00FF) fmt.Println("[extension arg = H_SHA256]")
hc.cipheropts |= (H_SHA256 << 8) c.cipheropts &= (0xFFFF00FF)
c.cipheropts |= (HmacSHA256 << 8)
break break
default: default:
fmt.Printf("[Dial ext \"%s\" ignored]\n", s) 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 // Close a hkex.Conn
func (hc *Conn) Close() (err error) { func (c *Conn) Close() (err error) {
err = hc.c.Close() err = c.c.Close()
fmt.Println("[Conn Closing]") fmt.Println("[Conn Closing]")
return 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 { type HKExListener struct {
l net.Listener 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) { func Listen(protocol string, ipport string) (hl HKExListener, e error) {
l, err := net.Listen(protocol, ipport) l, err := net.Listen(protocol, ipport)
if err != nil { if err != nil {
@ -172,12 +179,16 @@ func Listen(protocol string, ipport string) (hl HKExListener, e error) {
} }
// Close a hkex Listener // 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]") fmt.Println("[Listener Closed]")
return hl.l.Close()
} }
// Accept a client connection, conforming to net.Listener.Accept() // Accept a client connection, conforming to net.Listener.Accept()
//
// See go doc net.Listener.Accept
func (hl *HKExListener) Accept() (hc Conn, err error) { func (hl *HKExListener) Accept() (hc Conn, err error) {
c, err := hl.l.Accept() c, err := hl.l.Accept()
if err != nil { if err != nil {
@ -211,11 +222,14 @@ func (hl *HKExListener) Accept() (hc Conn, err error) {
hc.w = hc.getStream(hc.h.fa) hc.w = hc.getStream(hc.h.fa)
return 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") fmt.Printf("[Decrypting...]\n")
n, err = hc.c.Read(b) n, err = c.c.Read(b)
if err != nil && err.Error() != "EOF" { if err != nil && err.Error() != "EOF" {
panic(err) panic(err)
} }
@ -224,22 +238,28 @@ func (hc Conn) Read(b []byte) (n int, err error) {
// The StreamReader acts like a pipe, decrypting // The StreamReader acts like a pipe, decrypting
// whatever is available and forwarding the result // whatever is available and forwarding the result
// to the parameter of Read() as a normal io.Reader // 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) n, err = rs.Read(b)
fmt.Printf(" ptext:%+v\n", b[:n]) fmt.Printf(" ptext:%+v\n", b[:n])
return 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("[Encrypting...]\n")
fmt.Printf(" ptext:%+v\n", b) fmt.Printf(" ptext:%+v\n", b)
var wb bytes.Buffer var wb bytes.Buffer
// The StreamWriter acts like a pipe, forwarding whatever is // The StreamWriter acts like a pipe, forwarding whatever is
// written to it through the cipher, encrypting as it goes // written to it through the cipher, encrypting as it goes
ws := &cipher.StreamWriter{S: hc.w, W: &wb} ws := &cipher.StreamWriter{S: c.w, W: &wb}
n, err = ws.Write(b) _, err = ws.Write(b)
if err != nil {
panic(err)
}
fmt.Printf(" ctext:%+v\n", wb.Bytes()) fmt.Printf(" ctext:%+v\n", wb.Bytes())
n, err = hc.c.Write(wb.Bytes()) n, err = c.c.Write(wb.Bytes())
return return
} }