Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
15e9fe1
refactored out the http retries that the performance tool does fso th…
andy-shacklady-vodafone Dec 13, 2022
5e35823
paremeterise the internal service
andy-shacklady-vodafone Dec 13, 2022
818b4e2
map and slice manipulation and parallelisation of encrypt/decrypt on …
andy-shacklady-vodafone Dec 15, 2022
ffce568
add farm option to performance tool
andy-shacklady-vodafone Dec 15, 2022
6cc0273
fixing type conversion for MAX_BATCHROWS
andy-shacklady-vodafone Dec 16, 2022
6643d6e
fixing type conversion for MAX_BATCHROWS for decrypt
andy-shacklady-vodafone Dec 16, 2022
70d86b0
performance tuning: refactor out the key read as I think this is expe…
andy-shacklady-vodafone Dec 17, 2022
4a8869a
if we are going to use caching, make sure the cache is reinitialised …
andy-shacklady-vodafone Dec 17, 2022
4e2819c
tuning and metrics to see whats going on
andy-shacklady-vodafone Dec 20, 2022
d1a3566
change the cloudbuild GOPRIVATE
andy-shacklady-vodafone Jan 19, 2023
dbfb8e8
add wildcard to GOPRIVATE in cloud build
andy-shacklady-vodafone Jan 19, 2023
0b7d17e
remove the thread unsafe counter, add some extra logging
andy-shacklady-vodafone Jan 19, 2023
a46d7f3
optimisations - read config less, and bug fix for column based ops
andy-shacklady-vodafone Jan 19, 2023
d2cf82f
extra debug
andy-shacklady-vodafone Jan 19, 2023
a6a431e
extra logging and make sure config is read before try to get keys
andy-shacklady-vodafone Jan 20, 2023
d47769d
experimentl add of opentelemetry
andy-shacklady-vodafone Feb 4, 2023
71dc4a8
mod tidy and add missing opentelemetry.go
andy-shacklady-vodafone Feb 5, 2023
160de49
use go v1.18 for opentel
andy-shacklady-vodafone Feb 5, 2023
d0d811b
switching the builder to go-1.18
andy-shacklady-vodafone Feb 5, 2023
8c1ed9c
experimentation
andy-shacklady-vodafone Feb 5, 2023
305c5fa
opentelemetry improvements
andy-shacklady-vodafone Feb 6, 2023
55db8f8
opentel iteration
andy-shacklady-vodafone Feb 6, 2023
41ad687
more spans into Farm methods
andy-shacklady-vodafone Feb 7, 2023
aa16ebf
parallelise decrypt routine
andy-shacklady-vodafone Feb 8, 2023
1dfa834
marginal improvement to performance
andy-shacklady-vodafone Feb 13, 2023
4868f54
add fixdata option
andy-shacklady-vodafone Feb 13, 2023
09451fd
optionally split the data by number of cpus and add extra span info
andy-shacklady-vodafone Feb 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 243 additions & 26 deletions aeadutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@ package aeadplugin

import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"net/url"
"strconv"
"strings"
"time"

lorem "github.com/bozaro/golorem"
backoff "github.com/cenkalti/backoff/v4"

"github.com/google/tink/go/aead"
"github.com/google/tink/go/daead"
"github.com/google/tink/go/insecurecleartextkeyset"
"github.com/google/tink/go/keyset"
"github.com/google/tink/go/tink"

hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-retryablehttp"
)

