Dial() extensions to specify cipher/hmac alg and protocol options

This commit is contained in:
Russ Magee 2018-01-11 19:42:42 -08:00
parent 02d4d0cd50
commit 9054bcb89f
4 changed files with 137 additions and 33 deletions

View File

@ -13,7 +13,7 @@ import (
// encrypt/decrypt is done within the type. // encrypt/decrypt is done within the type.
// 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() {
conn, err := hkex.Dial("tcp", "localhost:2000") conn, err := hkex.Dial("tcp", "localhost:2000", "C_TWOFISH_128")
if err != nil { if err != nil {
// handle error // handle error
fmt.Println("Err!") fmt.Println("Err!")

View File

@ -38,11 +38,6 @@ type HerraduraKEx struct {
fa *big.Int fa *big.Int
} }
//// Returns a new HerraduraKEx struct with default intSz,pubSz
//func New() (h *HerraduraKEx) {
// return New(256, 64)
//}
// Return a new HerraduraKEx struct. // Return a new 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)
@ -145,7 +140,6 @@ 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)
} }
// Output HerraduraKEx type value as a string. Implements Stringer interface. // Output HerraduraKEx type value as a string. Implements Stringer interface.
func (h *HerraduraKEx) String() string { func (h *HerraduraKEx) String() string {
return fmt.Sprintf("s:%d p:%d\na:%s\nb:%s\nd:->%s\n<-PeerD:%s\nfa:%s", return fmt.Sprintf("s:%d p:%d\na:%s\nb:%s\nd:->%s\n<-PeerD:%s\nfa:%s",

View File

@ -27,31 +27,35 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"os" "os"
"golang.org/x/crypto/twofish"
) )
// Available ciphers for hkex.Conn // Available ciphers for hkex.Conn
const ( const (
C_AES_256 = 0 // (TODO: config or pass during KEx Dial()/Accept()) AES-256 cipher C_AES_256 = iota
C_TWOFISH_128 // golang.org/x/crypto/twofish
C_NONE_DISALLOWED
) )
// Available HMACs for hkex.Conn (TODO: not currently used) // Available HMACs for hkex.Conn (TODO: not currently used)
const ( const (
H_SHA256 = 0 H_BOGUS = iota
H_SHA256
H_NONE_DISALLOWED
) )
/*TODO: HMAC derived from HKEx FA.*/ /*TODO: HMAC derived from HKEx FA.*/
/* Support functionality to set up encryption after a channel has /* Support functionality to set up encryption after a channel has
been negotiated via hkexnet.go been negotiated via hkexnet.go
*/ */
func (hc Conn) getStream(keymat *big.Int, flags uint32) (ret cipher.Stream) { func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
var key []byte var key []byte
var block cipher.Block var block cipher.Block
var err error var err error
// 256 algs should be enough for everybody.(tm) copts := hc.cipheropts & 0xFF
cipherAlg := (flags & 8) switch copts {
//TODO: flags for HMAC from keymat
switch cipherAlg {
case C_AES_256: case C_AES_256:
key = keymat.Bytes()[0:aes.BlockSize] key = keymat.Bytes()[0:aes.BlockSize]
block, err = aes.NewCipher(key) block, err = aes.NewCipher(key)
@ -61,13 +65,38 @@ func (hc Conn) getStream(keymat *big.Int, flags uint32) (ret cipher.Stream) {
//} //}
iv = keymat.Bytes()[aes.BlockSize:] iv = keymat.Bytes()[aes.BlockSize:]
ret = cipher.NewOFB(block, iv) ret = cipher.NewOFB(block, iv)
fmt.Printf("[cipher AES_256 (%d)]\n", copts)
break
case C_TWOFISH_128:
key = keymat.Bytes()[0:twofish.BlockSize]
block, err = twofish.NewCipher(key)
iv := make([]byte, twofish.BlockSize)
//if _, err = io.ReadFull(crand.Reader, iv); err != nil {
// panic(err)
//}
iv = keymat.Bytes()[twofish.BlockSize:]
ret = cipher.NewOFB(block, iv)
fmt.Printf("[cipher TWOFISH_256 (%d)]\n", copts)
break break
default: default:
fmt.Println("DOOFUS SET A VALID CIPHER ALG") fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts)
block, err = nil, nil block, err = nil, nil
os.Exit(1) 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)
break
default:
fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts)
os.Exit(1)
}
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -33,35 +33,111 @@ import (
// A HKex connection - drop-in replacement for net.Conn // A HKex connection - 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
r cipher.Stream cipheropts uint32 // post-KEx cipher/hmac options
w cipher.Stream opts uint32 // post-KEx protocol options
r cipher.Stream
w cipher.Stream
}
// Return 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.
//
// 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.
//
// Consumers of this lib may use this for protocol-level options not part
// of the KEx or encryption info used by the connection.
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.
// Consumers of this lib may use this for protocol-level options not part
// of the KEx of encryption info used by the connection.
//
// opts - a uint32, caller-defined
func (c *Conn) SetOpts(opts uint32) {
c.opts = opts
}
func (hc *Conn) applyConnExtensions(extensions ...string) {
for _, s := range extensions {
switch s {
case "C_AES_256":
hc.cipheropts &= (0xFFFFFF00)
hc.cipheropts |= C_AES_256
break
case "C_TWOFISH_128":
fmt.Println("[extension arg = C_TWOFISH_128]")
hc.cipheropts &= (0xFFFFFF00)
hc.cipheropts |= C_TWOFISH_128
break
case "H_SHA256":
hc.cipheropts &= (0xFFFF00FF)
hc.cipheropts |= (H_SHA256 << 8)
break
default:
fmt.Printf("[Dial ext \"%s\" ignored]\n", s)
break
}
}
} }
// Dial as net.Dial(), but with implicit HKEx PeerD read on connect // Dial as net.Dial(), but with implicit HKEx PeerD read on connect
func Dial(protocol string, ipport string) (hc *Conn, err error) { // Can be called like net.Dial(), defaulting to C_AES_256/H_SHA256,
// or additional option arguments can be passed amongst the following:
//
// "C_AES_256" | "C_TWOFISH_128"
//
// "H_SHA256"
func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err error) {
c, err := net.Dial(protocol, ipport) c, err := net.Dial(protocol, ipport)
if err != nil { if err != nil {
return nil, err return nil, err
} }
hc = &Conn{c, New(0, 0), nil, nil} hc = &Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, r: nil, w: nil}
fmt.Fprintf(c, "0x%s\n", hc.h.d.Text(16)) hc.applyConnExtensions(extensions...)
fmt.Fprintf(c, "0x%s\n%08x:%08x\n", hc.h.d.Text(16),
hc.cipheropts, hc.opts)
d := big.NewInt(0) d := big.NewInt(0)
_, err = fmt.Fscanln(c, d) _, err = fmt.Fscanln(c, d)
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, err = fmt.Fscanf(c, "%08x:%08x\n",
&hc.cipheropts, &hc.opts)
if err != nil {
return nil, err
}
hc.h.PeerD = d hc.h.PeerD = d
fmt.Printf("** D:%s\n", hc.h.d.Text(16)) fmt.Printf("** D:%s\n", hc.h.d.Text(16))
fmt.Printf("**(c)** peerD:%s\n", hc.h.PeerD.Text(16)) fmt.Printf("**(c)** peerD:%s\n", hc.h.PeerD.Text(16))
hc.h.FA() hc.h.FA()
fmt.Printf("**(c)** FA:%s\n", hc.h.fa) fmt.Printf("**(c)** FA:%s\n", hc.h.fa)
hc.r = hc.getStream(hc.h.fa, 0x0) hc.r = hc.getStream(hc.h.fa)
hc.w = hc.getStream(hc.h.fa, 0x0) hc.w = hc.getStream(hc.h.fa)
return return
} }
@ -99,17 +175,22 @@ func (hl *HKExListener) Close() {
// Accept a client connection, conforming to net.Listener.Accept() // Accept a client connection, conforming to 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()
fmt.Println("[Accepted]")
if err != nil { if err != nil {
return Conn{nil, nil, nil, nil}, err return Conn{c: nil, h: nil, cipheropts: 0, opts: 0,
r: nil, w: nil}, err
} }
hc = Conn{c: c, h: New(0, 0), r: nil, w: nil} fmt.Println("[Accepted]")
hc = Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, r: nil, w: nil}
d := big.NewInt(0) d := big.NewInt(0)
_, err = fmt.Fscanln(c, d) _, err = fmt.Fscanln(c, d)
if err != nil { if err != nil {
fmt.Println("[Error]") return hc, err
}
_, err = fmt.Fscanf(c, "%08x:%08x\n",
&hc.cipheropts, &hc.opts)
if err != nil {
return hc, err return hc, err
} }
hc.h.PeerD = d hc.h.PeerD = d
@ -118,10 +199,11 @@ func (hl *HKExListener) Accept() (hc Conn, err error) {
hc.h.FA() hc.h.FA()
fmt.Printf("**(s)** FA:%s\n", hc.h.fa) fmt.Printf("**(s)** FA:%s\n", hc.h.fa)
fmt.Fprintf(c, "0x%s\n", hc.h.d.Text(16)) fmt.Fprintf(c, "0x%s\n%08x:%08x\n", hc.h.d.Text(16),
hc.cipheropts, hc.opts)
hc.r = hc.getStream(hc.h.fa, 0x0) hc.r = hc.getStream(hc.h.fa)
hc.w = hc.getStream(hc.h.fa, 0x0) hc.w = hc.getStream(hc.h.fa)
return return
} }
@ -176,4 +258,3 @@ func (hc Conn) Write(b []byte) (n int, err error) {
return return
} }
*/ */