Skip to content

Instantly share code, notes, and snippets.

@nickvanw
Created August 18, 2014 00:13
Show Gist options
  • Save nickvanw/96f739a68f36c0b29b9c to your computer and use it in GitHub Desktop.
Save nickvanw/96f739a68f36c0b29b9c to your computer and use it in GitHub Desktop.
FiSH DH1080 in Go
package main
import (
"crypto/cipher"
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"errors"
"fmt"
"math/big"
"strings"
"code.google.com/p/go.crypto/blowfish"
"github.com/monnand/dhkx"
)
const DH1080_PRIME = "FBE1022E23D213E8ACFA9AE8B9DFADA3EA6B7AC7A7B7E95AB5EB2DF858921FEADE95E6AC7BE7DE6ADBAB8A783E7AF7A7FA6A2B7BEB1E72EAE2B72F9FA2BFB2A2EFBEFAC868BADB3E828FA8BADFADA3E4CC1BE7E8AFE85E9698A783EB68FA07A77AB6AD7BEB618ACF9CA2897EB28A6189EFA07AB99A8A7FA9AE299EFA7BA66DEAFEFBEFBF0B7D8B"
func main() {
alice := DH1080_Init()
bob := DH1080_Init()
aliceData, err := alice.Pack()
if err != nil {
fmt.Println(err)
return
}
err = bob.Unpack(aliceData)
if err != nil {
fmt.Println(err)
return
}
bobData, err := bob.Pack()
if err != nil {
fmt.Println(err)
return
}
err = alice.Unpack(bobData)
if err != nil {
fmt.Println(err)
return
}
bobSecret, err := bob.GetSecret()
if err != nil {
fmt.Println(err)
return
}
aliceSecret, err := alice.GetSecret()
if err != nil {
fmt.Println(err)
return
}
testPhrase := blowfishChecksizeAndPad([]byte("This is a test"))
fmt.Println("Encoding:", string(testPhrase))
enc := blowfishEncrypt(testPhrase, []byte(bobSecret))
fmt.Println("Encoded Value:", enc)
dec := blowfishDecrypt(enc, []byte(aliceSecret))
fmt.Println("Decoded:", string(dec))
}
type DH1080 struct {
Public []byte
Private *dhkx.DHKey
State int
Secret *dhkx.DHKey
Group *dhkx.DHGroup
}
func DH1080_Init() *DH1080 {
DH1080Ctx := &DH1080{}
p, _ := new(big.Int).SetString(DH1080_PRIME, 16)
group := dhkx.CreateGroup(p, new(big.Int).SetInt64(2))
priv, _ := group.GeneratePrivateKey(rand.Reader)
pub := priv.Bytes()
DH1080Ctx.Public = pub
DH1080Ctx.Private = priv
DH1080Ctx.State = 0
DH1080Ctx.Group = group
return DH1080Ctx
}
func (dh *DH1080) Pack() (string, error) {
var cmd string
if dh.State == 0 {
dh.State = 1
cmd = "DH1080_INIT "
} else {
cmd = "DH1080_FINISH "
}
data, err := DH1080_Base64Encode(dh.Public)
if err != nil {
return "", err
}
return cmd + data, nil
}
func (dh *DH1080) GetSecret() (string, error) {
if dh.Secret == nil {
return "", errors.New("No secret to encode")
}
data := []byte(dh.Secret.String())
hasher := sha1.New()
hasher.Write(data)
return DH1080_Base64Encode(hasher.Sum(nil))
}
func (dh *DH1080) Unpack(msg string) error {
if !strings.HasPrefix(msg, "DH1080_") {
return errors.New("Invalid Message")
}
data := strings.Split(msg, " ")
keyData := data[1]
decodedKeyData, _ := DH1080_Base64Decode(keyData)
otherPubKey := dhkx.NewPublicKey(decodedKeyData)
key, err := dh.Group.ComputeKey(otherPubKey, dh.Private)
if err != nil {
return err
}
dh.Secret = key
return nil
}
func DH1080_Base64Encode(data []byte) (string, error) {
if len(data) < 1 {
return "", errors.New("Zero Length String")
}
encodedString := base64.StdEncoding.EncodeToString(data)
if !strings.Contains(encodedString, "=") {
encodedString += "A"
} else {
encodedString = strings.TrimRight(encodedString, "=")
}
return encodedString, nil
}
func DH1080_Base64Decode(data string) ([]byte, error) {
if len(data)%4 == 1 && data[len(data)-1] == 'A' {
return base64.StdEncoding.DecodeString(string(data[:len(data)-1]))
}
data = data + strings.Repeat("=", (4-(len(data)%4)))
return base64.StdEncoding.DecodeString(data)
}
func blowfishChecksizeAndPad(pt []byte) []byte {
// calculate modulus of plaintext to blowfish's cipher block size
// if result is not 0, then we need to pad
modulus := len(pt) % blowfish.BlockSize
if modulus != 0 {
// how many bytes do we need to pad to make pt to be a multiple of blowfish's block size?
padlen := blowfish.BlockSize - modulus
// let's add the required padding
for i := 0; i < padlen; i++ {
// add the pad, one at a time
pt = append(pt, 0)
}
}
// return the whole-multiple-of-blowfish.BlockSize-sized plaintext to the calling function
return pt
}
func blowfishDecrypt(et, key []byte) []byte {
// create the cipher
dcipher, err := blowfish.NewCipher(key)
if err != nil {
// fix this. its okay for this tester program, but...
panic(err)
}
// make initialisation vector to be the first 8 bytes of ciphertext.
// see related note in blowfishEncrypt()
div := et[:blowfish.BlockSize]
// check last slice of encrypted text, if it's not a modulus of cipher block size, we're in trouble
decrypted := et[blowfish.BlockSize:]
if len(decrypted)%blowfish.BlockSize != 0 {
panic("decrypted is not a multiple of blowfish.BlockSize")
}
// ok, we're good... create the decrypter
dcbc := cipher.NewCBCDecrypter(dcipher, div)
// decrypt!
dcbc.CryptBlocks(decrypted, decrypted)
return decrypted
}
func blowfishEncrypt(ppt, key []byte) []byte {
// create the cipher
ecipher, err := blowfish.NewCipher(key)
if err != nil {
// fix this. its okay for this tester program, but ....
panic(err)
}
// make ciphertext big enough to store len(ppt)+blowfish.BlockSize
ciphertext := make([]byte, blowfish.BlockSize+len(ppt))
// make initialisation vector to be the first 8 bytes of ciphertext. you
// wouldn't do this normally/in real code, but this IS example code! :)
eiv := ciphertext[:blowfish.BlockSize]
// create the encrypter
ecbc := cipher.NewCBCEncrypter(ecipher, eiv)
// encrypt the blocks, because block cipher
ecbc.CryptBlocks(ciphertext[blowfish.BlockSize:], ppt)
// return ciphertext to calling function
return ciphertext
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment