diff --git a/demo/client.go b/demo/client.go index 70cf7e9..9e641eb 100644 --- a/demo/client.go +++ b/demo/client.go @@ -1,6 +1,7 @@ package main import ( + "flag" "fmt" hkex "blitter.com/herradurakex" @@ -13,7 +14,12 @@ import ( // encrypt/decrypt is done within the type. // Compare to 'clientp.go' in this directory to see the equivalence. func main() { - conn, err := hkex.Dial("tcp", "localhost:2000", "C_TWOFISH_128") + var cAlg string + + flag.StringVar(&cAlg, "c", "C_AES_256", "cipher [\"C_AES_256\" | \"C_TWOFISH_128\" | \"C_BLOWFISH_64\"]") + flag.Parse() + + conn, err := hkex.Dial("tcp", "localhost:2000", cAlg) if err != nil { // handle error fmt.Println("Err!") diff --git a/hkexchan.go b/hkexchan.go index 1a332c8..d9d501a 100644 --- a/hkexchan.go +++ b/hkexchan.go @@ -28,6 +28,7 @@ import ( "math/big" "os" + "golang.org/x/crypto/blowfish" "golang.org/x/crypto/twofish" ) @@ -35,6 +36,7 @@ import ( const ( C_AES_256 = iota C_TWOFISH_128 // golang.org/x/crypto/twofish + C_BLOWFISH_64 // golang.org/x/crypto/blowfish C_NONE_DISALLOWED ) @@ -52,31 +54,48 @@ been negotiated via hkexnet.go func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) { var key []byte var block cipher.Block + var ivlen int var err error copts := hc.cipheropts & 0xFF + // 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: key = keymat.Bytes()[0:aes.BlockSize] block, err = aes.NewCipher(key) + ivlen = aes.BlockSize iv := make([]byte, aes.BlockSize) - //if _, err = io.ReadFull(crand.Reader, iv); err != nil { - // panic(err) - //} - iv = keymat.Bytes()[aes.BlockSize:] + 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: key = keymat.Bytes()[0:twofish.BlockSize] block, err = twofish.NewCipher(key) + ivlen = twofish.BlockSize iv := make([]byte, twofish.BlockSize) - //if _, err = io.ReadFull(crand.Reader, iv); err != nil { - // panic(err) - //} - iv = keymat.Bytes()[twofish.BlockSize:] + iv = keymat.Bytes()[twofish.BlockSize : twofish.BlockSize+ivlen] ret = cipher.NewOFB(block, iv) - fmt.Printf("[cipher TWOFISH_256 (%d)]\n", copts) + fmt.Printf("[cipher TWOFISH_128 (%d)]\n", copts) + break + case C_BLOWFISH_64: + 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 + // pass an iv larger than the blockSize harmlessly to + // cipher.NewOFB(); x/cipher/blowfish implementation will + // segfault here if len(iv) is not exactly blowfish.BlockSize. + // + // 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] + 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) @@ -86,7 +105,7 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) { hopts := (hc.cipheropts >> 8) & 0xFF switch hopts { - case H_BOGUS: + case H_BOGUS: fmt.Printf("[nop H_BOGUS (%d)]\n", hopts) break case H_SHA256: diff --git a/hkexnet.go b/hkexnet.go index 67640c0..2354cff 100644 --- a/hkexnet.go +++ b/hkexnet.go @@ -89,6 +89,11 @@ func (hc *Conn) applyConnExtensions(extensions ...string) { hc.cipheropts &= (0xFFFFFF00) hc.cipheropts |= C_TWOFISH_128 break + case "C_BLOWFISH_64": + fmt.Println("[extension arg = C_BLOWFISH_64]") + hc.cipheropts &= (0xFFFFFF00) + hc.cipheropts |= C_BLOWFISH_64 + break case "H_SHA256": hc.cipheropts &= (0xFFFF00FF) hc.cipheropts |= (H_SHA256 << 8)