Golang and Cryptography (Part 1)Golang provides an excellent base for cryptography application. In the following we will outline some of the core foundations [here]. The presentation covers:
FormatsThe most important representations for data in encryption are: hexadecimal and Base-64. For this, we can convert from a plaintext format into a binary form, and then use hexademical and Base-64 to represent these values: In cryptography we often deal with byte array, either of a fixed length or which are variable in length. A common format in displaying binary or byte information is hexadecimal: and in Base64: The following implements some hex and Base64 conversions: package main import ( "crypto/md5" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "os" ) func main() { s := "hello" argCount := len(os.Args[1:]) if argCount > 0 { s = os.Args[1] } hash := md5.Sum([]byte(s)) hash2 := sha256.Sum256([]byte(s)) //h1.Write([]byte(s)) fmt.Printf("Input:\t\t\t%s\n", s) fmt.Printf("Input (hex):\t\t%x\n", []byte(s)) fmt.Printf("Input (hex):\t\t%s\n", hex.EncodeToString([]byte(s))) fmt.Printf("Input (Base64):\t\t%s\n", base64.StdEncoding.EncodeToString([]byte(s))) fmt.Printf("MD5 (Hex):\t\t%x\n", hash) fmt.Printf("MD5 (hex):\t\t%s\n", hex.EncodeToString(hash[:])) fmt.Printf("MD5 (Base64):\t\t%s\n", base64.StdEncoding.EncodeToString(hash[:])) fmt.Printf("SHA-256 (Hex):\t\t%x\n", hash2) fmt.Printf("SHA-256 (hex):\t\t%s\n", hex.EncodeToString(hash2[:])) fmt.Printf("SHA-256 (Base64):\t%s\n", base64.StdEncoding.EncodeToString(hash2[:])) } And a sample run: Input: hello Input (hex): 68656c6c6f Input (hex): 68656c6c6f Input (Base64): aGVsbG8= MD5 (Hex): 5d41402abc4b2a76b9719d911017c592 MD5 (hex): 5d41402abc4b2a76b9719d911017c592 MD5 (Base64): XUFAKrxLKna5cZ2REBfFkg== SHA-256 (Hex): 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 SHA-256 (hex): 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 SHA-256 (Base64): LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ= For Big Integers we have: package main import ( "fmt" "math/big" "os" "strings" ) func isHexString(s string) bool { return strings.ContainsAny(s, "abc") } func toBigInt(s string) *big.Int { x, _ := new(big.Int).SetString("0", 16) if strings.HasPrefix(s, "0x") { s = strings.Replace(s, "0x", "", 1) x, _ = new(big.Int).SetString(s, 16) } else if strings.HasPrefix(s, "0") { x, _ = new(big.Int).SetString(s, 8) } else if isHexString(s) { x, _ = new(big.Int).SetString(s, 16) } else { x, _ = new(big.Int).SetString(s, 10) } return (x) } func main() { v1 := "2e63d239b3fc47433f19789843e30514b53dfd8773ebc915c0bc774e5368dbb6" v2 := "2e63d239b3fc47433f19789843e30514b53dfd8773ebc915c0bc774e5368dbb4" p1 := "127" argCount := len(os.Args[1:]) // x2, _ := new(big.Int).SetString(v1, 16) if argCount > 0 { v1 = os.Args[1] } if argCount > 1 { v2 = os.Args[2] } if argCount > 2 { p1 = os.Args[3] } x1 := toBigInt(v1) x2 := toBigInt(v2) prime := toBigInt(p1) fmt.Printf("Value 1 in hex: %x\n", v1) fmt.Printf("Value 1 in decimal: %s\n", v1) fmt.Printf("Value 2 in hex: %x\n", v1) fmt.Printf("Value 2 in decimal: %s\n", v1) fmt.Printf("v1+v2= %s\n", new(big.Int).Add(x1, x2).String()) fmt.Printf("v1-v2= %s\n", new(big.Int).Sub(x1, x2).String()) fmt.Printf("v1*v2= %s\n", new(big.Int).Mul(x1, x2).String()) fmt.Printf("p== %s\n", prime) p := new(big.Int).Exp(x1, x2, prime) // x1^x2 (mod prime) fmt.Printf("v1^v2 (mod p)= %s\n", prime) p = new(big.Int).Mul(x1, x2) p = new(big.Int).Mod(p, prime) fmt.Printf("v1*v2 (mod p)= %s\n", p) p = new(big.Int).Add(x1, x2) p = new(big.Int).Mod(p, prime) fmt.Printf("v1+v2 (mod p)= %s\n", p) Invx2 := new(big.Int).ModInverse(x2, prime) // x2^{-1} (mod prime) p = new(big.Int).Mul(x1, Invx2) p = new(big.Int).Mod(p, prime) fmt.Printf("v1/v2 (mod p)= %s\n", p) } A sample run is: Value 1 in hex: 32653633643233396233666334373433336631393738393834336533303531346235336466643837373365626339313563306263373734653533363864626236 Value 1 in decimal: 2e63d239b3fc47433f19789843e30514b53dfd8773ebc915c0bc774e5368dbb6 Value 2 in hex: 32653633643233396233666334373433336631393738393834336533303531346235336466643837373365626339313563306263373734653533363864626236 Value 2 in decimal: 2e63d239b3fc47433f19789843e30514b53dfd8773ebc915c0bc774e5368dbb6 v1+v2= 41965519633295562940561227248429783749428889582066031303567839694283936479082 v1-v2= 2 v1*v2= 440276209523128839864726536429235629067156961787158330200549510715779581777397206383182995070835335229221800721907970830304626962641835121492370755890680 p== 127 v1^v2 (mod p)= 127 v1*v2 (mod p)= 21 v1+v2 (mod p)= 56 v1/v2 (mod p)= 34 Symmetric keyWith symmetric key, Bob and Alice use the same key. With a block cipher, we need to add padding. Here is a program to add and remove PKCS7: package main import ( "fmt" "os" "strconv" "github.com/enceve/crypto/pad" ) func main() { msg := "Hello1111111111111" argCount := len(os.Args[1:]) blocksize := 16 if argCount > 0 { msg = string(os.Args[1]) } if argCount > 1 { blocksize, _ = strconv.Atoi(os.Args[2]) } m := []byte(msg) fmt.Printf("Block size:\t%d bytes (%d bits)\n\n", blocksize, blocksize*8) pkcs7 := pad.NewPKCS7(blocksize) pad1 := pkcs7.Pad(m) fmt.Printf("PKCS7 Pad:\t%x", pad1) res, _ := pkcs7.Unpad(pad1) fmt.Printf("\n Unpad:\t%s", res) x923 := pad.NewX923(blocksize) pad2 := x923.Pad(m) fmt.Printf("\n\nX923 Pad:\t%x", pad2) res, _ = x923.Unpad(pad2) fmt.Printf("\n Unpad:\t%s", res) ISO10126 := pad.NewISO10126(blocksize, nil) pad3 := ISO10126.Pad(m) fmt.Printf("\n\nXISO1012 Pad:\t%x", pad3) res, _ = ISO10126.Unpad(pad3) fmt.Printf("\n Unpad:\t%s", res) } A sample run is: Block size: 16 bytes (128 bits) PKCS7 Pad: 48656c6c6f0b0b0b0b0b0b0b0b0b0b0b Unpad: Hello X923 Pad: 48656c6c6f000000000000000000000b Unpad: Hello XISO1012 Pad: 48656c6c6f414a74068d99d58027020b Unpad: Hello For AES encryption we see three main modes: block; stream and AEAD: package main import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/sha256" "fmt" "io" "os" "strconv" "strings" "github.com/enceve/crypto/pad" "golang.org/x/crypto/pbkdf2" ) func getSalt(n int) []byte { nonce := make([]byte, n) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { panic(err.Error()) } return (nonce) } func main() { msg := "hello" passwd := "qwerty" mode := "gcm" size := 16 argCount := len(os.Args[1:]) if argCount > 0 { msg = os.Args[1] } if argCount > 1 { passwd = os.Args[2] } if argCount > 2 { mode = os.Args[3] } if argCount > 3 { size, _ = strconv.Atoi(os.Args[4]) } pwsalt := getSalt(12) // 96 bits for nonce/IV key := pbkdf2.Key([]byte(passwd), pwsalt, 10000, size, sha256.New) block, _ := aes.NewCipher(key) var salt []byte var plain []byte var ciphertext []byte plaintext := []byte(msg) if mode == "gcm" { // AEAD salt = getSalt(12) aesgcm, _ := cipher.NewGCM(block) ciphertext = aesgcm.Seal(nil, salt, plaintext, nil) plain, _ = aesgcm.Open(nil, salt, ciphertext, nil) } else if mode == "cbc" { // Block cipher plain = make([]byte, (len(plaintext)/16+1)*aes.BlockSize) ciphertext = make([]byte, (len(plaintext)/16+1)*aes.BlockSize) salt = getSalt(16) pkcs7 := pad.NewPKCS7(aes.BlockSize) pad1 := pkcs7.Pad(plaintext) blk := cipher.NewCBCEncrypter(block, salt) blk.CryptBlocks(ciphertext, pad1) blk = cipher.NewCBCDecrypter(block, salt) blk.CryptBlocks(plain, ciphertext) plain, _ = pkcs7.Unpad(plain) } else if mode == "cfb" { // Stream cipher salt = getSalt(aes.BlockSize) plain = make([]byte, len(plaintext)) ciphertext = make([]byte, len(plaintext)) stream := cipher.NewCFBEncrypter(block, salt) stream.XORKeyStream(ciphertext, plaintext) stream = cipher.NewCFBDecrypter(block, salt) stream.XORKeyStream(plain, ciphertext) } fmt.Printf("Mode:\t\t%s\n", strings.ToUpper(mode)) fmt.Printf("Key size:\t%d bits\n", size*8) fmt.Printf("Message:\t%s\n", msg) fmt.Printf("Password:\t%s\n", passwd) fmt.Printf("Password Salt:\t%x\n", pwsalt) fmt.Printf("\nKey:\t\t%x\n", key) fmt.Printf("\nCipher:\t\t%x\n", ciphertext) fmt.Printf("Salt:\t\t%x\n", salt) fmt.Printf("\nDecrypted:\t%s\n", plain) } A sample run for GCM is: Mode: GCM Key size: 128 bits Message: hello Password: qwerty Password Salt: 0c7708fead0911c4c83319f9 Key: 2af2a2a1b85432856be7887de01217f9 Cipher: 9e520dfde0b2f615e62a52ad306b3f208959e1a97d Salt: 6e21bc49b60c72dc93b027a1 Decrypted: hello HashingWith hashing we create a fixed length binary output which provides a one-way function. The following is some code: package main import ( "crypto/md5" "crypto/rand" "crypto/sha1" "crypto/sha256" "crypto/sha512" "fmt" "golang.org/x/crypto/argon2" "golang.org/x/crypto/bcrypt" "golang.org/x/crypto/blake2b" "golang.org/x/crypto/md4" "golang.org/x/crypto/ripemd160" "golang.org/x/crypto/scrypt" "golang.org/x/crypto/sha3" ) type params struct { memory uint32 iterations uint32 parallelism uint8 saltLength uint32 keyLength uint32 } func generateRandomBytes(n uint32) ([]byte, error) { b := make([]byte, n) _, err := rand.Read(b) if err != nil { return nil, err } return b, nil } func main() { s := "Abc" h1 := md4.New() h2 := md5.Sum([]byte(s)) h3 := sha1.Sum([]byte(s)) h4 := sha256.Sum256([]byte(s)) h5 := sha512.Sum512([]byte(s)) h6 := ripemd160.New() h7 := sha3.Sum256([]byte(s)) h8 := sha3.Sum512([]byte(s)) h9 := blake2b.Sum256([]byte(s)) h10 := blake2b.Sum512([]byte(s)) p := ¶ms{ memory: 64 * 1024, iterations: 1, parallelism: 1, saltLength: 16, keyLength: 32, } salt, err := generateRandomBytes(p.saltLength) h11 := argon2.IDKey([]byte(s), salt, p.iterations, p.memory, p.parallelism, p.keyLength) // Values are password, salt, N, r, p, key length h12, err := scrypt.Key([]byte(s), salt, 1 <<15, 4, 1, 32) cost := 1 h13, err := bcrypt.GenerateFromPassword([]byte(s), cost) h1.Write([]byte(s)) h6.Write([]byte(s)) fmt.Println("Input: ", s) fmt.Printf(" MD4: %x\n", h1.Sum(nil)) fmt.Printf("MD5: %x\n", h2) fmt.Printf("SHA-1: %x\n", h3) fmt.Printf("SHA-256: %x\n", h4) fmt.Printf("SHA-512: %x\n", h5) fmt.Printf("RIPEMD160: %x\n", h6.Sum(nil)) fmt.Printf("SHA-3 256: %x\n", h7) fmt.Printf("SHA-3 512: %x\n", h8) fmt.Printf("Blake2b 256-bit: %x\n", h9) fmt.Printf("Blake2b 512-bit: %x\n", h10) if err == nil { fmt.Printf("Argon2: %x\n", h11) fmt.Printf("Scrypt: %x\n", h12) fmt.Printf("BCrypt: %s\n", h13) } } A sample run: Input: Abc MD4: d4ce244fc98785d1eafd751b3b00ac00 MD5: 35593b7ce5020eae3ca68fd5b6f3e031 SHA-1: 915858afa2278f25527f192038108346164b47f2 SHA-256: 06d90109c8cce34ec0c776950465421e176f08b831a938b3c6e76cb7bee8790b SHA-512: 047b10fe577a23efd96546dcfce8485fc4aa8ae84dd3bf0c435a294cf318c7a260418dd96a97feb0ad7aed90ff011620cfa5d7b3cdc8aea4c4e81e56a0fc9934 RIPEMD160: f6f268b47b3d984c3efad5a36cb1890c5cf0f839 SHA-3 256: 6f7d689169ef1894c906a7fd1bbf91912173277ee992b666658118a67b054fbb SHA-3 512: 207fd38cf640abb5d5740f774cd82f69dbecc66f3715727d674ea2e9f0a5d37314525f1e747493b382acf2516d0a4b4513e00a0217ec98b2c4614ad2bb4aa9c1 Blake2b 256-bit: f7641b2e22df1dd8d93fbdd76cb12e748b3ca0025224a06ebf4c5a88bafbd5ad Blake2b 512-bit: 2916519a16d56ffc0ab56cc3c8ab3e8c0b06d53c71002edbc740066d64190d3ef1171c54253c7668e7a6f79eabcf726de5e836463be79fbec352b332397be1e7 Argon2: 44a060840466c3ad5c3d4fc0748c97c0696ab7549d06c85c65a7f6f74541215e Scrypt: 86b93fce7cdbbf3e20564fe8f3bb5549db7841c6290a0c461e1984ab5c48798a BCrypt: $2a$10$4iyk2eNPhCBJgV0fSHBjd.6YljK5xJJG5csXgFuq9IuH3I16glkEK HMACWith HMAC we can create an authenicated hash using a shared secret key. Bob and Alice will share the same secret key. package main import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "crypto/sha512" "encoding/hex" "fmt" "io" "os" ) func getSalt(n int) []byte { nonce := make([]byte, n) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { panic(err.Error()) } return (nonce) } func main() { key:="000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F" m := "The quick brown fox jumps over the lazy dog" argCount := len(os.Args[1:]) if argCount > 0 { m = os.Args[1] } if argCount > 1 { key = os.Args[2] } var secretKey, _ = hex.DecodeString(key) message := []byte(m) // salt := getSalt(16) fmt.Printf("Message: %s\n", m) fmt.Printf("Key: %x\n", secretKey) // fmt.Printf("\nSalt: %x\n", salt) hash := hmac.New(sha256.New, []byte(secretKey)) hash.Write(message) // hash.Write(salt) fmt.Printf("\nHMAC-Sha256: %x", hash.Sum(nil)) hash = hmac.New(sha512.New, []byte(secretKey)) hash.Write(message) // hash.Write(salt) fmt.Printf("\n\nHMAC-sha512: %x", hash.Sum(nil)) } A sample run: Message: The quick brown fox jumps over the lazy dog Key: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f HMAC-Sha256: f87ad256151fc7b4c5dffa4adb3ebe911a8eeb8a8ebdee3c2a4a8e5f5ec02c32 HMAC-sha512: 0623d51f882717efa360aa2217d0b554b57ea018eb518178b23045941a6ae24450af5c980f6ebca94ca5314a8590991b4eab6daa3f0c109345433f44ee234d00 For HKDF (HMAC Key Derivation Function) we can generate a shared key based on a secret and salt value: package main import ( "crypto/rand" "crypto/sha256" "fmt" "io" "os" "golang.org/x/crypto/hkdf" ) func getSalt(n int) []byte { nonce := make([]byte, n) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { panic(err.Error()) } return (nonce) } func main() { hash := sha256.New s := "The quick brown fox jumps over the lazy dog." salt := getSalt(hash().Size()) info := []byte("") argCount := len(os.Args[1:]) if argCount > 0 { s = os.Args[1] } secret := []byte(s) kdf := hkdf.New(hash, secret, salt, info) key1 := make([]byte, 16) _, _ = io.ReadFull(kdf, key1) fmt.Printf("Secret: %s\n", s) fmt.Printf("Salt: %x\n", salt) fmt.Printf("\nHKDF 16 byte key: %x\n", key1) key2 := make([]byte, 32) _, _ = io.ReadFull(kdf, key2) fmt.Printf("HKDF 32 byte key: %x\n", key2) } A sample run is: Secret: The quick brown fox jumps over the lazy dog. Salt: 282d76707f86b48ae91357b3070a0869f7c9ee53e105fdfbb5f51c83b9c21a87 HKDF 16 byte key: 327ee9b0b46815b85260be49f125a985 HKDF 32 byte key: 7e41b34d7ae05a44f13bbf3885ab2f517888e4819b752802d65bf8e6eea0b43b Public keyWith public key encryption, we create a public and a private key. package main import ( "crypto/rand" "fmt" "math/big" "os" "strconv" ) func main() { psize := 128 e := new(big.Int).SetInt64(65537) Mval := "12" argCount := len(os.Args[1:]) if argCount > 0 { Mval = os.Args[1] } if argCount > 1 { psize, _ = strconv.Atoi(os.Args[2]) } p, _ := rand.Prime(rand.Reader, psize) q, _ := rand.Prime(rand.Reader, psize) M, _ := new(big.Int).SetString(Mval, 10) // Base 10 N := new(big.Int).Mul(p, q) C := new(big.Int).Exp(M, e, N) Pminus1 := new(big.Int).Sub(p, new(big.Int).SetInt64(1)) Qminus1 := new(big.Int).Sub(q, new(big.Int).SetInt64(1)) PHI := new(big.Int).Mul(Pminus1, Qminus1) d := new(big.Int).ModInverse(e, PHI) Plain := new(big.Int).Exp(C, d, N) fmt.Printf("M= %s\n", M) fmt.Printf("p= %s\n", p) fmt.Printf("q= %s\n", q) fmt.Printf("N= %s\n", N) fmt.Printf("Public: e= %s\n", e) fmt.Printf("Private: d= %s\n", d) fmt.Printf("\nCipher C= %s\n", C) fmt.Printf("\nDecrypt Plain= %s\n", Plain) } A sample run is: M= 12 p= 295806851467514971672292028961750769609 q= 269393212158044082406569589759963515197 N= 79688357895191294286647587852540487066377756432903938914060265832475717247973 Public: e= 65537 Private: d= 64734859483682336947477469668758151744766997093916591625578680423822411214369 Cipher C= 10608469162454498211673212126644030923108520854821147642475399122477145638162 Decrypt Plain= 12 For ElGamal, we use discrete logarithms: package main import ( "crypto/rand" "fmt" "io" "math/big" ) func get_g_p(size string) (g, p *big.Int) { if size == "512" { p, _ = new(big.Int).SetString("FCA682CE8E12CABA26EFCCF7110E526DB078B05EDECBCD1EB4A208F3AE1617AE01F35B91A47E6DF63413C5E12ED0899BCD132ACD50D99151BDC43EE737592E17", 16) g, _ = new(big.Int).SetString("678471B27A9CF44EE91A49C5147DB1A9AAF244F05A434D6486931D2D14271B9E35030B71FD73DA179069B32E2935630E1C2062354D0DA20A6C416E50BE794CA4", 16) } if size == "768" { p, _ = new(big.Int).SetString("E9E642599D355F37C97FFD3567120B8E25C9CD43E927B3A9670FBEC5D890141922D2C3B3AD2480093799869D1E846AAB49FAB0AD26D2CE6A22219D470BCE7D777D4A21FBE9C270B57F607002F3CEF8393694CF45EE3688C11A8C56AB127A3DAF", 16) g, _ = new(big.Int).SetString("30470AD5A005FB14CE2D9DCD87E38BC7D1B1C5FACBAECBE95F190AA7A31D23C4DBBCBE06174544401A5B2C020965D8C2BD2171D3668445771F74BA084D2029D83C1C158547F3A9F1A2715BE23D51AE4D3E5A1F6A7064F316933A346D3F529252", 16) } if size == "1024" { p, _ = new(big.Int).SetString("FD7F53811D75122952DF4A9C2EECE4E7F611B7523CEF4400C31E3F80B6512669455D402251FB593D8D58FABFC5F5BA30F6CB9B556CD7813B801D346FF26660B76B9950A5A49F9FE8047B1022C24FBBA9D7FEB7C61BF83B57E7C6A8A6150F04FB83F6D3C51EC3023554135A169132F675F3AE2B61D72AEFF22203199DD14801C7", 16) g, _ = new(big.Int).SetString("F7E1A085D69B3DDECBBCAB5C36B857B97994AFBBFA3AEA82F9574C0B3D0782675159578EBAD4594FE67107108180B449167123E84C281613B7CF09328CC8A6E13C167A8B547C8D28E0A3AE1E2BB3A675916EA37F0BFA213562F1FB627A01243BCCA4F1BEA8519089A883DFE15AE59F06928B665E807B552564014C3BFECF492A", 16) } return } type PublicKey struct { G, P, Y *big.Int } type PrivateKey struct { PublicKey X *big.Int } func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (a, b *big.Int, err error) { k, _ := rand.Int(random, pub.P) m := new(big.Int).SetBytes(msg) a = new(big.Int).Exp(pub.G, k, pub.P) s := new(big.Int).Exp(pub.Y, k, pub.P) b = s.Mul(s, m) b.Mod(b, pub.P) return } func Decrypt(priv *PrivateKey, a, b *big.Int) (msg []byte, err error) { s := new(big.Int).Exp(a, priv.X, priv.P) s.ModInverse(s, priv.P) s.Mul(s, b) s.Mod(s, priv.P) em := s.Bytes() return em, nil } func main() { s := "hello world" x := "40" plen := "512" // s = string(os.Args[1]) // x = string(os.Args[2]) // plen =string(os.Args[3]) fmt.Printf("Input message: %s\n", s) fmt.Printf("Prime number size: %s\n\n", plen) xval, _ := new(big.Int).SetString(x, 10) g, p := get_g_p(plen) priv := &PrivateKey{ PublicKey: PublicKey{ G: g, P: p, }, X: xval, } priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P) message := []byte(s) a, b, _ := Encrypt(rand.Reader, &priv.PublicKey, message) message2, _ := Decrypt(priv, a, b) fmt.Printf("====Private key (x):\nX=%d", priv.X) fmt.Printf("\n\n====Public key (Y,G,P):\nY=%d\n\nG=%d\n\nP=%d", priv.Y, priv.PublicKey.G, priv.PublicKey.P) fmt.Printf("\n\n====Cipher (a=%s)\n\n(b=%s): ", a, b) fmt.Printf("\n\n====Decrypted: %s", string(message2)) } A sample run is: Input message: hello world Prime number size: 512 ====Private key (x): X=40 ====Public key (Y,G,P): Y=11765933737659181064003945366380762137134618693834882798460832005540886987522854475566040189884295346667906296155495198252840497522102322260522882578915137 G=5421644057436475141609648488325705128047428394380474376834667300766108262613900542681289080713724597310673074119355136085795982097390670890367185141189796 P=13232376895198612407547930718267435757728527029623408872245156039757713029036368719146452186041204237350521785240337048752071462798273003935646236777459223 ====Cipher (a=159783758277664012928674851197220532735544644017547303825497596160008339162415680504589377836091871017589903008929153269423823510319876676018134081389599) (b=1565286765679818277375001876954396058169539985188749813327442938468495299977799355944246916699970457317552490495405386419565432882567243815601446053149268): ====Decrypted: hello world ECDSAFor ECDSA: package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "fmt" "os" "strings" "github.com/dustinxie/ecc" ) func getCurve(s string) elliptic.Curve { if strings.Contains(s, "384") { return (ecc.P384()) } else if strings.Contains(s, "521") { return (ecc.P521()) } return (ecc.P256k1()) } func main() { msg := "Hello 123" curveType := "" argCount := len(os.Args[1:]) if argCount > 0 { msg = os.Args[1] } if argCount > 1 { curveType = os.Args[2] } pubkeyCurve := getCurve(curveType) m := []byte(msg) digest := sha256.Sum256(m) privatekey, _ := ecdsa.GenerateKey(pubkeyCurve, rand.Reader) pubkey := privatekey.PublicKey r, s, _ := ecdsa.Sign(rand.Reader, privatekey, digest[:]) fmt.Printf("=== Message ===\n") fmt.Printf("Msg=%s\nHash=%x\n", msg, digest) fmt.Printf("\n=== Private key ===\n") fmt.Printf("Private key=%x\n", privatekey.D) fmt.Printf("Curve=%s\n", privatekey.Curve.Params().Name) fmt.Printf("\n=== Public key (X,Y) ===\n") fmt.Printf("X=%s Y=%s\n", pubkey.X, pubkey.Y) fmt.Printf(" Hex: X=%x Y=%x\n", pubkey.X.Bytes(), pubkey.Y.Bytes()) fmt.Printf("\n=== Signature (R,S) ===\n") fmt.Printf("R=%s S=%s\n", r, s) fmt.Printf(" Hex: R=%x S=%x\n", r, s) rtn := ecdsa.Verify(&pubkey, digest[:], r, s) if rtn { fmt.Printf("Signature verifies") } else { fmt.Printf("Signature does not verify") } } A sample run is: === Private key === Private key=d2a60621f5866bd413c8e98fa90c0ea696b99bb3dee9262c65a3a540ead5d46a Curve=P-256k1 === Public key (X,Y) === X=80821331051023746223830068219547774778570362684127746989627081930841526444419 Y=58249137465050673556703463636023643181757172398528046136871137906486743981588 Hex: X=b2af40966979d9d2d4d388f476c2a790236997253ac067145d655a6fdd612183 Y=80c7d7ed9376bfae0ccd8ece6f5f812c08d79d63df60135d6cfbcb9dcad4de14 === Signature (R,S) === R=73267642912309299955050307148272353870495402285772390441578067564792986384508 S=50408473822392060484338227503577083148905530272862983096693552784217265409905 Hex: R=a1fc042d5df880ce79483c2c52d60c46b13f314bba4f8e20277b8d6ad4aeb87c S=6f722f637ce219a0c13dec485ab048b8387806bc95e5b76773d5738b20400f71 Signature verifies The following verifies the signature: package main import ( "crypto/ecdsa" "crypto/sha256" "fmt" "math/big" "os" "strings" "github.com/dustinxie/ecc" ) func isHexString(s string) bool { return strings.ContainsAny(s, "abcdef") } func toBigInt(s string) *big.Int { x, _ := new(big.Int).SetString("0", 16) if isHexString(s) { x, _ = new(big.Int).SetString(s, 16) } else { x, _ = new(big.Int).SetString(s, 10) } return (x) } func main() { msg := "Hello 123" r := "2e63d239b3fc47433f19789843e30514b53dfd8773ebc915c0bc774e5368dbb6" s := "39b22fd14b77ae40399340f55fe0ceced6b3107a57bffc65c1dbc9b7a91a050a" bobpub_X := "46cab231f27992deef926ed08b4c8102505d8a29ed0b544777a7ded3996a059c" bobpub_Y := "cc86ca06058be27a7e61830f4e804a02ff072d59e78749b544640280aed73c53" argCount := len(os.Args[1:]) if argCount > 0 { msg = os.Args[1] } if argCount > 1 { r = os.Args[2] } if argCount > 2 { s = os.Args[3] } if argCount > 3 { bobpub_X = os.Args[4] } if argCount > 4 { bobpub_Y = os.Args[5] } m := []byte(msg) digest := sha256.Sum256(m) x := toBigInt(bobpub_X) y := toBigInt(bobpub_Y) R := toBigInt(r) S := toBigInt(s) publicKey := ecdsa.PublicKey{ Curve: ecc.P256k1(), //secp256k1 X: x, Y: y, } fmt.Printf("Message to sign: %s\n", msg) fmt.Printf("Bob Public Key: X:%s Y:%s\n", bobpub_X, bobpub_Y) fmt.Printf("Bob Signature: R:%s S:%s\n", r, s) fmt.Printf("\n=== Now checking with Bob's public key ===\n") rtn := ecdsa.Verify(&publicKey, digest[:], R, S) if rtn { fmt.Printf("Signature works. Yipee!") } else { fmt.Printf("Signature does not check out!!!") } } A sample run: Message to sign: Hello 123 Bob Public Key: X:46cab231f27992deef926ed08b4c8102505d8a29ed0b544777a7ded3996a059c Y:cc86ca06058be27a7e61830f4e804a02ff072d59e78749b544640280aed73c53 Bob Signature: R:2e63d239b3fc47433f19789843e30514b53dfd8773ebc915c0bc774e5368dbb6 S:39b22fd14b77ae40399340f55fe0ceced6b3107a57bffc65c1dbc9b7a91a050a === Now checking with Bob's public key === Signature works. Yipee! For EdDSA: package main import ( "crypto/ed25519" "crypto/sha256" "fmt" "os" ) func main() { msg := "Hello 1234" argCount := len(os.Args[1:]) if argCount > 0 { msg = os.Args[1] } publ, priv, _ := ed25519.GenerateKey((nil)) m := []byte(msg) digest := sha256.Sum256(m) sig := ed25519.Sign(priv, digest[:]) fmt.Printf("=== Message ===\n") fmt.Printf("Msg=%s\nHash=%x\n", msg, digest) fmt.Printf("\n=== Private key ===\n") fmt.Printf("Public key=%x\n\n", publ) fmt.Printf("Private key=%x\n\n", priv[0:32]) fmt.Printf("Signature: (%x,%x)\n\n", sig[0:32], sig[32:64]) rtn := ed25519.Verify(publ, digest[:], sig) if rtn { fmt.Printf("Signature verifies") } else { fmt.Printf("Signature does not verify") } } A sample run: === Message === Msg=Hello 1234 Hash=798dafff617a026d7c433bee5678a89cba023eef003b5b92f9daee401d6d7f93 === Private key === Public key=6a34676b06e4061e7e75cb8b1469399ac82c7a588e75c3b97f602de4d42b2f8a Private key=3566b01b5ad4cc82635fe308563286070a052ba5a4322a85a29fe2f1b7d5939c Signature: (1c021af5a08a82fd125e93314b5e16ab75b88229f8c65b51daf59b7254a7440d,923c7e87fbbb4cad100f62346bab83846907a81c77bbe43600ff27dcf23b2e0e) Signature verifies |