func CreateInsecureHandleAndAead(rawKeyset string) (*keyset.Handle, tink.AEAD, error) {
Expand Down Expand Up @@ -391,38 +400,17 @@ func isEncryptionJsonKey(keyStr string) bool {
return strings.Contains(keyStr, "primaryKeyId")
}

func isKeyJsonDeterministic(encryptionkey interface{}) (string, bool) {
encryptionKeyStr := fmt.Sprintf("%v", encryptionkey)
func isKeyJsonDeterministic(encryptionkeyIntf interface{}) (string, bool) {
encryptionKeyStr := fmt.Sprintf("%v", encryptionkeyIntf)
deterministic := false
if strings.Contains(encryptionKeyStr, "AesSivKey") {
deterministic = true
}
return encryptionKeyStr, deterministic
}

func getEncryptionKey(fieldName string, setDepth ...int) (interface{}, bool) {
maxDepth := 5
if len(setDepth) > 0 {
maxDepth = setDepth[0]
}
possiblyEncryptionKey, ok := AEAD_CONFIG.Get(fieldName)
if !ok {
return nil, ok
}
for i := 1; i < maxDepth; i++ {
possiblyEncryptionKeyStr := possiblyEncryptionKey.(string)
if !isEncryptionJsonKey(possiblyEncryptionKeyStr) {
possiblyEncryptionKey, ok = AEAD_CONFIG.Get(possiblyEncryptionKeyStr)
if !ok {
return nil, ok
}
} else {
return possiblyEncryptionKey, true
}
}

isKeysetFound := false
return nil, isKeysetFound
func IsKeyJsonDeterministic(encryptionkeyIntf interface{}) (string, bool) {
return isKeyJsonDeterministic(encryptionkeyIntf)
}

func muteKeyMaterial(theKey string) string {
Expand All @@ -444,3 +432,232 @@ func muteKeyMaterial(theKey string) string {
}
return mutedMaterial
}

func createHttpClient(httpProxy string) *retryablehttp.Client {
var tr *http.Transport
if httpProxy == "" {
tr = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
} else {
proxyUrl, _ := url.Parse(httpProxy)
tr = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
Proxy: http.ProxyURL(proxyUrl),
}
}

httpClient := &http.Client{Transport: tr}
client := retryablehttp.NewClient()
client.HTTPClient = httpClient
client.RetryMax = 10 // max 10 retries
client.RetryWaitMax = 300 * time.Second // max 5 mins between retries
return client
}

func goDoHttp(inputData map[string]interface{}, url string, bodyMap map[string]interface{}, httpProxy string, token string) error {

client := createHttpClient(httpProxy)
payloadBytes, err := json.Marshal(inputData)
if err != nil {
fmt.Printf("goDoHttp json.Marshal Error=%v\n", err)
return err
}
inputBody := bytes.NewReader(payloadBytes)

req, err := retryablehttp.NewRequest(http.MethodPost, url, inputBody)

if err != nil {
fmt.Printf("goDoHttp http.NewRequest Error=%v\n", err)
return err
}
req.Header.Set("X-Vault-Token", token)
req.Header.Set("Content-Type", "application/json")

resp, err := client.Do(req)
if err != nil {
fmt.Printf("goDoHttp client.Do Error=%v\n", err)
return err
}

defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("goDoHttp io.ReadAll Error=%v\n", err)
return err
}

err = json.Unmarshal([]byte(body), &bodyMap)
if err != nil {
fmt.Printf("goDoHttp Unmarshall Error=%v\n", err)
return err
}
return nil
}

func EncryptOrDecryptDataChan(url string, inputMap map[string]interface{}, httpProxy string, token string, ch chan map[string]interface{}) {
dataMap, err := EncryptOrDecryptData(url, inputMap, httpProxy, token)
if err != nil {
// TODO - not sure on putting a panic here
panic(err)
}
ch <- dataMap
}

func EncryptOrDecryptData(url string, inputMap map[string]interface{}, httpProxy string, token string) (map[string]interface{}, error) {

response := make(map[string]interface{})
emptyMap := make(map[string]interface{})
data := make(map[string]interface{})
ok := true
i := 0

// I absolutely hate that you can only wrap a function with no args that returns an error "func() error" here
// so I have to rely on variable scope, but life is too short
operation := func() error {
// if i > 0 {
// fmt.Printf("Retry=%v encryptOrDecryptData\n", i)
// }
i++
err := goDoHttp(inputMap, url, response, httpProxy, token)
if err != nil {
fmt.Printf("encryptOrDecryptData Try=%v Error after goDoHttp=%v\n", i, err)
return err
}
data, ok = response["data"].(map[string]interface{})
if !ok {
errors, ok := response["errors"].([]interface{})
if ok {
// we have errors from vault
fmt.Printf("encryptOrDecryptData Try=%v Vault Response=%v\n", i, errors)
return fmt.Errorf("error response from vault %v", errors)
} else {
// we have errors but no idea why
fmt.Printf("encryptOrDecryptData Try=%v Vault Response - no idea\n", i)
return fmt.Errorf("error converting response to map[string]interface{}")
}
}
return nil // or an error
}
xbo := backoff.NewExponentialBackOff()
xbo.MaxElapsedTime = 15 * time.Minute
err := backoff.Retry(operation, xbo)
if err != nil {
// Handle error.
return emptyMap, err
}

return data, nil
}

func makeInputdata(inputMap map[string]map[string]interface{}, rows int, fields int, baseName string) {
for i := 0; i < rows; i++ {
s := fmt.Sprint(i)
inputMap[s] = map[string]interface{}{}

for j := 0; j < fields; j++ {
randomStr := ""
randomInt := rand.Intn(6)
switch randomInt {
case 0:
randomStr = lorem.New().Email()
case 1:
randomStr = lorem.New().FirstName(lorem.Female)
case 2:
randomStr = lorem.New().FullName(lorem.Male)
case 3:
randomStr = lorem.New().Host()
case 4:
randomStr = lorem.New().Url()
case 5:
randomStr = lorem.New().Word(0, 10)
default:
randomStr = lorem.New().Word(0, 10)
}

inputMap[s][baseName+fmt.Sprint(j)] = randomStr

}
}
}

func createSliceOfMapsFromMap(inputMap map[string]map[string]interface{}, maxSize int) []map[string]map[string]interface{} {

// create a slice of maps with zero elements and max size
batchMaps := make([]map[string]map[string]interface{}, 0, maxSize)
// make the fist element of the slice and empty (but not nil) map
batchMaps = append(batchMaps, make(map[string]map[string]interface{}))

i, n := 0, 0
for k, v := range inputMap {
n++

batchMaps[i][k] = v

if n%maxSize == 0 && n != len(inputMap) {
// create a new element of the slice if we are at max size but not at the last element
i++
batchMaps = append(batchMaps, make(map[string]map[string]interface{}))
}
}

return batchMaps
}

func createMapFromSliceOfMaps(sliceMap []map[string]map[string]interface{}) map[string]map[string]interface{} {

newMap := make(map[string]map[string]interface{})

for _, innerMap := range sliceMap {
for ki, vi := range innerMap {
newMap[ki] = vi
}
}

return newMap
}

func createSliceOfMapsFromMapStrInt(inputMap map[string]interface{}, maxSize int) []map[string]interface{} {

// create a slice of maps with zero elements and max size
batchMaps := make([]map[string]interface{}, 0, maxSize)
// make the fist element of the slice and empty (but not nil) map
batchMaps = append(batchMaps, make(map[string]interface{}))

// if the map is les than or equal to the maxSize, then the map is the first and opnly element
if len(inputMap) <= maxSize {
batchMaps[0] = inputMap
return batchMaps
}

i, n := 0, 0
l := len(inputMap)

for k, v := range inputMap {
n++

batchMaps[i][k] = v

if n%maxSize == 0 && n != l {
// create a new element of the slice if we are at max size but not at the last element
i++
batchMaps = append(batchMaps, make(map[string]interface{}))
}
}

return batchMaps
}

func createMapFromSliceOfMapsStrInt(sliceMap []map[string]interface{}) map[string]interface{} {

newMap := make(map[string]interface{})

for _, innerMap := range sliceMap {
for ki, vi := range innerMap {
newMap[ki] = vi
}
}

return newMap
}
72 changes: 72 additions & 0 deletions aeadutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,4 +668,76 @@ func TestAeadUtils(t *testing.T) {
}

})

