From be4de07eb893fb60ce1b59576fc8822fd3f2b6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Fri, 3 Feb 2023 00:59:54 +0100 Subject: [PATCH] v0.0.77 --- cmdext/cmdrunner_test.go | 28 ++++++++ cryptext/aes.go | 134 ++++++++++++++++++++++++++++----------- cryptext/aes_test.go | 17 ++++- cryptext/hash_test.go | 6 ++ 4 files changed, 147 insertions(+), 38 deletions(-) diff --git a/cmdext/cmdrunner_test.go b/cmdext/cmdrunner_test.go index 2deac37..9e2d9b8 100644 --- a/cmdext/cmdrunner_test.go +++ b/cmdext/cmdrunner_test.go @@ -1,6 +1,7 @@ package cmdext import ( + "fmt" "testing" "time" ) @@ -196,3 +197,30 @@ func TestPartialReadUnflushedStderr(t *testing.T) { } } + +func TestListener(t *testing.T) { + + _, err := Runner("python"). + Arg("-c"). + Arg("import sys;" + + "import time;" + + "print(\"message 1\", flush=True);" + + "time.sleep(1);" + + "print(\"message 2\", flush=True);" + + "time.sleep(1);" + + "print(\"message 3\", flush=True);" + + "time.sleep(1);" + + "print(\"message 4\", file=sys.stderr, flush=True);" + + "time.sleep(1);" + + "print(\"message 5\", flush=True);" + + "time.sleep(1);" + + "print(\"final\");"). + ListenStdout(func(s string) { fmt.Printf("@@STDOUT <<- %v (%v)\n", s, time.Now().Format(time.RFC3339Nano)) }). + ListenStderr(func(s string) { fmt.Printf("@@STDERR <<- %v (%v)\n", s, time.Now().Format(time.RFC3339Nano)) }). + Timeout(10 * time.Second). + Run() + + if err != nil { + panic(err) + } +} diff --git a/cryptext/aes.go b/cryptext/aes.go index 9546aac..9f9ff41 100644 --- a/cryptext/aes.go +++ b/cryptext/aes.go @@ -1,10 +1,13 @@ package cryptext import ( + "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" - "encoding/base64" + "crypto/sha256" + "encoding/base32" + "encoding/json" "errors" "golang.org/x/crypto/scrypt" "io" @@ -12,35 +15,88 @@ import ( // https://stackoverflow.com/a/18819040/1761622 -func EncryptAESSimple(password, text []byte) ([]byte, error) { - - key, err := scrypt.Key(password, nil, 32768, 8, 1, 32) // this is not 100% correct, rounds too low and salt is missing - if err != nil { - return nil, err - } - - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - b := base64.StdEncoding.EncodeToString(text) - ciphertext := make([]byte, aes.BlockSize+len(b)) - - iv := ciphertext[:aes.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - return nil, err - } - - cfb := cipher.NewCFBEncrypter(block, iv) - cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b)) - - return ciphertext, nil +type aesPayload struct { + Salt []byte `json:"s"` + IV []byte `json:"i"` + Data []byte `json:"d"` + Rounds int `json:"r"` + Version uint `json:"v"` + Algorithm string `json:"a"` } -func DecryptAESSimple(password, text []byte) ([]byte, error) { +func EncryptAESSimple(password []byte, data []byte, rounds int) (string, error) { - key, err := scrypt.Key(password, nil, 32768, 8, 1, 32) // this is not 100% correct, rounds too low and salt is missing + salt := make([]byte, 8) + _, err := io.ReadFull(rand.Reader, salt) + if err != nil { + return "", err + } + + key, err := scrypt.Key(password, salt, rounds, 8, 1, 32) + if err != nil { + return "", err + } + + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + h := sha256.New() + h.Write(data) + checksum := h.Sum(nil) + if len(checksum) != 32 { + return "", errors.New("wrong cs size") + } + + ciphertext := make([]byte, 32+len(data)) + + iv := make([]byte, aes.BlockSize) + _, err = io.ReadFull(rand.Reader, iv) + if err != nil { + return "", err + } + + combinedData := make([]byte, 0, 32+len(data)) + combinedData = append(combinedData, checksum...) + combinedData = append(combinedData, data...) + + cfb := cipher.NewCFBEncrypter(block, iv) + cfb.XORKeyStream(ciphertext, combinedData) + + pl := aesPayload{ + Salt: salt, + IV: iv, + Data: ciphertext, + Version: 1, + Rounds: rounds, + Algorithm: "AES", + } + + jbin, err := json.Marshal(pl) + if err != nil { + return "", err + } + + res := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(jbin) + + return res, nil +} + +func DecryptAESSimple(password []byte, encText string) ([]byte, error) { + + jbin, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(encText) + if err != nil { + return nil, err + } + + var pl aesPayload + err = json.Unmarshal(jbin, &pl) + if err != nil { + return nil, err + } + + key, err := scrypt.Key(password, pl.Salt, pl.Rounds, 8, 1, 32) // this is not 100% correct, rounds too low and salt is missing if err != nil { return nil, err } @@ -50,18 +106,24 @@ func DecryptAESSimple(password, text []byte) ([]byte, error) { return nil, err } - if len(text) < aes.BlockSize { - return nil, errors.New("ciphertext too short") + dest := make([]byte, len(pl.Data)) + + cfb := cipher.NewCFBDecrypter(block, pl.IV) + cfb.XORKeyStream(dest, pl.Data) + + if len(dest) < 32 { + return nil, errors.New("payload too small") } - iv := text[:aes.BlockSize] - text = text[aes.BlockSize:] - cfb := cipher.NewCFBDecrypter(block, iv) - cfb.XORKeyStream(text, text) + chck := dest[:32] + data := dest[32:] - data, err := base64.StdEncoding.DecodeString(string(text)) - if err != nil { - return nil, err + h := sha256.New() + h.Write(data) + chck2 := h.Sum(nil) + + if !bytes.Equal(chck, chck2) { + return nil, errors.New("checksum mismatch") } return data, nil diff --git a/cryptext/aes_test.go b/cryptext/aes_test.go index ec5c5a3..3954537 100644 --- a/cryptext/aes_test.go +++ b/cryptext/aes_test.go @@ -1,6 +1,9 @@ package cryptext -import "testing" +import ( + "fmt" + "testing" +) func TestEncryptAESSimple(t *testing.T) { @@ -8,15 +11,25 @@ func TestEncryptAESSimple(t *testing.T) { str1 := []byte("Hello World") - str2, err := EncryptAESSimple(pw, str1) + str2, err := EncryptAESSimple(pw, str1, 512) if err != nil { panic(err) } + fmt.Printf("%s\n", str2) + str3, err := DecryptAESSimple(pw, str2) if err != nil { panic(err) } assertEqual(t, string(str1), string(str3)) + + str4, err := EncryptAESSimple(pw, str3, 512) + if err != nil { + panic(err) + } + + assertNotEqual(t, string(str2), string(str4)) + } diff --git a/cryptext/hash_test.go b/cryptext/hash_test.go index 1e1febf..421b2ed 100644 --- a/cryptext/hash_test.go +++ b/cryptext/hash_test.go @@ -23,3 +23,9 @@ func assertEqual(t *testing.T, actual string, expected string) { t.Errorf("values differ: Actual: '%v', Expected: '%v'", actual, expected) } } + +func assertNotEqual(t *testing.T, actual string, expected string) { + if actual == expected { + t.Errorf("values do not differ: Actual: '%v', Expected: '%v'", actual, expected) + } +}