Merge pull request #1 from jantytgat/0.1.1-dev.20241024
0.1.1 dev.20241024
This commit is contained in:
76
.github/workflows/codeql.yml
vendored
Normal file
76
.github/workflows/codeql.yml
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '17 21 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
53
examples/encrypt/main.go
Normal file
53
examples/encrypt/main.go
Normal file
@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jantytgat/go-transcrypt/pkg/transcrypt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
var key string
|
||||
if key, err = transcrypt.CreateHexKey(12); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var salt []byte
|
||||
// Uncomment the following lines if you want to use a pre-defined salt
|
||||
// if salt, err = transcrypt.CreateSalt(); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
|
||||
fmt.Println("Key: ", key)
|
||||
fmt.Println("###############")
|
||||
|
||||
var inputString = "hello world"
|
||||
fmt.Println("input:", inputString)
|
||||
var encryptedString string
|
||||
if encryptedString, err = transcrypt.Encrypt(key, salt, transcrypt.AES_256_GCM, inputString); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Encrypted:", encryptedString)
|
||||
|
||||
var decryptedString any
|
||||
if decryptedString, err = transcrypt.Decrypt(key, encryptedString); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Decrypted:", decryptedString)
|
||||
fmt.Println("###############")
|
||||
|
||||
var inputInt = 123456
|
||||
fmt.Println("input:", inputInt)
|
||||
var encryptedInt string
|
||||
if encryptedInt, err = transcrypt.Encrypt(key, salt, transcrypt.AES_256_GCM, inputInt); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Encrypted:", encryptedInt)
|
||||
|
||||
var decryptedInt any
|
||||
if decryptedInt, err = transcrypt.Decrypt(key, encryptedInt); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Decrypted:", decryptedInt)
|
||||
}
|
9
go.mod
9
go.mod
@ -1,3 +1,10 @@
|
||||
module github.com/jantytgat/go-transcrypt
|
||||
|
||||
go 1.23.2
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/minio/sio v0.4.1
|
||||
golang.org/x/crypto v0.32.0
|
||||
)
|
||||
|
||||
require golang.org/x/sys v0.29.0 // indirect
|
||||
|
8
go.sum
Normal file
8
go.sum
Normal file
@ -0,0 +1,8 @@
|
||||
github.com/minio/sio v0.4.1 h1:EMe3YBC1nf+sRQia65Rutxi+Z554XPV0dt8BIBA+a/0=
|
||||
github.com/minio/sio v0.4.1/go.mod h1:oBSjJeGbBdRMZZwna07sX9EFzZy+ywu5aofRiV1g79I=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
23
pkg/transcrypt/cipherSuite.go
Normal file
23
pkg/transcrypt/cipherSuite.go
Normal file
@ -0,0 +1,23 @@
|
||||
package transcrypt
|
||||
|
||||
const (
|
||||
AES_256_GCM CipherSuite = iota
|
||||
CHACHA20_POLY1305
|
||||
)
|
||||
|
||||
// CipherSuite defines which cipher suites can be used for transcryption of data.
|
||||
// It is based on the types available in github.com/minio/sio .
|
||||
type CipherSuite byte
|
||||
|
||||
// GetCipherSuite converts a string into its respective CipherSuite.
|
||||
// It returns CHACHA20_POLY1305 by default if the string cannot be converted.
|
||||
func GetCipherSuite(s string) CipherSuite {
|
||||
switch s {
|
||||
case "AES_256_GCM":
|
||||
return AES_256_GCM
|
||||
case "CHACHA20_POLY1305":
|
||||
return CHACHA20_POLY1305
|
||||
default:
|
||||
return CHACHA20_POLY1305
|
||||
}
|
||||
}
|
37
pkg/transcrypt/cipherSuite_test.go
Normal file
37
pkg/transcrypt/cipherSuite_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package transcrypt
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetCipherSuite(t *testing.T) {
|
||||
type args struct {
|
||||
s string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want CipherSuite
|
||||
}{
|
||||
{
|
||||
name: "AES_256_GCM",
|
||||
args: args{s: "AES_256_GCM"},
|
||||
want: AES_256_GCM,
|
||||
},
|
||||
{
|
||||
name: "CHACHA20_POLY1305",
|
||||
args: args{s: "CHACHA20_POLY1305"},
|
||||
want: CHACHA20_POLY1305,
|
||||
},
|
||||
{
|
||||
name: "random",
|
||||
args: args{s: "random"},
|
||||
want: CHACHA20_POLY1305,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := GetCipherSuite(tt.args.s); got != tt.want {
|
||||
t.Errorf("GetCipherSuite() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
110
pkg/transcrypt/convert.go
Normal file
110
pkg/transcrypt/convert.go
Normal file
@ -0,0 +1,110 @@
|
||||
package transcrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/sio"
|
||||
)
|
||||
|
||||
// Defines the default layout of a string representing encrypted data.
|
||||
// The string is divided in sections delimited by a colon.
|
||||
// 1. Cipher suite
|
||||
// 2. Salt
|
||||
// 3. Data
|
||||
// 4. Original data type
|
||||
var regexEncryptedString = regexp.MustCompile(`\d{2}:[\w\d]{24}:[\w\d]*:[\w\d]*`)
|
||||
|
||||
// convertBytesToValue converts a byte-array to a reflect.Value.
|
||||
// It takes a byte-slice and a reflect.Kind and returns an error if the conversion fails.
|
||||
func convertBytesToValue(d []byte, k reflect.Kind) (reflect.Value, error) {
|
||||
switch k {
|
||||
case reflect.Int:
|
||||
v := reflect.New(reflect.TypeOf(0))
|
||||
v.Elem().SetInt(int64(binary.BigEndian.Uint64(d)))
|
||||
return reflect.ValueOf(v.Elem().Interface()), nil
|
||||
case reflect.Uint64:
|
||||
v := reflect.New(reflect.TypeOf(uint64(0)))
|
||||
v.Elem().SetUint(binary.BigEndian.Uint64(d))
|
||||
return reflect.ValueOf(v.Elem().Interface()), nil
|
||||
case reflect.String:
|
||||
return reflect.ValueOf(string(d)), nil
|
||||
default:
|
||||
return reflect.Value{}, fmt.Errorf("unknown type %v", k)
|
||||
}
|
||||
}
|
||||
|
||||
// convertValueToHexString converts a value to a hex-encoded string.
|
||||
// It returns an empty string and an error if the value is a kind reflect.Int and cannot be converted.
|
||||
func convertValueToHexString(v reflect.Value) (string, error) {
|
||||
var err error
|
||||
switch v.Kind() {
|
||||
case reflect.Int:
|
||||
bufWriter := bytes.NewBuffer(make([]byte, 0))
|
||||
if err = binary.Write(bufWriter, binary.BigEndian, v.Int()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(bufWriter.Bytes()), nil
|
||||
case reflect.String:
|
||||
return hex.EncodeToString([]byte(v.String())), nil
|
||||
default:
|
||||
return "", fmt.Errorf("unknown type %v", v.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
// decodeHexString decodes data into the pieces that make up the encrypted data.
|
||||
// It takes an encrypted key and data string and returns the actual encrypted data as a byte-slice, reflect.Kind and the encryption config.
|
||||
// It returns an error if the data string is empty or invalid, or any of the steps to get the encrypted data fails.
|
||||
func decodeHexString(key string, data string) ([]byte, reflect.Kind, sio.Config, error) {
|
||||
if key == "" {
|
||||
return nil, reflect.Invalid, sio.Config{}, fmt.Errorf("key is empty")
|
||||
}
|
||||
if data == "" {
|
||||
return nil, reflect.Invalid, sio.Config{}, fmt.Errorf("value is empty")
|
||||
}
|
||||
|
||||
if !regexEncryptedString.MatchString(data) {
|
||||
return nil, reflect.Invalid, sio.Config{}, fmt.Errorf("value is not valid")
|
||||
}
|
||||
|
||||
var split []string
|
||||
split = strings.Split(data, ":")
|
||||
|
||||
var err error
|
||||
var cipherSuiteBytes []byte
|
||||
if cipherSuiteBytes, err = hex.DecodeString(split[0]); err != nil {
|
||||
return nil, reflect.Invalid, sio.Config{}, fmt.Errorf("cannot decode cipersuite: %w", err)
|
||||
}
|
||||
|
||||
var salt []byte
|
||||
if salt, err = hex.DecodeString(split[1]); err != nil {
|
||||
return nil, reflect.Invalid, sio.Config{}, fmt.Errorf("cannot decode salt: %w", err)
|
||||
}
|
||||
|
||||
var encryptedBytes []byte
|
||||
if encryptedBytes, err = hex.DecodeString(split[2]); err != nil {
|
||||
return nil, reflect.Invalid, sio.Config{}, fmt.Errorf("cannot decode encrypted data: %w", err)
|
||||
}
|
||||
|
||||
var kindBytes []byte
|
||||
if kindBytes, err = hex.DecodeString(split[3]); err != nil {
|
||||
return nil, reflect.Invalid, sio.Config{}, fmt.Errorf("cannot decode kindBytes: %w", err)
|
||||
}
|
||||
|
||||
var kind reflect.Kind
|
||||
if kind = getKindForString(string(kindBytes)); kind == reflect.Invalid {
|
||||
return nil, reflect.Invalid, sio.Config{}, fmt.Errorf("cannot decode kind: %w", err)
|
||||
}
|
||||
|
||||
var cryptoConfig sio.Config
|
||||
if cryptoConfig, err = createCryptoConfig(key, cipherSuiteBytes, salt); err != nil {
|
||||
return nil, reflect.Invalid, sio.Config{}, fmt.Errorf("cannot create crypto config: %w", err)
|
||||
}
|
||||
|
||||
return encryptedBytes, kind, cryptoConfig, nil
|
||||
}
|
291
pkg/transcrypt/convert_test.go
Normal file
291
pkg/transcrypt/convert_test.go
Normal file
@ -0,0 +1,291 @@
|
||||
package transcrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/sio"
|
||||
)
|
||||
|
||||
func Test_convertBytesToValue_String(t *testing.T) {
|
||||
type args struct {
|
||||
d []byte
|
||||
k reflect.Kind
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want reflect.Value
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "string",
|
||||
args: args{
|
||||
d: []byte("hello world"),
|
||||
k: reflect.TypeOf("hello world").Kind(),
|
||||
},
|
||||
want: reflect.ValueOf("hello world"),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := convertBytesToValue(tt.args.d, tt.args.k)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("convertBytesToValue() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got.String(), tt.want.String()) {
|
||||
t.Errorf("convertBytesToValue() got = %v, want = %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_convertBytesToValue_Uint(t *testing.T) {
|
||||
type args struct {
|
||||
d []byte
|
||||
k reflect.Kind
|
||||
}
|
||||
var inputUint uint64 = 132130
|
||||
bufWriterUint := bytes.NewBuffer(make([]byte, 0))
|
||||
if bufErr := binary.Write(bufWriterUint, binary.BigEndian, inputUint); bufErr != nil {
|
||||
panic(bufErr)
|
||||
}
|
||||
|
||||
var inputInt = 132130
|
||||
bufWriterInt := bytes.NewBuffer(make([]byte, 0))
|
||||
if bufErr := binary.Write(bufWriterInt, binary.BigEndian, int64(inputInt)); bufErr != nil {
|
||||
panic(bufErr)
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want reflect.Value
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "uint64",
|
||||
args: args{
|
||||
d: bufWriterUint.Bytes(),
|
||||
k: reflect.TypeOf(inputUint).Kind(),
|
||||
},
|
||||
want: reflect.ValueOf(inputUint),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "int64",
|
||||
args: args{
|
||||
d: bufWriterInt.Bytes(),
|
||||
k: reflect.TypeOf(inputInt).Kind(),
|
||||
},
|
||||
want: reflect.ValueOf(inputInt),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := convertBytesToValue(tt.args.d, tt.args.k)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("convertBytesToValue() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got == tt.want {
|
||||
t.Errorf("convertBytesToValue() got = %v, want = %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_convertValueToHexString(t *testing.T) {
|
||||
type args struct {
|
||||
v reflect.Value
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "string",
|
||||
args: args{
|
||||
v: reflect.ValueOf("hello world"),
|
||||
},
|
||||
want: "68656c6c6f20776f726c64",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "int",
|
||||
args: args{
|
||||
v: reflect.ValueOf(132130),
|
||||
},
|
||||
want: "0000000000020422",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := convertValueToHexString(tt.args.v)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("convertValueToHexString() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("convertValueToHexString() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_decodeHexString(t *testing.T) {
|
||||
type args struct {
|
||||
key string
|
||||
data string
|
||||
}
|
||||
|
||||
decodedHexKey, _ := hex.DecodeString("308204a20201000282010100b44756063cdb25f2e5ec868ecc5edc733896d637f5e0ffd8a3f820fb0f2acb2268c69a7774c2d4c4ede1ec47d894bbb0f928e9e700db03767b6152548c49f3170d011dd87aa3d36a1f49d38756bcbc6e122d362ff3986165985d264f11ba8e1d35006b66290c4efa8dfd6b4bcbb5e3fbf9277d31ffaa09b319a976313c3bcd25f3b74be78df293bd7b38e5a6caf48be79684882af47630eb147ecb4426e9e7e6e9554b1d8b53530c5c22cfe44e95bfeeca4bb79f1a3c08e5de93d3bf458aae5e821e9e6c4ec6c3602b9b1f56b31da14775608374ec8653e34e2025901cbef725241b166187972dc1c1913f2c8ba54f1ceb443b4019096fac816cf6334aa2e3f50203010001028201004703b2c75241a17945491ed831794cadb6a4f44da6f5b2d2cc047a396b8817ecbe093ddfc086def9941b62d00a68cc66b23f83a4139a328b019f1ca7617bdfde3ca92bf0929ee630ef924d590ab9de201dd8e17792257c7860c490caa4d930122146c107c533ac08d6d5f4e62ea0bfe60a079c318ddc95658fbe4968aba982edbe0775e71eba35836758b2c486e54e54de4cbb3b7004a9e16b0d0da3b88dbf026413d97e0396aec1f739d504b4eb75a7719fb7bda9886c78a99a79a55ad2b06c36a46ac6df71cd021296020865a2f79b3a0950f9268a1fc773ab407e3e283c10f6fe9412acbae260c2a5c601d75a731db18294a91d20cce7d73d039d7162742102818100efad47d757feb7f570f55f81b7b0fe271de91633234146b5f9fb682b3aafd574481bbd49c85b27c4c118f2634755800b4eb967e46ff6ca4f392bac226410ce31b7c0d22735e61928a940d36033aac6aaa088d21a36720b90904a7ea8b62f998a99938d5ced7b8f2eb2339e34559b25506c43bed054562543a260f426c3136ac702818100c08e75852d7375a09087a5e793d7372e3804257dda911408a1270a14a825879e26d5c7b85796a5ff6f4a290a9185cc097d3eebe0bb2fd8520b50cc9f4bb30a7815a51a23d5ea752f8035c242bce5839ad4f4c6fcb5921e1a8a8f672fc378a7afc57a04c2e882c21414c656b84bf8c3efd4ea0dbbd4269e048e66196d57b99f630281807f592ce0e8ea78c83afac582611df40cc8c1be7ff16d8faac566a5d4c25c0728bfdfad55f4d52a6e4ac37c96efa22864d9b17dd84cfd6e4565f5248329741c7b224d9bdc25b15b10d5cd92027db171d9db6e976442259aab775f7da91b14739ac73b35537903bbf26dd12b7057441631833503c021ef9be131f81e023288b0a50281807cc552bd4320479e0d48f865c0547a3b06ad19261dd45828e75386a2aff9f190b7155b5ec5d2a629881183da87452d5b10bf0ed506361073c94547f20879315572a112f91989dcf93498a111e198ced82b19993ef2e08585293796e34a440a54491fb1aa22436842dedb4e2209885e5e2f96a1e38daaa045cf87b4fe3713de8502818021680ea1a93b048af3deec6fd09d9d110d391e908cb6eeab615e5595556f07238c44f7b8795cffe3508aa3dff21de927576454537473e9b07eaedf0864f4a5a0f6275fbef3f6c9b42f7c692b351451004c25c4918edabbd1f22e5d174aac550db7327e99cb58be7641ec0479425bf00f8b3640685a95e70c93e820c7f39e1a1e")
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantData []byte
|
||||
wantKind reflect.Kind
|
||||
wantConfig sio.Config
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty_key",
|
||||
args: args{
|
||||
key: "",
|
||||
data: "",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.Invalid,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty_data",
|
||||
args: args{
|
||||
key: string(decodedHexKey),
|
||||
data: "",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.Invalid,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_value_ciphersuite",
|
||||
args: args{
|
||||
key: string(decodedHexKey),
|
||||
data: "__:68e191dfc1f3180904d19a58:20001500e8e191dfc1f3180904d19a589d6c41d057473145672f5e7a90b1fa1d47b21ece952eafbbfa38668f2885b323179721bc10a5:737472696e67",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.Invalid,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_value_nonce",
|
||||
args: args{
|
||||
key: string(decodedHexKey),
|
||||
data: "00:__:20001500e8e191dfc1f3180904d19a589d6c41d057473145672f5e7a90b1fa1d47b21ece952eafbbfa38668f2885b323179721bc10a5:737472696e67",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.Invalid,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_value_data",
|
||||
args: args{
|
||||
key: string(decodedHexKey),
|
||||
data: "00:68e191dfc1f3180904d19a58:__:737472696e67",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.Invalid,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_value_kind",
|
||||
args: args{
|
||||
key: string(decodedHexKey),
|
||||
data: "00:68e191dfc1f3180904d19a58:20001500e8e191dfc1f3180904d19a589d6c41d057473145672f5e7a90b1fa1d47b21ece952eafbbfa38668f2885b323179721bc10a5:__",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.Invalid,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_ciphersuite",
|
||||
args: args{
|
||||
key: string(decodedHexKey),
|
||||
data: "dd:68e191dfc1f3180904d19a58:20001500e8e191dfc1f3180904d19a589d6c41d057473145672f5e7a90b1fa1d47b21ece952eafbbfa38668f2885b323179721bc10a5:737472696e67",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.String,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_nonce",
|
||||
args: args{
|
||||
key: string(decodedHexKey),
|
||||
data: "00:dddddddddddddddddddddddd:20001500e8e191dfc1f3180904d19a589d6c41d057473145672f5e7a90b1fa1d47b21ece952eafbbfa38668f2885b323179721bc10a5:737472696e67",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.String,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_data",
|
||||
args: args{
|
||||
key: string(decodedHexKey),
|
||||
data: "00:68e191dfc1f3180904d19a58:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd:737472696e67",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.String,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_kind",
|
||||
args: args{
|
||||
key: string(decodedHexKey),
|
||||
data: "00:68e191dfc1f3180904d19a58:20001500e8e191dfc1f3180904d19a589d6c41d057473145672f5e7a90b1fa1d47b21ece952eafbbfa38668f2885b323179721bc10a5:dddddddddddd",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.String,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: string(decodedHexKey),
|
||||
data: "00:68e191dfc1f3180904d19a58:20001500e8e191dfc1f3180904d19a589d6c41d057473145672f5e7a90b1fa1d47b21ece952eafbbfa38668f2885b323179721bc10a5:737472696e67",
|
||||
},
|
||||
wantData: nil,
|
||||
wantKind: reflect.String,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotData, gotKind, gotConfig, err := decodeHexString(tt.args.key, tt.args.data)
|
||||
if err != nil {
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("decodeHexString() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
if string(gotData) == string(tt.wantData) {
|
||||
t.Errorf("decodeHexString() gotData = %v, wantData %v", gotData, tt.wantData)
|
||||
}
|
||||
if gotKind != tt.wantKind {
|
||||
t.Errorf("decodeHexString() gotKind = %v, wantKind %v", gotKind, tt.wantKind)
|
||||
}
|
||||
if string(gotConfig.CipherSuites) == string(tt.wantConfig.CipherSuites) {
|
||||
t.Errorf("decodeHexString() gotCipherSuites = %v, wantCipherSuites %v", gotConfig, tt.wantConfig)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
148
pkg/transcrypt/crypto.go
Normal file
148
pkg/transcrypt/crypto.go
Normal file
@ -0,0 +1,148 @@
|
||||
package transcrypt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"github.com/minio/sio"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// CreateHexKey generates a random key which can be used for encryption.
|
||||
// It generates a RSA Private Key with the supplied bitSize, and converts it to a hex-encoded PEM Block.
|
||||
func CreateHexKey(bitSize int) (string, error) {
|
||||
if bitSize < 12 {
|
||||
return "", errors.New("bit size must be at least 12")
|
||||
}
|
||||
var err error
|
||||
var privKey *rsa.PrivateKey
|
||||
|
||||
var reader = rand.Reader
|
||||
|
||||
if privKey, err = rsa.GenerateKey(reader, bitSize); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hex.EncodeToString(pem.EncodeToMemory(
|
||||
&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(privKey),
|
||||
})), nil
|
||||
}
|
||||
|
||||
// CreateSalt creates a random 12-byte salt for use with the encrypt/decrypt functionality.
|
||||
func CreateSalt() ([]byte, error) {
|
||||
var nonce [12]byte
|
||||
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
||||
return nil, fmt.Errorf("failed to read random data for nonce: %w", err)
|
||||
}
|
||||
|
||||
return nonce[:], nil
|
||||
}
|
||||
|
||||
// createCryptoConfig creates a sio.config from the supplied key, cipher and optional salt.
|
||||
// It returns an error if either key or cipher is empty.
|
||||
// It also returns an error if the supplied salt is less than 12 bytes long.
|
||||
func createCryptoConfig(key string, cipher []byte, salt []byte) (sio.Config, error) {
|
||||
if key == "" {
|
||||
return sio.Config{}, errors.New("key is empty")
|
||||
}
|
||||
|
||||
if cipher == nil {
|
||||
return sio.Config{}, errors.New("cipher is empty")
|
||||
}
|
||||
|
||||
var err error
|
||||
// If salt is nil, create a new salt that can be used for encryption
|
||||
if salt == nil {
|
||||
if salt, err = CreateSalt(); err != nil {
|
||||
return sio.Config{}, fmt.Errorf("could not create salt: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(salt) < 12 {
|
||||
return sio.Config{}, fmt.Errorf("salt needs to be at least 12 bytes, got %d", len(salt))
|
||||
}
|
||||
|
||||
// Create encryption key
|
||||
kdf := hkdf.New(sha256.New, []byte(key), salt[:12], nil)
|
||||
var encKey [32]byte
|
||||
if _, err = io.ReadFull(kdf, encKey[:]); err != nil {
|
||||
return sio.Config{}, fmt.Errorf("failed to derive encryption encKey: %w", err)
|
||||
}
|
||||
|
||||
return sio.Config{
|
||||
CipherSuites: cipher,
|
||||
Key: encKey[:],
|
||||
Nonce: (*[12]byte)(salt[:]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getKindFromString converts a string to its representative reflect.Kind.
|
||||
// It returns a reflect.Invalid by default if the supplied string cannot be found.
|
||||
func getKindForString(s string) reflect.Kind {
|
||||
switch s {
|
||||
case "bool":
|
||||
return reflect.Bool
|
||||
case "int":
|
||||
return reflect.Int
|
||||
case "int8":
|
||||
return reflect.Int8
|
||||
case "int16":
|
||||
return reflect.Int16
|
||||
case "int32":
|
||||
return reflect.Int32
|
||||
case "int64":
|
||||
return reflect.Int64
|
||||
case "uint":
|
||||
return reflect.Uint
|
||||
case "uint8":
|
||||
return reflect.Uint8
|
||||
case "uint16":
|
||||
return reflect.Uint16
|
||||
case "uint32":
|
||||
return reflect.Uint32
|
||||
case "uint64":
|
||||
return reflect.Uint64
|
||||
case "uintptr":
|
||||
return reflect.Uintptr
|
||||
case "float32":
|
||||
return reflect.Float32
|
||||
case "float64":
|
||||
return reflect.Float64
|
||||
case "complex64":
|
||||
return reflect.Complex64
|
||||
case "complex128":
|
||||
return reflect.Complex128
|
||||
case "array":
|
||||
return reflect.Array
|
||||
case "chan":
|
||||
return reflect.Chan
|
||||
case "func":
|
||||
return reflect.Func
|
||||
case "interface":
|
||||
return reflect.Interface
|
||||
case "map":
|
||||
return reflect.Map
|
||||
case "pointer":
|
||||
return reflect.Pointer
|
||||
case "slice":
|
||||
return reflect.Slice
|
||||
case "string":
|
||||
return reflect.String
|
||||
case "struct":
|
||||
return reflect.Struct
|
||||
case "unsafepointer":
|
||||
return reflect.UnsafePointer
|
||||
default:
|
||||
return reflect.Invalid
|
||||
}
|
||||
}
|
288
pkg/transcrypt/crypto_test.go
Normal file
288
pkg/transcrypt/crypto_test.go
Normal file
@ -0,0 +1,288 @@
|
||||
package transcrypt
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_CreateHexKey(t *testing.T) {
|
||||
type args struct {
|
||||
bitSize int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
bitSize int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "invalid_size_0",
|
||||
bitSize: 0,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_size_11",
|
||||
bitSize: 11,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid_size_12",
|
||||
bitSize: 12,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid_size_256",
|
||||
bitSize: 256,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid_size_1024",
|
||||
bitSize: 1024,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid_size_2048",
|
||||
bitSize: 2048,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := CreateHexKey(tt.bitSize)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("CreateHexKey() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CreateSalt(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := CreateSalt()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("CreateSalt() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_createCryptoConfig(t *testing.T) {
|
||||
type args struct {
|
||||
key string
|
||||
cipher []byte
|
||||
salt []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty_key",
|
||||
args: args{
|
||||
key: "test",
|
||||
cipher: nil,
|
||||
salt: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty_cipher",
|
||||
args: args{
|
||||
key: "test",
|
||||
cipher: nil,
|
||||
salt: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_salt",
|
||||
args: args{
|
||||
key: "test",
|
||||
cipher: []byte("cipher"),
|
||||
salt: []byte("salt"),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: "test",
|
||||
cipher: []byte("cipher"),
|
||||
salt: nil,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := createCryptoConfig(tt.args.key, tt.args.cipher, tt.args.salt)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("createCryptoConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getKindForString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
kind string
|
||||
want reflect.Kind
|
||||
}{
|
||||
{
|
||||
name: "bool",
|
||||
kind: "bool",
|
||||
want: reflect.Bool,
|
||||
},
|
||||
{
|
||||
name: "int",
|
||||
kind: "int",
|
||||
want: reflect.Int,
|
||||
},
|
||||
{
|
||||
name: "int8",
|
||||
kind: "int8",
|
||||
want: reflect.Int8,
|
||||
},
|
||||
{
|
||||
name: "int16",
|
||||
kind: "int16",
|
||||
want: reflect.Int16,
|
||||
},
|
||||
{
|
||||
name: "int32",
|
||||
kind: "int32",
|
||||
want: reflect.Int32,
|
||||
},
|
||||
{
|
||||
name: "int64",
|
||||
kind: "int64",
|
||||
want: reflect.Int64,
|
||||
},
|
||||
{
|
||||
name: "uint",
|
||||
kind: "uint",
|
||||
want: reflect.Uint,
|
||||
},
|
||||
{
|
||||
name: "uint8",
|
||||
kind: "uint8",
|
||||
want: reflect.Uint8,
|
||||
},
|
||||
{
|
||||
name: "uint16",
|
||||
kind: "uint16",
|
||||
want: reflect.Uint16,
|
||||
},
|
||||
{
|
||||
name: "uint32",
|
||||
kind: "uint32",
|
||||
want: reflect.Uint32,
|
||||
},
|
||||
{
|
||||
name: "uint64",
|
||||
kind: "uint64",
|
||||
want: reflect.Uint64,
|
||||
},
|
||||
{
|
||||
name: "uintptr",
|
||||
kind: "uintptr",
|
||||
want: reflect.Uintptr,
|
||||
},
|
||||
{
|
||||
name: "float32",
|
||||
kind: "float32",
|
||||
want: reflect.Float32,
|
||||
},
|
||||
{
|
||||
name: "float64",
|
||||
kind: "float64",
|
||||
want: reflect.Float64,
|
||||
},
|
||||
{
|
||||
name: "complex64",
|
||||
kind: "complex64",
|
||||
want: reflect.Complex64,
|
||||
},
|
||||
{
|
||||
name: "complex128",
|
||||
kind: "complex128",
|
||||
want: reflect.Complex128,
|
||||
},
|
||||
{
|
||||
name: "array",
|
||||
kind: "array",
|
||||
want: reflect.Array,
|
||||
},
|
||||
{
|
||||
name: "chan",
|
||||
kind: "chan",
|
||||
want: reflect.Chan,
|
||||
},
|
||||
{
|
||||
name: "func",
|
||||
kind: "func",
|
||||
want: reflect.Func,
|
||||
},
|
||||
{
|
||||
name: "interface",
|
||||
kind: "interface",
|
||||
want: reflect.Interface,
|
||||
},
|
||||
{
|
||||
name: "map",
|
||||
kind: "map",
|
||||
want: reflect.Map,
|
||||
},
|
||||
{
|
||||
name: "pointer",
|
||||
kind: "pointer",
|
||||
want: reflect.Pointer,
|
||||
},
|
||||
{
|
||||
name: "slice",
|
||||
kind: "slice",
|
||||
want: reflect.Slice,
|
||||
},
|
||||
{
|
||||
name: "string",
|
||||
kind: "string",
|
||||
want: reflect.String,
|
||||
},
|
||||
{
|
||||
name: "struct",
|
||||
kind: "struct",
|
||||
want: reflect.Struct,
|
||||
},
|
||||
{
|
||||
name: "unsafepointer",
|
||||
kind: "unsafepointer",
|
||||
want: reflect.UnsafePointer,
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
kind: "default",
|
||||
want: reflect.Invalid,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := getKindForString(tt.kind); got != tt.want {
|
||||
t.Errorf("getKindForString() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
104
pkg/transcrypt/transcrypt.go
Normal file
104
pkg/transcrypt/transcrypt.go
Normal file
@ -0,0 +1,104 @@
|
||||
// Package transcrypt provides functionality to encrypt arbitrary data into a hex encoded string for safe on-disk storage, and decrypt said string.
|
||||
package transcrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/sio"
|
||||
)
|
||||
|
||||
// Decrypt decrypts a supplied hex-encoded data string using the supplied secret key.
|
||||
// It will return an error if either the key or the data is empty.
|
||||
// If the hex-encoded string data cannot be converted into proper encrypted data, decryption will also fail with an error.
|
||||
func Decrypt(key string, data string) (any, error) {
|
||||
if key == "" {
|
||||
return nil, errors.New("key is empty")
|
||||
}
|
||||
if data == "" {
|
||||
return nil, errors.New("data is empty")
|
||||
}
|
||||
|
||||
var err error
|
||||
var encryptedData []byte
|
||||
var kind reflect.Kind
|
||||
var cryptoConfig sio.Config
|
||||
|
||||
if encryptedData, kind, cryptoConfig, err = decodeHexString(key, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var decryptedHexData *bytes.Buffer
|
||||
decryptedHexData = bytes.NewBuffer(make([]byte, 0))
|
||||
if _, err = sio.Decrypt(decryptedHexData, bytes.NewBuffer(encryptedData), cryptoConfig); err != nil {
|
||||
return nil, fmt.Errorf("decrypt failed: %w", err)
|
||||
}
|
||||
|
||||
var decryptedData []byte
|
||||
if decryptedData, err = hex.DecodeString(string(decryptedHexData.Bytes())); err != nil {
|
||||
return nil, fmt.Errorf("decode decrypted hex data failed: %w", err)
|
||||
}
|
||||
|
||||
var outputValue reflect.Value
|
||||
if outputValue, err = convertBytesToValue(decryptedData, kind); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return outputValue.Interface(), nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts the supplied data using the supplied secret key and cipher suite.
|
||||
// It will return an error if either the key is empty or the data is nil.
|
||||
// Additionally, if the necessary cryptographic configuration cannot be created using the supplied cipherSuite, it will return an error.
|
||||
// If a salt is provided, it must be at least 12 bytes.
|
||||
// If salt is nil, the function will automatically create one on-the-fly.
|
||||
func Encrypt(key string, salt []byte, cipherSuite CipherSuite, d any) (string, error) {
|
||||
if key == "" {
|
||||
return "", errors.New("key is empty")
|
||||
}
|
||||
|
||||
if d == nil {
|
||||
return "", errors.New("data is nil")
|
||||
}
|
||||
|
||||
if salt != nil && len(salt) < 12 {
|
||||
return "", fmt.Errorf("salt needs to be at least 12 bytes, got %d", len(salt))
|
||||
}
|
||||
|
||||
var err error
|
||||
var data string
|
||||
// Convert input data to reflect.Value before serialization
|
||||
if data, err = convertValueToHexString(reflect.ValueOf(d)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var cryptoConfig sio.Config
|
||||
if cryptoConfig, err = createCryptoConfig(key, []byte{byte(cipherSuite)}, salt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
encryptedData := bytes.NewBuffer(make([]byte, 0))
|
||||
if _, err = sio.Encrypt(encryptedData, bytes.NewBuffer([]byte(data)), cryptoConfig); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Encode all details in hex before joining together
|
||||
encryptedString := strings.Join(
|
||||
[]string{
|
||||
hex.EncodeToString([]byte{byte(cipherSuite)}),
|
||||
hex.EncodeToString(cryptoConfig.Nonce[:]),
|
||||
hex.EncodeToString(encryptedData.Bytes()),
|
||||
hex.EncodeToString([]byte(reflect.TypeOf(d).Kind().String())),
|
||||
}, ":",
|
||||
)
|
||||
|
||||
if !regexEncryptedString.MatchString(encryptedString) {
|
||||
return "", fmt.Errorf("could not validate encrypted data")
|
||||
}
|
||||
|
||||
return encryptedString, nil
|
||||
}
|
166
pkg/transcrypt/transcrypt_test.go
Normal file
166
pkg/transcrypt/transcrypt_test.go
Normal file
@ -0,0 +1,166 @@
|
||||
package transcrypt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecrypt(t *testing.T) {
|
||||
type args struct {
|
||||
key string
|
||||
data string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want any
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty_key",
|
||||
args: args{
|
||||
key: "",
|
||||
data: "",
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty_data",
|
||||
args: args{
|
||||
key: "key",
|
||||
data: "",
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_data",
|
||||
args: args{
|
||||
key: "key",
|
||||
data: "invalid_data",
|
||||
},
|
||||
want: "hello world",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_key",
|
||||
args: args{
|
||||
key: "key",
|
||||
data: "00:5a412cac418ecf54f86c0da4:20001500da412cac418ecf54f86c0da472bb69380c4abb66a0f8542e4b147d01fa503589bb4e3a37c2e2f979d4721da17397089d1477:737472696e67",
|
||||
},
|
||||
want: "hello world",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid_string",
|
||||
args: args{
|
||||
key: "2d2d2d2d2d424547494e205253412050524956415445204b45592d2d2d2d2d0a4d423843415141434167773341674d42414145434167635a41674537416745314167455441674578416745780a2d2d2d2d2d454e44205253412050524956415445204b45592d2d2d2d2d0a",
|
||||
data: "00:5a412cac418ecf54f86c0da4:20001500da412cac418ecf54f86c0da472bb69380c4abb66a0f8542e4b147d01fa503589bb4e3a37c2e2f979d4721da17397089d1477:737472696e67",
|
||||
},
|
||||
want: "hello world",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid_int",
|
||||
args: args{
|
||||
key: "2d2d2d2d2d424547494e205253412050524956415445204b45592d2d2d2d2d0a4d423843415141434167773341674d42414145434167635a41674537416745314167455441674578416745780a2d2d2d2d2d454e44205253412050524956415445204b45592d2d2d2d2d0a",
|
||||
data: "00:41ce7b530435c9189a203937:20000f00c1ce7b530435c9189a20393717bf895ce6d904a75640a6de8d2e33ab3c2fb3751e2825e9f6f2b23f5bf4df12:696e74",
|
||||
},
|
||||
want: 123456,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Decrypt(tt.args.key, tt.args.data)
|
||||
if err != nil {
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Decrypt() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Decrypt() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
type args struct {
|
||||
key string
|
||||
salt []byte
|
||||
cipherSuite CipherSuite
|
||||
d any
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty_key",
|
||||
args: args{
|
||||
key: "",
|
||||
salt: nil,
|
||||
cipherSuite: AES_256_GCM,
|
||||
d: nil,
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty_data",
|
||||
args: args{
|
||||
key: "key",
|
||||
salt: nil,
|
||||
cipherSuite: AES_256_GCM,
|
||||
d: nil,
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_salt",
|
||||
args: args{
|
||||
key: "key",
|
||||
salt: []byte("invalid"),
|
||||
cipherSuite: AES_256_GCM,
|
||||
d: "data",
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: "2d2d2d2d2d424547494e205253412050524956415445204b45592d2d2d2d2d0a4d423843415141434167773341674d42414145434167635a41674537416745314167455441674578416745780a2d2d2d2d2d454e44205253412050524956415445204b45592d2d2d2d2d0a",
|
||||
salt: []byte("saltsaltsalt"),
|
||||
cipherSuite: AES_256_GCM,
|
||||
d: "hello world",
|
||||
},
|
||||
want: "00:73616c7473616c7473616c74:20001500f3616c7473616c7473616c74da182aeef9d1060ec5564b974689147f32bf626db98a13a0f4f6adf6df675dd07fa1463e3d1e:737472696e67",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Encrypt(tt.args.key, tt.args.salt, tt.args.cipherSuite, tt.args.d)
|
||||
if err != nil {
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Encrypt() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("Encrypt() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user