Go lang 3DES partially decrypted the encrypted string - go

While performing decryption using 3des the given encrypted text is not fully decrypted, not sure where it went wrong, help me complete the decryption error
The code is avaialbe at Go Playground for Insection and run
package main
import (
"crypto/des"
"encoding/hex"
"fmt"
)
func main() {
// Mimimum Key Size of Length 24
key := "mysecretPasswordkeySiz24"
plainText := "https://8gwifi.org"
ct := EncryptTripleDES([]byte(key),plainText)
fmt.Printf("Original Text: %s\n",plainText)
fmt.Printf("3DES Encrypted Text: %s\n", ct)
DecryptTripleDES([]byte(key),ct)
}
func EncryptTripleDES(key []byte, plaintext string) string {
c,err := des.NewTripleDESCipher(key)
if err != nil {
fmt.Errorf("NewTripleDESCipher(%d bytes) = %s", len(key), err)
panic(err)
}
out := make([]byte, len(plaintext))
c.Encrypt(out, []byte(plaintext))
return hex.EncodeToString(out)
}
func DecryptTripleDES(key []byte, ct string) {
ciphertext, _ := hex.DecodeString(ct)
c, err := des.NewTripleDESCipher([]byte(key))
if err != nil {
fmt.Errorf("NewTripleDESCipher(%d bytes) = %s", len(key), err)
panic(err)
}
plain := make([]byte, len(ciphertext))
c.Decrypt(plain, ciphertext)
s := string(plain[:])
fmt.Printf("3DES Decrypyed Text: %s\n", s)
}
The output
Original Text: https://8gwifi.org
3DES Encrypted Text: a6e5215154bf86d000000000000000000000
3DES Decrypyed Text: https://

the given encrypted text is not fully decrypted
The encrypted text you gave is fully decrypted. The problem is not (yet) the decryption but your encryption. As documented des.NewTripleDESCipher returns a cipher.Block and cipher.Block.Encrypt encrypts as documented only the first block of the input data. Given that DES has a block size of 8 byte only the first 8 byte of the input data are encrypted, i.e. https://.
This means in order to encrypt all data you must encrypt all blocks. Similar you need to decrypt all blocks when decrypting - but cipher.Block.Decrypt also decrypts only a single block.
Apart from that DES is broken, so don't use it for something serious.

Related

Golang- AES Decryption is not returning same Text

I am following this documentation and was trying to implement a simple AES encryption and decryption with using GoLang. For plain text it is working fine however, for UUID it is not working. Excepting a resolution of this and why this is happening. Here is my sample code
package main
import (
"crypto/aes"
"encoding/hex"
"fmt"
)
func main() {
key := "thisis32bitlongpassphraseimusing"
pt := "a30a1777-e9f4-ed45-4755-add00172ebae"
c := EncryptAES([]byte(key), pt)
fmt.Println(pt)
fmt.Println(c)
DecryptAES([]byte(key), c)
}
func EncryptAES(key []byte, plaintext string) string {
c, err := aes.NewCipher(key)
CheckError(err)
out := make([]byte, len(plaintext))
c.Encrypt(out, []byte(plaintext))
return hex.EncodeToString(out)
}
func DecryptAES(key []byte, ct string) {
ciphertext, _ := hex.DecodeString(ct)
c, err := aes.NewCipher(key)
CheckError(err)
pt := make([]byte, len(ciphertext))
c.Decrypt(pt, ciphertext)
s := string(pt[:])
fmt.Println("DECRYPTED:", s)
}
func CheckError(err error) {
if err != nil {
panic(err)
}
}
And here is the output
a30a1777-e9f4-ed45-4755-add00172ebae
e0f32a5bcf576754da4206cc967157ae0000000000000000000000000000000000000000
DECRYPTED: a30a1777-e9f4-ed
As you can see in the remaining last part of the UUID is disappearing. I have attached as a snap which says it didn't decrypt the last part properly. Does anyone know reasoning behind this? I have seen a close question like that but not and exactly one.
If you look at the page for the AES block cipher you'll find out that aes.NewCipher returns a Block as mentioned by Jake in the comments.
Now if you go to that page you'll see that this page points out various modes that you can use to create a real, secure cipher out of a block cipher. A block cipher only handles blocks of data, which are always 128 bits / 16 bytes in the case of AES. So this is precisely why there are all those zero's after the ciphertext, it encrypted 16 bytes and that was that. Note that ciphertext should always look as randomized bytes.
Unfortunately it doesn't directly list the authenticated (AEAD) modes, so please take a look here as well. That said, you can see that CTR mode is in there including examples, and that's the main idea missing from the question + my answer that you've linked to. Nothing in your code shows a mode of operation, and certainly not counter mode.
The problem with your code is that AES encryption requires the input to be a multiple of the block size (16 bytes for AES-128), but the UUID you're trying to encrypt ("a30a1777-e9f4-ed45-4755-add00172ebae")is 36 bytes long and it is causing the cipher to error.
One way to fix this issue is by padding the plaintext so that its length is a multiple of the block size before encryption. The Go standard library has a package called crypto/padding that provides functions for adding padding to plaintext.
You can modify your EncryptAES function like this:
func EncryptAES(key []byte, plaintext string) string {
c, err := aes.NewCipher(key)
CheckError(err)
plaintextBytes := []byte(plaintext)
// Add padding to plaintext
blockSize := c.BlockSize()
padding := blockSize - (len(plaintextBytes) % blockSize)
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
plaintextBytes = append(plaintextBytes, padtext...)
out := make([]byte, len(plaintextBytes))
c.Encrypt(out, plaintextBytes)
return hex.EncodeToString(out)
}
Then in the DecryptAES function, you can remove the padding before decrypting the ciphertext like this:
func DecryptAES(key []byte, ct string) {
ciphertext, _ := hex.DecodeString(ct)
c, err := aes.NewCipher(key)
CheckError(err)
pt := make([]byte, len(ciphertext))
c.Decrypt(pt, ciphertext)
//Remove padding
padLen := int(pt[len(pt)-1])
s := string(pt[:len(pt)-padLen])
fmt.Println("DECRYPTED:", s)
}
As for padding schemes, you might want to try padding scheme like pkcs#5 or pkcs#7.