t.Run("test mapSlice", func(t *testing.T) {

var inputMap = map[string]map[string]interface{}{}

makeInputdata(inputMap, 10, 6, "field")

// define the max number of items in each slice
// initialize an empty slice of the correct type, max capacity same as the chunksize
mapSlice := createSliceOfMapsFromMap(inputMap, 11)

if len(mapSlice) != 1 {
t.Errorf("Expected Slice length of: %v got: %v", 1, len(mapSlice))
}

mapSlice = createSliceOfMapsFromMap(inputMap, 3)

if len(mapSlice) != 4 {
t.Errorf("Expected Slice length of: %v got: %v", 4, len(mapSlice))
}

mapSlice = createSliceOfMapsFromMap(inputMap, 10)

if len(mapSlice) != 1 {
t.Errorf("Expected Slice length of: %v got: %v", 1, len(mapSlice))
}

mapSlice = createSliceOfMapsFromMap(inputMap, 1)

if len(mapSlice) != 10 {
t.Errorf("Expected Slice length of: %v got: %v", 10, len(mapSlice))
}

newMap := createMapFromSliceOfMaps(mapSlice)

if !reflect.DeepEqual(newMap, inputMap) {
t.Errorf("Reassembled Map is not the same as original")

}

})

t.Run("test mapSliceNew", func(t *testing.T) {

var inputMap = map[string]map[string]interface{}{}

makeInputdata(inputMap, 10, 6, "field")

//newMap, ok := inputMap.(map[string]interface{})

var inputMapNew = map[string]interface{}{}
for k, v := range inputMap {
inputMapNew[k] = v
}

// define the max number of items in each slice
// initialize an empty slice of the correct type, max capacity same as the chunksize
mapSlice := createSliceOfMapsFromMapStrInt(inputMapNew, 3)

if len(mapSlice) != 4 {
t.Errorf("Expected Slice length of: %v got: %v", 4, len(mapSlice))
}

newMap := createMapFromSliceOfMapsStrInt(mapSlice)

if !reflect.DeepEqual(newMap, inputMapNew) {
t.Errorf("Reassembled Map is not the same as original")

}

})

}
Loading