From 1817627234842bb17fd6ffce300b82f988af725e Mon Sep 17 00:00:00 2001 From: Russ Magee Date: Fri, 12 Jan 2018 22:13:01 -0800 Subject: [PATCH] -Cleaned up lib code with gometalinter.v1 -Added -h opt to demo client (hmac) --- README.md | 22 ++++++++++++- demo/client.go | 6 ++-- herradurakex.go | 45 ++++++++++---------------- hkexchan.go | 36 ++++++++------------- hkexnet.go | 86 ++++++++++++++++++++++++++++++------------------- 5 files changed, 109 insertions(+), 86 deletions(-) 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 }