Implementing Ethereum personal_sign (EIP-191) from go-ethereum gives different signature from ethers.js

I am attempting to generate a personal_sign in Golang like its implemented in ethers.js. Similar question but that ended up using the regular sign over the personal sign_implementation.
Ethers
// keccak256 hash of the data
let dataHash = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes(JSON.stringify(dataToSign))
);
//0x8d218fc37d2fd952b2d115046b786b787e44d105cccf156882a2e74ad993ee13
let signature = await wallet.signMessage(dataHash); // 0x469b07327fc41a2d85b7e69bcf4a9184098835c47cc7575375e3a306c3718ae35702af84f3a62aafeb8aab6a455d761274263d79e7fc99fbedfeaf759d8dc9361c
Golang:
func signHash(data []byte) common.Hash {
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
return crypto.Keccak256Hash([]byte(msg))
}
privateKey, err := crypto.HexToECDSA(hexPrivateKey)
if err != nil {
log.Fatal(err)
}
dataHash := crypto.Keccak256Hash(dataToSign) //0x8d218fc37d2fd952b2d115046b786b787e44d105cccf156882a2e74ad993ee13
signHash := signHash(dataHash.Bytes())
signatureBytes, err := crypto.Sign(signHash.Bytes(), privateKey)
if err != nil {
log.Fatal(err)
}
// signatureBytes 0xec56178d3dca77c3cee7aed83cdca2ffa2bec8ef1685ce5103cfa72c27beb61313d91b9ad9b9a644b0edf6352cb69f2f8acd25297e3c64cd060646242e0455ea00
As you can see the hash is the same, but the signature is different:
0x469b07327fc41a2d85b7e69bcf4a9184098835c47cc7575375e3a306c3718ae35702af84f3a62aafeb8aab6a455d761274263d79e7fc99fbedfeaf759d8dc9361c Ethers
0xec56178d3dca77c3cee7aed83cdca2ffa2bec8ef1685ce5103cfa72c27beb61313d91b9ad9b9a644b0edf6352cb69f2f8acd25297e3c64cd060646242e0455ea00 Golang
Looking at the source code of Ethers.js I can't find anything different aside how the padding is managed.
Edit
Check the approved answer
signHash(data []byte) common.Hash {
hexData := hexutil.Encode(data)
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(hexData), hexData)
return crypto.Keccak256Hash([]byte(msg))
}
There is a bug in the JavaScript code.
From the documentation of signer.signMessage() (see the Note section), it appears that a string is UTF8 encoded and binary data must be passed as TypedArray or Array.
The Keccak hash is returned hex encoded, i.e. as string, and is therefore UTF8 encoded, which is incorrect. Instead, it must be converted to a TypedArray. For this purpose the library provides the function ethers.utils.arrayify().
The following JavaScript is based on the posted code, but performs the required hex decoding:
(async () => {
let privateKey = "0x8da4ef21b864d2cc526dbdb2a120bd2874c36c9d0a1fb7f8c63d7f7a8b41de8f";
let dataToSign = {"data1":"value1","data2":"value2"};
let dataHash = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes(JSON.stringify(dataToSign))
);
dataHashBin = ethers.utils.arrayify(dataHash)
let wallet = new ethers.Wallet(privateKey);
let signature = await wallet.signMessage(dataHashBin);
document.getElementById("signature").innerHTML = signature; // 0xfcc3e9431c139b5f943591af78c280b939595ce9df66210b7b8bb69565bdd2af7081a8acc0cbb5ea55bd0d673b176797966a5180c11ac297b7e6344c5822e66d1c
})();
<script src="https://cdn.ethers.io/lib/ethers-5.0.umd.min.js" type="text/javascript"></script>
<p style="font-family:'Courier New', monospace;" id="signature"></p>
which produces the following signature:
0xfcc3e9431c139b5f943591af78c280b939595ce9df66210b7b8bb69565bdd2af7081a8acc0cbb5ea55bd0d673b176797966a5180c11ac297b7e6344c5822e66d1c
The Go code below is based on the unmodified posted Go code, but using key and data from the JavaScript code for a comparison:
package main
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"encoding/hex"
"encoding/json"
"log"
)
func signHash(data []byte) common.Hash {
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
return crypto.Keccak256Hash([]byte(msg))
}
func main() {
hexPrivateKey := "8da4ef21b864d2cc526dbdb2a120bd2874c36c9d0a1fb7f8c63d7f7a8b41de8f"
dataMap := map[string]string{"data1":"value1","data2":"value2"}
dataToSign, _ := json.Marshal(dataMap)
privateKey, err := crypto.HexToECDSA(hexPrivateKey)
if err != nil {
log.Fatal(err)
}
dataHash := crypto.Keccak256Hash(dataToSign) //0x8d218fc37d2fd952b2d115046b786b787e44d105cccf156882a2e74ad993ee13
signHash := signHash(dataHash.Bytes())
signatureBytes, err := crypto.Sign(signHash.Bytes(), privateKey)
if err != nil {
log.Fatal(err)
}
fmt.Println("0x" + hex.EncodeToString(signatureBytes))
}
The Go Code gives the following signature:
0xfcc3e9431c139b5f943591af78c280b939595ce9df66210b7b8bb69565bdd2af7081a8acc0cbb5ea55bd0d673b176797966a5180c11ac297b7e6344c5822e66d01
Both signatures match except for the last byte.
The JavaScript code returns the signature in the format r|s|v (see here). v is one byte in size and is just the value in which both signatures differ.
It is v = 27 + rid where rid is the recovery ID. The recovery ID has values between 0 and 3, so v has values between 27 and 30 or 0x1b and 0x1e (see here).
The Go code, on the other hand, returns the recovery ID in the last byte instead of v. So that the signature of the Go code matches that of the JavaScript code in the last byte as well, the recovery ID must be replaced by v:
signatureBytes[64] += 27
fmt.Println("0x" + hex.EncodeToString(signatureBytes))

How to handle invalid keys in NewCBCDecrypter example?

I found an example for the usage of CBC decrypter:
https://golang.org/pkg/crypto/cipher/#NewCBCDecrypter
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"fmt"
)
func main() {
key, _ := hex.DecodeString("6368616e676520746869732070617373")
ciphertext, _ := hex.DecodeString("73c86d43a9d700a253a96c85b0f6b03ac9792e0e757f869cca306bd3cba1c62b")
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
if len(ciphertext) < aes.BlockSize {
panic("ciphertext too short")
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
if len(ciphertext)%aes.BlockSize != 0 {
panic("ciphertext is not a multiple of the block size")
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(ciphertext, ciphertext)
fmt.Printf("%s\n", ciphertext)
}
It works fine. But if I modify the key to make it invalid then it returns some strange characters instead of throwing an error.
How could I make it more programmer friendly by throwing an error if the key is invalid?
How could I make it more programmer friendly by throwing an error if the key is invalid?
You cannot. That simply isn't how this stuff works. You supply a key and this key is used to decipher the input. There is no "correct" or "wrong" key here, there is just a key here.
You might be able to inspect the deciphered output: If you know the output always starts with some magic prefix or is a well-formed XML or anything you can check reliably after deciphering you can fail. But there is really nothing intrinsic to a key being wrong or right.

Convert os.Stdin to []byte

I'm trying to implement a small chat-server in golang with end-to-end encryption. Starting of the example for server https://github.com/adonovan/gopl.io/tree/master/ch8/chat and client https://github.com/adonovan/gopl.io/blob/master/ch8/netcat3/netcat.go I stumbled upon https://www.thepolyglotdeveloper.com/2018/02/encrypt-decrypt-data-golang-application-crypto-packages/ to encrypt and decrypt in Go.
The function to encrypt:
func encrypt(data []byte, passphrase string) []byte {
block, _ := aes.NewCipher([]byte(createHash(passphrase)))
gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
panic(err.Error())
}
ciphertext := gcm.Seal(nonce, nonce, data, nil)
return ciphertext
}
in func main():
ciphertext := encrypt([]byte(os.Stdin), "password")
mustCopy(conn, ciphertext)
conn.Close()
os.Stdin is os.file, while it is needed as []byte. The solution should be io.Reader or via buffer, but I can't find a working solution.
I tried
bytes.NewBuffer([]byte(os.Stdin))
and
reader := bytes.NewReader(os.Stdin)
Any input is more than welcome. Sorry if I'm not seeing the obvious problem/solution here, as I'm fairly new.
os.Stdin is an io.Reader. You can't convert it to a []byte, but you can read from it, and the data you read from it, that may be read into a []byte.
Since in many terminals reading from os.Stdin gives data by lines, you should read a complete line from it. Reading from os.Stdin might block until a complete line is available.
For that you have many possibilities, one is to use bufio.Scanner.
This is how you can do it:
scanner := bufio.NewScanner(os.Stdin)
if !scanner.Scan() {
log.Printf("Failed to read: %v", scanner.Err())
return
}
line := scanner.Bytes() // line is of type []byte, exactly what you need

Go encryption differs from Ruby encryption using same key and iv

I have the following Ruby code:
require 'base64'
require 'openssl'
data = '503666666'
key = '4768c01c4f598828ef80d9982d95f888fb952c5b12189c002123e87f751e3e82'
nonce = '4eFi6Q3PX1478767\n'
nonce = Base64.decode64(nonce)
c = OpenSSL::Cipher.new('aes-256-gcm')
c.encrypt
c.key = key
c.iv = nonce
result = c.update(data) + c.final
tag = c.auth_tag
puts Base64.encode64(result + tag) # => J3AVfNG84bz2UuXcfre7LVjSbMpX9XBq6g==\n
that I'm trying to replicate in Golang.
Here's what I have so far:
package main
import (
"fmt"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/hex"
)
func main() {
data := []byte("503666666")
key, err := hex.DecodeString(`4768c01c4f598828ef80d9982d95f888fb952c5b12189c002123e87f751e3e82`)
if err != nil {
panic(err)
}
nonceB64 := "4eFi6Q3PX1478767\n"
nonce, err := base64.StdEncoding.DecodeString(nonceB64)
if err != nil {
panic(err)
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err.Error())
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
ciphertext := aesgcm.Seal(nil, nonce, data, nil)
fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(ciphertext))
}
However the outcome from the Go version is:
+S52HGbLV1xp+GnF0v8VNOqc5J2GY2+SqA==
vs.
J3AVfNG84bz2UuXcfre7LVjSbMpX9XBq6g==\n
Why am I getting different results?
Thanks,
The AES 256 cipher requires a 32 byte key. The Ruby code is setting the key to a 64 byte string consisting of hexadecimal digits. OpenSSL is truncating the string to 32 bytes before use (change key to '4768c01c4f598828ef80d9982d95f888' in the Ruby code and you'll get the same output).
The Go code however is hex decoding the key before use, converting the 64 hexadecimal digits to the 32 bytes required for the key.
If you want to change the Go code so that it matches the Ruby result, then you'll need to truncate the key and remove the hex decoding step:
key := []byte("4768c01c4f598828ef80d9982d95f888")
However, I'd argue that the key handling in the Go version of the code is better. If you want to change the Ruby version to match the Go version, you can hex decode the key before use:
key = [key].pack('H*')

Resources