From 0a3c625a4ab6ea612d458f493d967461305bc136 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 10:58:34 +0200 Subject: [PATCH 01/17] test: extract buildPricer() and buildPricerWithScale() --- doubleclick/doubleclick_pricer_test.go | 167 +++++-------------------- 1 file changed, 31 insertions(+), 136 deletions(-) diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index 4471a4b..b9578ea 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -12,6 +12,26 @@ func buildNewDoubleClickPricer(encryptionKey string, integrityKey string, isBase return NewDoubleClickPricer(encryptionKey, integrityKey, isBase64Keys, keyDecodingMode, scaleFactor, isDebugMode) } +// Create a pricer with: +// - HEX keys +// - Price scale factor as micro +func buildPricer() (*DoubleClickPricer, error) { + return buildPricerWithScale(1000000) +} + +func buildPricerWithScale(scaleFactor float64) (*DoubleClickPricer, error) { + var pricer *DoubleClickPricer + var err error + pricer, err = NewDoubleClickPricer( + "652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", + "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", + false, + helpers.Hexa, + scaleFactor, + false) + return pricer, err +} + type priceTestCase struct { encrypted string clear float64 @@ -23,21 +43,10 @@ func newPriceTestCase(encrypted string, clear float64, scaleFactor float64) pric } func TestDecryptEmpty(t *testing.T) { - var pricer *DoubleClickPricer - var err error - pricer, err = buildNewDoubleClickPricer( - "6356770B3C111C07F778AFD69F16643E9110090FD4C479D91181EED2523788F1", - "3588BF6D387E8AEAD4EEC66798255369AF47BFD48B056E8934CEFEF3609C469E", - false, // Keys are not base64 - helpers.Utf8, - 1000000, - false, - ) + pricer, _ := buildPricer() - assert.Nil(t, err, "Error creating new Pricer : ", err) // Execute: - var result float64 - result, err = pricer.Decrypt("") + result, err := pricer.Decrypt("") // Verify: assert.Equal(t, err, ErrWrongSize) assert.Equal(t, float64(0), result) @@ -84,24 +93,8 @@ func TestDecryptGoogleOfficialExamples(t *testing.T) { } func TestDecryptWithHexaKeys(t *testing.T) { - // Create a pricer with: - // - HEX keys - // - Price scale factor as micro - // - No debug mode - // Setup: - var pricer *DoubleClickPricer - var err error - pricer, err = buildNewDoubleClickPricer( - "652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", - "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", - false, // Keys are not base64 - helpers.Hexa, - 1000000, - false, - ) - - assert.Nil(t, err, "Error creating new Pricer : ", err) + pricer, _ := buildPricer() // Encrypted prices we will try to decrypt var pricesTestCase = []priceTestCase{ @@ -177,26 +170,10 @@ func TestDecryptWithScaleFactor(t *testing.T) { } for _, priceTestCase := range pricesTestCase { - // Create a pricer with: - // - HEX keys - // - Price scale factor as micro - // - No debug mode - var pricer *DoubleClickPricer - var err error - pricer, err = buildNewDoubleClickPricer( - "652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", - "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", - false, // Keys are not base64 - helpers.Hexa, - priceTestCase.scaleFactor, - false, - ) - - assert.Nil(t, err, "Error creating new Pricer : ", err) + pricer, _ := buildPricerWithScale(priceTestCase.scaleFactor) // Execute: - var result float64 - result, err = pricer.Decrypt(priceTestCase.encrypted) + result, err := pricer.Decrypt(priceTestCase.encrypted) // Verify: assert.Nil(t, err, "Decryption failed. Error : %s", err) @@ -209,24 +186,8 @@ func TestDecryptWithDebug(t *testing.T) { } func TestEncryptWithHexaKeys(t *testing.T) { - // Create a pricer with: - // - HEX keys - // - Price scale factor as micro - // - No debug mode - // Setup: - var pricer *DoubleClickPricer - var err error - pricer, err = buildNewDoubleClickPricer( - "652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", - "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", - false, // Keys are not base64 - helpers.Hexa, - 1000000, - false, - ) - - assert.Nil(t, err, "Error creating new Pricer : ", err) + pricer, _ := buildPricer() // Clear prices we will try to encrypt var pricesTestCase = []priceTestCase{ @@ -311,26 +272,10 @@ func TestEncryptWithScaleFactor(t *testing.T) { } for _, priceTestCase := range pricesTestCase { - // Create a pricer with: - // - HEX keys - // - Price scale factor as micro - // - No debug mode - var pricer *DoubleClickPricer - var err error - pricer, err = buildNewDoubleClickPricer( - "652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", - "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", - false, // Keys are not base64 - helpers.Hexa, - priceTestCase.scaleFactor, - false, - ) - - assert.Nil(t, err, "Error creating new Pricer : ", err) + pricer, _ := buildPricerWithScale(priceTestCase.scaleFactor) // Execute: - var result string - result, err = pricer.Encrypt("", priceTestCase.clear) + result, err := pricer.Encrypt("", priceTestCase.clear) // Verify: assert.Nil(t, err, "Encryption failed. Error : %s", err) @@ -338,29 +283,9 @@ func TestEncryptWithScaleFactor(t *testing.T) { } } -func TestEncryptWithDebug(t *testing.T) { - // TODO : To be implemented -} - func TestEncryptDecryptWithHexaKeys(t *testing.T) { - // Create a pricer with: - // - HEX keys - // - Price scale factor as micro - // - No debug mode - // Setup: - var pricer *DoubleClickPricer - var err error - pricer, err = buildNewDoubleClickPricer( - "652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", - "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", - false, // Keys are not base64 - helpers.Hexa, - 1000000, - false, - ) - - assert.Nil(t, err, "Error creating new Pricer : ", err) + pricer, _ := buildPricer() // Clear prices to encrypt var pricesTestCase = []priceTestCase{ @@ -457,22 +382,7 @@ func TestEncryptDecryptWithSeed(t *testing.T) { newPriceTestCase("", 1000, 1000000), } - // Create a pricer with: - // - HEX keys - // - Price scale factor as micro - // - No debug mode - var pricer *DoubleClickPricer - var err error - pricer, err = buildNewDoubleClickPricer( - "652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", - "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", - false, // Keys are not base64 - helpers.Hexa, - 1000000, - false, - ) - - assert.Nil(t, err, "Error creating new Pricer : ", err) + pricer, _ := buildPricer() var encryptedPrices []string @@ -526,22 +436,7 @@ func TestEncryptDecryptWithScaleFactor(t *testing.T) { for _, scaleFactor := range scaleFactorsToTest { - // Create a pricer with: - // - HEX keys - // - Price scale factor as micro - // - No debug mode - var pricer *DoubleClickPricer - var err error - pricer, err = buildNewDoubleClickPricer( - "652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", - "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", - false, // Keys are not base64 - helpers.Hexa, - scaleFactor, - false, - ) - - assert.Nil(t, err, "Error creating new Pricer : ", err) + pricer, _ := buildPricerWithScale(scaleFactor) for _, price := range pricesTestCase { // Execute: From e83fd833e1ba6871e0a60e49190fb0c9a14b9636 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 25 Feb 2023 23:26:20 +0200 Subject: [PATCH 02/17] test: inline buildNewDoubleClickPricer() --- doubleclick/doubleclick_pricer_test.go | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index b9578ea..d284eb9 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -8,10 +8,6 @@ import ( "github.com/benjaminch/pricers/helpers" ) -func buildNewDoubleClickPricer(encryptionKey string, integrityKey string, isBase64Keys bool, keyDecodingMode helpers.KeyDecodingMode, scaleFactor float64, isDebugMode bool) (*DoubleClickPricer, error) { - return NewDoubleClickPricer(encryptionKey, integrityKey, isBase64Keys, keyDecodingMode, scaleFactor, isDebugMode) -} - // Create a pricer with: // - HEX keys // - Price scale factor as micro @@ -59,14 +55,13 @@ func TestDecryptGoogleOfficialExamples(t *testing.T) { // Setup: var pricer *DoubleClickPricer var err error - pricer, err = buildNewDoubleClickPricer( + pricer, err = NewDoubleClickPricer( "ZS-DraBUUVeht_sMDgn1nnM3My_nq9TrEESbjubDkTU", "vQo9-4KtlcXmPhWaYvc8asqYuiSVMiGUdZ1RLXfrK7U", - true, // Keys are base64 + true, helpers.Utf8, 1000000, - false, - ) + false) assert.Nil(t, err, "Error creating new Pricer : ", err) @@ -127,14 +122,13 @@ func TestDecryptWithUtf8Keys(t *testing.T) { // Setup: var pricer *DoubleClickPricer var err error - pricer, err = buildNewDoubleClickPricer( + pricer, err = NewDoubleClickPricer( "6356770B3C111C07F778AFD69F16643E9110090FD4C479D91181EED2523788F1", "3588BF6D387E8AEAD4EEC66798255369AF47BFD48B056E8934CEFEF3609C469E", - false, // Keys are not base64 + false, helpers.Utf8, 1000000, - false, - ) + false) assert.Nil(t, err, "Error creating new Pricer : ", err) @@ -326,14 +320,13 @@ func TestEncryptDecryptWithUtf8Keys(t *testing.T) { // Setup: var pricer *DoubleClickPricer var err error - pricer, err = buildNewDoubleClickPricer( + pricer, err = NewDoubleClickPricer( "6356770B3C111C07F778AFD69F16643E9110090FD4C479D91181EED2523788F1", "3588BF6D387E8AEAD4EEC66798255369AF47BFD48B056E8934CEFEF3609C469E", - false, // Keys are not base64 + false, helpers.Utf8, 1000000, - false, - ) + false) assert.Nil(t, err, "Error creating new Pricer : ", err) From f8047a18b051d9a70ae9d11da7efdeddba61c6ad Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 11:07:18 +0200 Subject: [PATCH 03/17] #4 Remove debug logging The logging is almost useless for users but a developer can debug in IDE. The raw keys bytes are not saying anything. We have errors to return a message to a caller. In the same time the code needs to be compiled and it makes the program slower because it resides in CPU cache. --- README.md | 2 +- doubleclick/doubleclick_pricer.go | 54 ++------------------------ doubleclick/doubleclick_pricer_test.go | 7 ---- helpers/helpers.go | 7 +--- 4 files changed, 5 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 3facf4a..7c64b78 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ pricer, err = doubleclick.NewDoubleClickPricer( true, // Keys are base64 helpers.Utf8, // Keys should be ingested as Utf-8 1000000, // Price scale Factor Micro - false, // No debug + false, // No debug. Deprecated ) ``` ##### Encrypting a clear price diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index 3f572a9..98596ad 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -5,9 +5,7 @@ import ( "crypto/md5" "encoding/base64" "encoding/binary" - "encoding/hex" "errors" - "fmt" "hash" "strings" @@ -26,11 +24,10 @@ type DoubleClickPricer struct { integrityKey hash.Hash keyDecodingMode helpers.KeyDecodingMode scaleFactor float64 - isDebugMode bool } // NewDoubleClickPricer returns a DoubleClickPricer struct. -// Keys are either base 64 websafe of hexa. keyDecodingMode +// Keys are either Base64Url (websafe) of hexa. keyDecodingMode // should be used to specify how keys should be decoded. // Factor the clear price will be multiplied by before encryption. // from specs, scaleFactor is 1,000,000, but you can set something else. @@ -56,30 +53,13 @@ func NewDoubleClickPricer( return nil, err } - if isDebugMode { - fmt.Println("Keys decoding mode : ", keyDecodingMode) - fmt.Println("Encryption key : ", encryptionKey) - encryptionKeyHexa, err := hex.DecodeString(encryptionKey) - if err != nil { - encryptionKeyHexa = []byte(encryptionKey) - } - fmt.Println("Encryption key (bytes) : ", []byte(encryptionKeyHexa)) - fmt.Println("Integrity key : ", integrityKey) - integrityKeyHexa, err := hex.DecodeString(integrityKey) - if err != nil { - integrityKeyHexa = []byte(integrityKey) - } - fmt.Println("Integrity key (bytes) : ", []byte(integrityKeyHexa)) - } - return &DoubleClickPricer{ encryptionKeyRaw: encryptionKey, integrityKeyRaw: integrityKey, encryptionKey: encryptingFun, integrityKey: integrityFun, keyDecodingMode: keyDecodingMode, - scaleFactor: scaleFactor, - isDebugMode: isDebugMode}, + scaleFactor: scaleFactor}, nil } @@ -91,37 +71,21 @@ func (dc *DoubleClickPricer) Encrypt(seed string, price float64) (string, error) signature []byte ) - data := helpers.ApplyScaleFactor(price, dc.scaleFactor, dc.isDebugMode) + data := helpers.ApplyScaleFactor(price, dc.scaleFactor) // Create Initialization Vector from seed iv = md5.Sum([]byte(seed)) - if dc.isDebugMode { - fmt.Println("Seed : ", seed) - fmt.Println("Initialization vector : ", iv) - } //pad = hmac(e_key, iv), first 8 bytes pad := helpers.HmacSum(dc.encryptionKey, iv[:], nil)[:8] - if dc.isDebugMode { - fmt.Println("// pad = hmac(e_key, iv), first 8 bytes") - fmt.Println("Pad : ", pad) - } // signature = hmac(i_key, data || iv), first 4 bytes signature = helpers.HmacSum(dc.integrityKey, data[:], iv[:])[:4] - if dc.isDebugMode { - fmt.Println("// signature = hmac(i_key, data || iv), first 4 bytes") - fmt.Println("Signature : ", signature) - } // enc_data = pad data for i := range data { encoded[i] = pad[i] ^ data[i] } - if dc.isDebugMode { - fmt.Println("// enc_data = pad data") - fmt.Println("Encoded price bytes : ", encoded) - } // final_message = WebSafeBase64Encode( iv || enc_price || signature ) return base64.RawURLEncoding.EncodeToString(append(append(iv[:], encoded[:]...), signature...)), nil @@ -143,11 +107,6 @@ func (dc *DoubleClickPricer) Decrypt(encryptedPrice string) (float64, error) { return errPrice, err } - if dc.isDebugMode { - fmt.Println("Encrypted price : ", encryptedPrice) - fmt.Println("Base64 decoded price : ", decoded) - } - // Get elements var ( iv []byte @@ -163,13 +122,6 @@ func (dc *DoubleClickPricer) Decrypt(encryptedPrice string) (float64, error) { // pad = hmac(e_key, iv) pad := helpers.HmacSum(dc.encryptionKey, iv, nil)[:8] - if dc.isDebugMode { - fmt.Println("IV : ", hex.EncodeToString(iv)) - fmt.Println("Encoded price : ", hex.EncodeToString(p)) - fmt.Println("Signature : ", hex.EncodeToString(signature)) - fmt.Println("Pad : ", hex.EncodeToString(pad)) - } - // priceMicro = p pad for i := range p { priceMicro[i] = pad[i] ^ p[i] diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index d284eb9..b110689 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -117,7 +117,6 @@ func TestDecryptWithUtf8Keys(t *testing.T) { // Create a pricer with: // - UTF-8 keys // - Price scale factor as micro - // - No debug mode // Setup: var pricer *DoubleClickPricer @@ -175,10 +174,6 @@ func TestDecryptWithScaleFactor(t *testing.T) { } } -func TestDecryptWithDebug(t *testing.T) { - // TODO: To be implemented -} - func TestEncryptWithHexaKeys(t *testing.T) { // Setup: pricer, _ := buildPricer() @@ -210,7 +205,6 @@ func TestEncryptWithUtf8Keys(t *testing.T) { // Create a pricer with: // - UTF-8 keys // - Price scale factor as micro - // - No debug mode var pricer *DoubleClickPricer var err error pricer, err = buildNewDoubleClickPricer( @@ -315,7 +309,6 @@ func TestEncryptDecryptWithUtf8Keys(t *testing.T) { // Create a pricer with: // - UTF-8 keys // - Price scale factor as micro - // - No debug mode // Setup: var pricer *DoubleClickPricer diff --git a/helpers/helpers.go b/helpers/helpers.go index ff64388..1b41da8 100644 --- a/helpers/helpers.go +++ b/helpers/helpers.go @@ -7,7 +7,6 @@ import ( "encoding/binary" "encoding/hex" "errors" - "fmt" "hash" "strings" ) @@ -89,13 +88,9 @@ func HmacSum(hmac hash.Hash, buf, buf2 []byte) []byte { // ApplyScaleFactor : Applies a scale factor to a given price. // Scaled price will be represented on 8 bytes. -func ApplyScaleFactor(price float64, scaleFactor float64, isDebugMode bool) [8]byte { +func ApplyScaleFactor(price float64, scaleFactor float64) [8]byte { scaledPrice := [8]byte{} binary.BigEndian.PutUint64(scaledPrice[:], uint64(price*scaleFactor)) - if isDebugMode { - fmt.Printf("Micro price bytes: %v", scaledPrice) - } - return scaledPrice } From da858c5dfe20dd222727864f9dd37956ab54538a Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 10:39:43 +0200 Subject: [PATCH 04/17] go.mod cleanup --- go.mod | 5 +---- go.sum | 12 ++++-------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index eabcec5..d509bc4 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,4 @@ module github.com/benjaminch/pricers go 1.12 -require ( - github.com/benjaminch/openrtb-pricers v0.2.0 - github.com/stretchr/testify v1.4.0 -) +require github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 3263b4a..acb88a4 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,11 @@ -github.com/benjaminch/openrtb-pricers v0.2.0 h1:rEoZbSsa47sprVLE6qjSXsq9ROUGAAsKjFOV0gtzUH0= -github.com/benjaminch/openrtb-pricers v0.2.0/go.mod h1:/I+cVRYTUI3TkNxO3bvIzC7E6NcEzsDsdNxZt6J6RVI= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 97a9d6b3af1bb3e492f3c9b8b7020411b497c08e Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 12:41:54 +0200 Subject: [PATCH 05/17] #4 Remove debug logging Remove deprecated isDebugMode --- README.md | 3 +-- doubleclick/doubleclick_pricer.go | 3 +-- doubleclick/doubleclick_pricer_test.go | 12 ++++-------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 7c64b78..0c844dd 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,7 @@ pricer, err = doubleclick.NewDoubleClickPricer( "vQo9-4KtlcXmPhWaYvc8asqYuiSVMiGUdZ1RLXfrK7U", // Integrity key true, // Keys are base64 helpers.Utf8, // Keys should be ingested as Utf-8 - 1000000, // Price scale Factor Micro - false, // No debug. Deprecated + 1000000 // Price scale Factor Micro ) ``` ##### Encrypting a clear price diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index 98596ad..cbf08eb 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -39,8 +39,7 @@ func NewDoubleClickPricer( integrityKey string, isBase64Keys bool, keyDecodingMode helpers.KeyDecodingMode, - scaleFactor float64, - isDebugMode bool) (*DoubleClickPricer, error) { + scaleFactor float64) (*DoubleClickPricer, error) { var err error var encryptingFun, integrityFun hash.Hash diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index b110689..cf067e4 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -23,8 +23,7 @@ func buildPricerWithScale(scaleFactor float64) (*DoubleClickPricer, error) { "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", false, helpers.Hexa, - scaleFactor, - false) + scaleFactor) return pricer, err } @@ -60,8 +59,7 @@ func TestDecryptGoogleOfficialExamples(t *testing.T) { "vQo9-4KtlcXmPhWaYvc8asqYuiSVMiGUdZ1RLXfrK7U", true, helpers.Utf8, - 1000000, - false) + 1000000) assert.Nil(t, err, "Error creating new Pricer : ", err) @@ -126,8 +124,7 @@ func TestDecryptWithUtf8Keys(t *testing.T) { "3588BF6D387E8AEAD4EEC66798255369AF47BFD48B056E8934CEFEF3609C469E", false, helpers.Utf8, - 1000000, - false) + 1000000) assert.Nil(t, err, "Error creating new Pricer : ", err) @@ -318,8 +315,7 @@ func TestEncryptDecryptWithUtf8Keys(t *testing.T) { "3588BF6D387E8AEAD4EEC66798255369AF47BFD48B056E8934CEFEF3609C469E", false, helpers.Utf8, - 1000000, - false) + 1000000) assert.Nil(t, err, "Error creating new Pricer : ", err) From eaa4e674e00f330205e0543ffeb7c7c040ea90ca Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 22:14:16 +0200 Subject: [PATCH 06/17] EncryDecryptionption -> Decryption --- doubleclick/doubleclick_pricer_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index cf067e4..20dd9f8 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -294,7 +294,7 @@ func TestEncryptDecryptWithHexaKeys(t *testing.T) { // Decrypt decrypted, err = pricer.Decrypt(encrypted) - assert.Nil(t, err, "EncryDecryptionption failed. Error : %s", err) + assert.Nil(t, err, "Decryption failed. Error : %s", err) // Verify: // Assert that the decrypted price is the one with encrypted in a first place @@ -341,7 +341,7 @@ func TestEncryptDecryptWithUtf8Keys(t *testing.T) { // Decrypt decrypted, err = pricer.Decrypt(encrypted) - assert.Nil(t, err, "EncryDecryptionption failed. Error : %s", err) + assert.Nil(t, err, "Decryption failed. Error : %s", err) // Verify: // Assert that the decrypted price is the one with encrypted in a first place @@ -382,7 +382,7 @@ func TestEncryptDecryptWithSeed(t *testing.T) { // Decrypt decrypted, err = pricer.Decrypt(encrypted) - assert.Nil(t, err, "EncryDecryptionption failed. Error : %s", err) + assert.Nil(t, err, "Decryption failed. Error : %s", err) // Verify: // Assert that the decrypted price is the one with encrypted in a first place @@ -432,7 +432,7 @@ func TestEncryptDecryptWithScaleFactor(t *testing.T) { // Decrypt decrypted, err = pricer.Decrypt(encrypted) - assert.Nil(t, err, "EncryDecryptionption failed. Error : %s", err) + assert.Nil(t, err, "Decryption failed. Error : %s", err) // Verify: // Assert that the decrypted price is the one with encrypted in a first place From 020e43984a93e596cec369c075566291012afbb8 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 22:18:10 +0200 Subject: [PATCH 07/17] #14 Remove unused fields from DoubleClickPricer struct These fields are never read but consumes memory. If we are going to create many instances of the Pricer then it will consume a lot of memory and make program slower --- doubleclick/doubleclick_pricer.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index cbf08eb..60f2044 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -18,12 +18,9 @@ var ErrWrongSignature = errors.New("Failed to decrypt") // DoubleClickPricer implementing price encryption and decryption // Specs : https://developers.google.com/ad-exchange/rtb/response-guide/decrypt-price type DoubleClickPricer struct { - encryptionKeyRaw string - integrityKeyRaw string - encryptionKey hash.Hash - integrityKey hash.Hash - keyDecodingMode helpers.KeyDecodingMode - scaleFactor float64 + encryptionKey hash.Hash + integrityKey hash.Hash + scaleFactor float64 } // NewDoubleClickPricer returns a DoubleClickPricer struct. @@ -53,12 +50,9 @@ func NewDoubleClickPricer( } return &DoubleClickPricer{ - encryptionKeyRaw: encryptionKey, - integrityKeyRaw: integrityKey, - encryptionKey: encryptingFun, - integrityKey: integrityFun, - keyDecodingMode: keyDecodingMode, - scaleFactor: scaleFactor}, + encryptionKey: encryptingFun, + integrityKey: integrityFun, + scaleFactor: scaleFactor}, nil } From a62d94f858080bf84f547b9ebf84b3b9bd4b19b2 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 22:27:13 +0200 Subject: [PATCH 08/17] Extract RawKeyBytes() The function will decode the string key --- doubleclick/doubleclick_pricer.go | 8 +++++--- helpers/helpers.go | 10 ++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index 60f2044..d6a4293 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -38,17 +38,19 @@ func NewDoubleClickPricer( keyDecodingMode helpers.KeyDecodingMode, scaleFactor float64) (*DoubleClickPricer, error) { var err error - var encryptingFun, integrityFun hash.Hash - encryptingFun, err = helpers.CreateHmac(encryptionKey, isBase64Keys, keyDecodingMode) + encryptionKeyRaw, err := helpers.RawKeyBytes(encryptionKey, isBase64Keys, keyDecodingMode) if err != nil { return nil, err } - integrityFun, err = helpers.CreateHmac(integrityKey, isBase64Keys, keyDecodingMode) + integrityKeyRaw, err := helpers.RawKeyBytes(integrityKey, isBase64Keys, keyDecodingMode) if err != nil { return nil, err } + encryptingFun := helpers.CreateHmac(encryptionKeyRaw) + integrityFun := helpers.CreateHmac(integrityKeyRaw) + return &DoubleClickPricer{ encryptionKey: encryptingFun, integrityKey: integrityFun, diff --git a/helpers/helpers.go b/helpers/helpers.go index 1b41da8..c6a69ff 100644 --- a/helpers/helpers.go +++ b/helpers/helpers.go @@ -49,8 +49,11 @@ func ParseKeyDecodingMode(input string) (KeyDecodingMode, error) { return parsed, err } -// CreateHmac : Returns Hash from input string. -func CreateHmac(key string, isBase64 bool, mode KeyDecodingMode) (hash.Hash, error) { +func CreateHmac(keyRaw []byte) hash.Hash { + return hmac.New(sha1.New, keyRaw) +} + +func RawKeyBytes(key string, isBase64 bool, mode KeyDecodingMode) ([]byte, error) { var err error var b64DecodedKey []byte var k []byte @@ -72,8 +75,7 @@ func CreateHmac(key string, isBase64 bool, mode KeyDecodingMode) (hash.Hash, err if err != nil { return nil, err } - - return hmac.New(sha1.New, k), nil + return k, nil } // HmacSum : Returns Hmac sum bytes. From a7f931e281bc2b28aa69279a2868aa0c2fa87186 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 22:30:42 +0200 Subject: [PATCH 09/17] doubleclick_pricer_test.go buildPricer() no error --- doubleclick/doubleclick_pricer_test.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index 20dd9f8..c498729 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -11,20 +11,19 @@ import ( // Create a pricer with: // - HEX keys // - Price scale factor as micro -func buildPricer() (*DoubleClickPricer, error) { +func buildPricer() *DoubleClickPricer { return buildPricerWithScale(1000000) } -func buildPricerWithScale(scaleFactor float64) (*DoubleClickPricer, error) { +func buildPricerWithScale(scaleFactor float64) *DoubleClickPricer { var pricer *DoubleClickPricer - var err error - pricer, err = NewDoubleClickPricer( + pricer, _ = NewDoubleClickPricer( "652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", false, helpers.Hexa, scaleFactor) - return pricer, err + return pricer } type priceTestCase struct { @@ -38,7 +37,7 @@ func newPriceTestCase(encrypted string, clear float64, scaleFactor float64) pric } func TestDecryptEmpty(t *testing.T) { - pricer, _ := buildPricer() + pricer := buildPricer() // Execute: result, err := pricer.Decrypt("") @@ -87,7 +86,7 @@ func TestDecryptGoogleOfficialExamples(t *testing.T) { func TestDecryptWithHexaKeys(t *testing.T) { // Setup: - pricer, _ := buildPricer() + pricer := buildPricer() // Encrypted prices we will try to decrypt var pricesTestCase = []priceTestCase{ @@ -160,7 +159,7 @@ func TestDecryptWithScaleFactor(t *testing.T) { } for _, priceTestCase := range pricesTestCase { - pricer, _ := buildPricerWithScale(priceTestCase.scaleFactor) + pricer := buildPricerWithScale(priceTestCase.scaleFactor) // Execute: result, err := pricer.Decrypt(priceTestCase.encrypted) @@ -173,7 +172,7 @@ func TestDecryptWithScaleFactor(t *testing.T) { func TestEncryptWithHexaKeys(t *testing.T) { // Setup: - pricer, _ := buildPricer() + pricer := buildPricer() // Clear prices we will try to encrypt var pricesTestCase = []priceTestCase{ @@ -257,7 +256,7 @@ func TestEncryptWithScaleFactor(t *testing.T) { } for _, priceTestCase := range pricesTestCase { - pricer, _ := buildPricerWithScale(priceTestCase.scaleFactor) + pricer := buildPricerWithScale(priceTestCase.scaleFactor) // Execute: result, err := pricer.Encrypt("", priceTestCase.clear) @@ -270,7 +269,7 @@ func TestEncryptWithScaleFactor(t *testing.T) { func TestEncryptDecryptWithHexaKeys(t *testing.T) { // Setup: - pricer, _ := buildPricer() + pricer := buildPricer() // Clear prices to encrypt var pricesTestCase = []priceTestCase{ @@ -364,7 +363,7 @@ func TestEncryptDecryptWithSeed(t *testing.T) { newPriceTestCase("", 1000, 1000000), } - pricer, _ := buildPricer() + pricer := buildPricer() var encryptedPrices []string @@ -418,7 +417,7 @@ func TestEncryptDecryptWithScaleFactor(t *testing.T) { for _, scaleFactor := range scaleFactorsToTest { - pricer, _ := buildPricerWithScale(scaleFactor) + pricer := buildPricerWithScale(scaleFactor) for _, price := range pricesTestCase { // Execute: From d2170d884bb3e932ddc8e312f30101a34b610760 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 22:34:17 +0200 Subject: [PATCH 10/17] NewDoubleClickPricerFromRawKeys() that accepts already parsed keys You can decode the keys one and then create multiple pricers with them. --- doubleclick/doubleclick_pricer.go | 12 ++++++++++++ doubleclick/doubleclick_pricer_test.go | 11 ++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index d6a4293..9f1d445 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -58,6 +58,18 @@ func NewDoubleClickPricer( nil } +func NewDoubleClickPricerFromRawKeys( + encryptionKeyRaw []byte, + integrityKeyRaw []byte, + scaleFactor float64) *DoubleClickPricer { + encryptingFun := helpers.CreateHmac(encryptionKeyRaw) + integrityFun := helpers.CreateHmac(integrityKeyRaw) + return &DoubleClickPricer{ + encryptionKey: encryptingFun, + integrityKey: integrityFun, + scaleFactor: scaleFactor} +} + // Encrypt encrypts a clear price and a given seed. func (dc *DoubleClickPricer) Encrypt(seed string, price float64) (string, error) { var ( diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index c498729..913eec8 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -8,6 +8,9 @@ import ( "github.com/benjaminch/pricers/helpers" ) +var encryptionKeyRaw, _ = helpers.RawKeyBytes("652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", false, helpers.Hexa) +var integrityKeyRaw, _ = helpers.RawKeyBytes("bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", false, helpers.Hexa) + // Create a pricer with: // - HEX keys // - Price scale factor as micro @@ -16,13 +19,7 @@ func buildPricer() *DoubleClickPricer { } func buildPricerWithScale(scaleFactor float64) *DoubleClickPricer { - var pricer *DoubleClickPricer - pricer, _ = NewDoubleClickPricer( - "652f83ada0545157a1b7fb0c0e09f59e7337332fe7abd4eb10449b8ee6c39135", - "bd0a3dfb82ad95c5e63e159a62f73c6aca98ba2495322194759d512d77eb2bb5", - false, - helpers.Hexa, - scaleFactor) + pricer := NewDoubleClickPricerFromRawKeys(encryptionKeyRaw, integrityKeyRaw, scaleFactor) return pricer } From 65d804aea18f6a28fd62648493013df6dd694048 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 22:55:21 +0200 Subject: [PATCH 11/17] Encrypt(): remove error It will never occur. Even seed is just an empty string the md5 hash will be not empty --- doubleclick/doubleclick_pricer.go | 41 +++++++++----------------- doubleclick/doubleclick_pricer_test.go | 21 ++++--------- 2 files changed, 20 insertions(+), 42 deletions(-) diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index 9f1d445..474ca75 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -71,65 +71,52 @@ func NewDoubleClickPricerFromRawKeys( } // Encrypt encrypts a clear price and a given seed. -func (dc *DoubleClickPricer) Encrypt(seed string, price float64) (string, error) { - var ( - iv [16]byte - encoded [8]byte - signature []byte - ) - +func (dc *DoubleClickPricer) Encrypt(seed string, price float64) string { data := helpers.ApplyScaleFactor(price, dc.scaleFactor) // Create Initialization Vector from seed - iv = md5.Sum([]byte(seed)) + iv := md5.Sum([]byte(seed)) //pad = hmac(e_key, iv), first 8 bytes pad := helpers.HmacSum(dc.encryptionKey, iv[:], nil)[:8] // signature = hmac(i_key, data || iv), first 4 bytes - signature = helpers.HmacSum(dc.integrityKey, data[:], iv[:])[:4] + signature := helpers.HmacSum(dc.integrityKey, data[:], iv[:])[:4] // enc_data = pad data + encoded := [8]byte{} for i := range data { encoded[i] = pad[i] ^ data[i] } // final_message = WebSafeBase64Encode( iv || enc_price || signature ) - return base64.RawURLEncoding.EncodeToString(append(append(iv[:], encoded[:]...), signature...)), nil + return base64.RawURLEncoding.EncodeToString(append(append(iv[:], encoded[:]...), signature...)) } // Decrypt decrypts an encrypted price. func (dc *DoubleClickPricer) Decrypt(encryptedPrice string) (float64, error) { - var err error - var errPrice float64 // Decode base64 url // Just to be safe remove padding if it was added by mistake encryptedPrice = strings.TrimRight(encryptedPrice, "=") if len(encryptedPrice) != 38 { - return errPrice, ErrWrongSize + return 0, ErrWrongSize } decoded, err := base64.RawURLEncoding.DecodeString(encryptedPrice) if err != nil { - return errPrice, err + return 0, err } // Get elements - var ( - iv []byte - p []byte - signature []byte - priceMicro [8]byte - ) - - iv = decoded[0:16] - p = decoded[16:24] - signature = decoded[24:28] + iv := decoded[0:16] + p := decoded[16:24] + signature := decoded[24:28] // pad = hmac(e_key, iv) pad := helpers.HmacSum(dc.encryptionKey, iv, nil)[:8] // priceMicro = p pad + priceMicro := [8]byte{} for i := range p { priceMicro[i] = pad[i] ^ p[i] } @@ -139,9 +126,9 @@ func (dc *DoubleClickPricer) Decrypt(encryptedPrice string) (float64, error) { // success = (conf_sig == sig) if !bytes.Equal(confirmationSignature, signature) { - return errPrice, ErrWrongSignature + return 0, ErrWrongSignature } - price := float64(binary.BigEndian.Uint64(priceMicro[:])) / dc.scaleFactor - + priceInMicros := binary.BigEndian.Uint64(priceMicro[:]) + price := float64(priceInMicros) / dc.scaleFactor return price, nil } diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index 913eec8..7bf4a47 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -183,12 +183,9 @@ func TestEncryptWithHexaKeys(t *testing.T) { for _, price := range pricesTestCase { // Execute: - var result string - var err error - result, err = pricer.Encrypt("", price.clear) + result := pricer.Encrypt("", price.clear) // Verify: - assert.Nil(t, err, "Encryption failed. Error : %s", err) assert.Equal(t, result, price.encrypted, "Encryption failed. Should be : %s but was : %s", price.encrypted, result) } } @@ -256,10 +253,8 @@ func TestEncryptWithScaleFactor(t *testing.T) { pricer := buildPricerWithScale(priceTestCase.scaleFactor) // Execute: - result, err := pricer.Encrypt("", priceTestCase.clear) + result := pricer.Encrypt("", priceTestCase.clear) - // Verify: - assert.Nil(t, err, "Encryption failed. Error : %s", err) assert.Equal(t, result, priceTestCase.encrypted, "Encryption failed. Should be : %s but was : %s (scale factor: %f)", priceTestCase.encrypted, result, priceTestCase.scaleFactor) } } @@ -285,8 +280,7 @@ func TestEncryptDecryptWithHexaKeys(t *testing.T) { var err error // Encrypt - encrypted, err = pricer.Encrypt("", price.clear) - assert.Nil(t, err, "Encryption failed. Error : %s", err) + encrypted = pricer.Encrypt("", price.clear) // Decrypt decrypted, err = pricer.Decrypt(encrypted) @@ -332,8 +326,7 @@ func TestEncryptDecryptWithUtf8Keys(t *testing.T) { var err error // Encrypt - encrypted, err = pricer.Encrypt("", price.clear) - assert.Nil(t, err, "Encryption failed. Error : %s", err) + encrypted = pricer.Encrypt("", price.clear) // Decrypt decrypted, err = pricer.Decrypt(encrypted) @@ -373,8 +366,7 @@ func TestEncryptDecryptWithSeed(t *testing.T) { var err error // Encrypt - encrypted, err = pricer.Encrypt(seed, price.clear) - assert.Nil(t, err, "Encryption failed. Error : %s", err) + encrypted = pricer.Encrypt(seed, price.clear) // Decrypt decrypted, err = pricer.Decrypt(encrypted) @@ -423,8 +415,7 @@ func TestEncryptDecryptWithScaleFactor(t *testing.T) { var err error // Encrypt - encrypted, err = pricer.Encrypt("", price.clear) - assert.Nil(t, err, "Encryption failed. Error : %s", err) + encrypted = pricer.Encrypt("", price.clear) // Decrypt decrypted, err = pricer.Decrypt(encrypted) From fc2c16834330cbd6f1a911dd72b5b49f9a093322 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 23:00:07 +0200 Subject: [PATCH 12/17] Extract DecryptRaw --- doubleclick/doubleclick_pricer.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index 474ca75..72a1cc5 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -95,7 +95,12 @@ func (dc *DoubleClickPricer) Encrypt(seed string, price float64) string { // Decrypt decrypts an encrypted price. func (dc *DoubleClickPricer) Decrypt(encryptedPrice string) (float64, error) { + priceInMicros, err := dc.DecryptRaw(encryptedPrice) + price := float64(priceInMicros) / dc.scaleFactor + return price, err +} +func (dc *DoubleClickPricer) DecryptRaw(encryptedPrice string) (uint64, error) { // Decode base64 url // Just to be safe remove padding if it was added by mistake encryptedPrice = strings.TrimRight(encryptedPrice, "=") @@ -129,6 +134,5 @@ func (dc *DoubleClickPricer) Decrypt(encryptedPrice string) (float64, error) { return 0, ErrWrongSignature } priceInMicros := binary.BigEndian.Uint64(priceMicro[:]) - price := float64(priceInMicros) / dc.scaleFactor - return price, nil + return priceInMicros, nil } From 4a77bda4dd1076db75e21f0822c6aa65ba943312 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 23:10:47 +0200 Subject: [PATCH 13/17] Extract DecryptRaw() The method accepts a price in raw bytes. It also receives a buffer for decoding which can be reused later --- doubleclick/doubleclick_pricer.go | 17 ++++---- doubleclick/doubleclick_pricer_test.go | 56 ++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index 72a1cc5..daad3d8 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -6,10 +6,8 @@ import ( "encoding/base64" "encoding/binary" "errors" - "hash" - "strings" - "github.com/benjaminch/pricers/helpers" + "hash" ) var ErrWrongSize = errors.New("Encrypted price is not 38 chars") @@ -95,22 +93,27 @@ func (dc *DoubleClickPricer) Encrypt(seed string, price float64) string { // Decrypt decrypts an encrypted price. func (dc *DoubleClickPricer) Decrypt(encryptedPrice string) (float64, error) { - priceInMicros, err := dc.DecryptRaw(encryptedPrice) + buf := make([]byte, 28) + priceInMicros, err := dc.DecryptRaw([]byte(encryptedPrice), buf) price := float64(priceInMicros) / dc.scaleFactor return price, err } -func (dc *DoubleClickPricer) DecryptRaw(encryptedPrice string) (uint64, error) { +// DecryptRaw decrypts an encrypted price. +// It returns the price as integer in micros without applying a scaleFactor +// You must pass a buffer for decoder so that can reused again to avoid allocation +func (dc *DoubleClickPricer) DecryptRaw(encryptedPrice []byte, buf []byte) (uint64, error) { // Decode base64 url // Just to be safe remove padding if it was added by mistake - encryptedPrice = strings.TrimRight(encryptedPrice, "=") + encryptedPrice = bytes.TrimRight(encryptedPrice, "=") if len(encryptedPrice) != 38 { return 0, ErrWrongSize } - decoded, err := base64.RawURLEncoding.DecodeString(encryptedPrice) + _, err := base64.RawURLEncoding.Decode(buf, encryptedPrice) if err != nil { return 0, err } + decoded := buf // Get elements iv := decoded[0:16] diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index 7bf4a47..d564cb2 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -1,6 +1,7 @@ package doubleclick import ( + "runtime" "testing" "github.com/stretchr/testify/assert" @@ -427,3 +428,58 @@ func TestEncryptDecryptWithScaleFactor(t *testing.T) { } } } + +func TestDecryptAlloc(t *testing.T) { + pricer := buildPricer() + encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" + // We can use testing.AllocsPerRun() but it gives only mallocs + // warmup + _, _ = pricer.Decrypt(encryptedPrice) + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + var memstats runtime.MemStats + runtime.ReadMemStats(&memstats) + mallocs := 0 - memstats.Mallocs + allocBytes := 0 - memstats.Alloc + + // Run the function the specified number of times + _, _ = pricer.Decrypt(encryptedPrice) + + // Read the final statistics + runtime.ReadMemStats(&memstats) + mallocs += memstats.Mallocs + allocBytes += memstats.Alloc + + assert.Equal(t, uint64(5), mallocs) + assert.Equal(t, uint64(144), allocBytes) +} + +func TestDecryptRawAlloc(t *testing.T) { + pricer := buildPricer() + encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" + encryptedPriceBytes := []byte(encryptedPrice) // don't inline + buf := make([]byte, 28) + mallocs := testing.AllocsPerRun(1, func() { + _, _ = pricer.DecryptRaw(encryptedPriceBytes, buf) + }) + assert.Equal(t, float64(3), mallocs) +} + +// BenchmarkDecrypt-8 1708762 694.2 ns/op +func BenchmarkDecrypt(b *testing.B) { + pricer := buildPricer() + encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" + for i := 0; i < b.N; i++ { + _, _ = pricer.Decrypt(encryptedPrice) + } +} + +// BenchmarkDecryptRaw-8 1830206 649.4 ns/op +func BenchmarkDecryptRaw(b *testing.B) { + pricer := buildPricer() + encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" + buf := make([]byte, 28) + encryptedPriceBytes := []byte(encryptedPrice) // don't inline + for i := 0; i < b.N; i++ { + _, _ = pricer.DecryptRaw(encryptedPriceBytes, buf) + } +} From 47007da4c260f13eb4e7d8b7d8f96dbe641c83d9 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 26 Feb 2023 23:51:14 +0200 Subject: [PATCH 14/17] HmacSum() add hmacBuf param It can be pre-allocated. This saved one allocation and improved speed --- doubleclick/doubleclick_pricer.go | 21 ++++++++++++--------- doubleclick/doubleclick_pricer_test.go | 14 +++++++------- helpers/helpers.go | 4 ++-- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index daad3d8..601c69f 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -76,10 +76,10 @@ func (dc *DoubleClickPricer) Encrypt(seed string, price float64) string { iv := md5.Sum([]byte(seed)) //pad = hmac(e_key, iv), first 8 bytes - pad := helpers.HmacSum(dc.encryptionKey, iv[:], nil)[:8] + pad := helpers.HmacSum(dc.encryptionKey, iv[:], nil, nil)[:8] // signature = hmac(i_key, data || iv), first 4 bytes - signature := helpers.HmacSum(dc.integrityKey, data[:], iv[:])[:4] + signature := helpers.HmacSum(dc.integrityKey, data[:], iv[:], nil)[:4] // enc_data = pad data encoded := [8]byte{} @@ -93,7 +93,7 @@ func (dc *DoubleClickPricer) Encrypt(seed string, price float64) string { // Decrypt decrypts an encrypted price. func (dc *DoubleClickPricer) Decrypt(encryptedPrice string) (float64, error) { - buf := make([]byte, 28) + buf := make([]byte, 56) priceInMicros, err := dc.DecryptRaw([]byte(encryptedPrice), buf) price := float64(priceInMicros) / dc.scaleFactor return price, err @@ -101,19 +101,22 @@ func (dc *DoubleClickPricer) Decrypt(encryptedPrice string) (float64, error) { // DecryptRaw decrypts an encrypted price. // It returns the price as integer in micros without applying a scaleFactor -// You must pass a buffer for decoder so that can reused again to avoid allocation +// You must pass a buffer (56 bytes) for decoder so that can reused again to avoid allocation func (dc *DoubleClickPricer) DecryptRaw(encryptedPrice []byte, buf []byte) (uint64, error) { + // first 28 bytes of buf are used to decode price, second 28 for a hmac sum buffer + decoded := buf[:28] + hmacBuf := buf[28:] + // Decode base64 url // Just to be safe remove padding if it was added by mistake encryptedPrice = bytes.TrimRight(encryptedPrice, "=") if len(encryptedPrice) != 38 { return 0, ErrWrongSize } - _, err := base64.RawURLEncoding.Decode(buf, encryptedPrice) + _, err := base64.RawURLEncoding.Decode(decoded, encryptedPrice) if err != nil { return 0, err } - decoded := buf // Get elements iv := decoded[0:16] @@ -121,21 +124,21 @@ func (dc *DoubleClickPricer) DecryptRaw(encryptedPrice []byte, buf []byte) (uint signature := decoded[24:28] // pad = hmac(e_key, iv) - pad := helpers.HmacSum(dc.encryptionKey, iv, nil)[:8] + pad := helpers.HmacSum(dc.encryptionKey, iv, nil, hmacBuf)[:8] // priceMicro = p pad priceMicro := [8]byte{} for i := range p { priceMicro[i] = pad[i] ^ p[i] } + priceInMicros := binary.BigEndian.Uint64(priceMicro[:]) // conf_sig = hmac(i_key, data || iv) - confirmationSignature := helpers.HmacSum(dc.integrityKey, priceMicro[:], iv)[:4] + confirmationSignature := helpers.HmacSum(dc.integrityKey, priceMicro[:], iv, hmacBuf)[:4] // success = (conf_sig == sig) if !bytes.Equal(confirmationSignature, signature) { return 0, ErrWrongSignature } - priceInMicros := binary.BigEndian.Uint64(priceMicro[:]) return priceInMicros, nil } diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index d564cb2..144d381 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -449,22 +449,22 @@ func TestDecryptAlloc(t *testing.T) { mallocs += memstats.Mallocs allocBytes += memstats.Alloc - assert.Equal(t, uint64(5), mallocs) - assert.Equal(t, uint64(144), allocBytes) + assert.Equal(t, uint64(3), mallocs) + assert.Equal(t, uint64(128), allocBytes) } func TestDecryptRawAlloc(t *testing.T) { pricer := buildPricer() encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" encryptedPriceBytes := []byte(encryptedPrice) // don't inline - buf := make([]byte, 28) + buf := make([]byte, 56) mallocs := testing.AllocsPerRun(1, func() { _, _ = pricer.DecryptRaw(encryptedPriceBytes, buf) }) - assert.Equal(t, float64(3), mallocs) + assert.Equal(t, float64(1), mallocs) } -// BenchmarkDecrypt-8 1708762 694.2 ns/op +// BenchmarkDecrypt-8 1816929 657.1 ns/op func BenchmarkDecrypt(b *testing.B) { pricer := buildPricer() encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" @@ -473,11 +473,11 @@ func BenchmarkDecrypt(b *testing.B) { } } -// BenchmarkDecryptRaw-8 1830206 649.4 ns/op +// BenchmarkDecryptRaw-8 2003535 588.9 ns/op func BenchmarkDecryptRaw(b *testing.B) { pricer := buildPricer() encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" - buf := make([]byte, 28) + buf := make([]byte, 56) encryptedPriceBytes := []byte(encryptedPrice) // don't inline for i := 0; i < b.N; i++ { _, _ = pricer.DecryptRaw(encryptedPriceBytes, buf) diff --git a/helpers/helpers.go b/helpers/helpers.go index c6a69ff..7b60d05 100644 --- a/helpers/helpers.go +++ b/helpers/helpers.go @@ -79,13 +79,13 @@ func RawKeyBytes(key string, isBase64 bool, mode KeyDecodingMode) ([]byte, error } // HmacSum : Returns Hmac sum bytes. -func HmacSum(hmac hash.Hash, buf, buf2 []byte) []byte { +func HmacSum(hmac hash.Hash, buf, buf2, hmacBuf []byte) []byte { hmac.Reset() hmac.Write(buf) if buf2 != nil { hmac.Write(buf2) } - return hmac.Sum(nil) + return hmac.Sum(hmacBuf[:0]) } // ApplyScaleFactor : Applies a scale factor to a given price. From 08860b948ae7a1ff84a2efa59d28d0f7eb17984a Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Mon, 27 Feb 2023 00:03:46 +0200 Subject: [PATCH 15/17] DecodeRaw() convert byte arrays to int The generated assembly has a cycle to make xor with two arrays p and pad. Given that the two arrays have only 8 bytes we can convert them to uint64 and perform a usual xor. The optimization makes code harder to read and saves only about 4ns. --- doubleclick/doubleclick_pricer.go | 18 +++++++++--------- doubleclick/doubleclick_pricer_test.go | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index 601c69f..d0cc203 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -120,24 +120,24 @@ func (dc *DoubleClickPricer) DecryptRaw(encryptedPrice []byte, buf []byte) (uint // Get elements iv := decoded[0:16] - p := decoded[16:24] - signature := decoded[24:28] + p := binary.BigEndian.Uint64(decoded[16:24]) + signature := binary.BigEndian.Uint32(decoded[24:28]) // pad = hmac(e_key, iv) - pad := helpers.HmacSum(dc.encryptionKey, iv, nil, hmacBuf)[:8] + padBytes := helpers.HmacSum(dc.encryptionKey, iv, nil, hmacBuf)[:8] + pad := binary.BigEndian.Uint64(padBytes) // priceMicro = p pad + priceInMicros := pad ^ p priceMicro := [8]byte{} - for i := range p { - priceMicro[i] = pad[i] ^ p[i] - } - priceInMicros := binary.BigEndian.Uint64(priceMicro[:]) + binary.BigEndian.PutUint64(priceMicro[:], priceInMicros) // conf_sig = hmac(i_key, data || iv) - confirmationSignature := helpers.HmacSum(dc.integrityKey, priceMicro[:], iv, hmacBuf)[:4] + confirmationSignatureBytes := helpers.HmacSum(dc.integrityKey, priceMicro[:], iv, hmacBuf)[:4] + confirmationSignature := binary.BigEndian.Uint32(confirmationSignatureBytes) // success = (conf_sig == sig) - if !bytes.Equal(confirmationSignature, signature) { + if confirmationSignature != signature { return 0, ErrWrongSignature } return priceInMicros, nil diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index 144d381..ab00cd8 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -464,7 +464,7 @@ func TestDecryptRawAlloc(t *testing.T) { assert.Equal(t, float64(1), mallocs) } -// BenchmarkDecrypt-8 1816929 657.1 ns/op +// BenchmarkDecrypt-8 1831339 649.4 ns/op func BenchmarkDecrypt(b *testing.B) { pricer := buildPricer() encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" @@ -473,7 +473,7 @@ func BenchmarkDecrypt(b *testing.B) { } } -// BenchmarkDecryptRaw-8 2003535 588.9 ns/op +// BenchmarkDecryptRaw-8 2003535 583.1 ns/op func BenchmarkDecryptRaw(b *testing.B) { pricer := buildPricer() encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" From d4d924a35040802ed513dbba935c600fc1db3358 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Mon, 10 Apr 2023 20:30:48 +0300 Subject: [PATCH 16/17] Use decoded and hmacBuf as fields instead of buf param --- doubleclick/doubleclick_pricer.go | 43 ++++++++++++++------------ doubleclick/doubleclick_pricer_test.go | 14 ++++----- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/doubleclick/doubleclick_pricer.go b/doubleclick/doubleclick_pricer.go index d0cc203..9a5feea 100644 --- a/doubleclick/doubleclick_pricer.go +++ b/doubleclick/doubleclick_pricer.go @@ -15,9 +15,12 @@ var ErrWrongSignature = errors.New("Failed to decrypt") // DoubleClickPricer implementing price encryption and decryption // Specs : https://developers.google.com/ad-exchange/rtb/response-guide/decrypt-price +// It's not thread safe so use PricersPool type DoubleClickPricer struct { encryptionKey hash.Hash integrityKey hash.Hash + decoded []byte // 28 bytes + hmacBuf []byte // 28 bytes scaleFactor float64 } @@ -48,12 +51,16 @@ func NewDoubleClickPricer( encryptingFun := helpers.CreateHmac(encryptionKeyRaw) integrityFun := helpers.CreateHmac(integrityKeyRaw) + decoded := [28]byte{} + hmacBuf := [28]byte{} return &DoubleClickPricer{ - encryptionKey: encryptingFun, - integrityKey: integrityFun, - scaleFactor: scaleFactor}, - nil + encryptionKey: encryptingFun, + integrityKey: integrityFun, + scaleFactor: scaleFactor, + decoded: decoded[:], + hmacBuf: hmacBuf[:], + }, nil } func NewDoubleClickPricerFromRawKeys( @@ -62,10 +69,14 @@ func NewDoubleClickPricerFromRawKeys( scaleFactor float64) *DoubleClickPricer { encryptingFun := helpers.CreateHmac(encryptionKeyRaw) integrityFun := helpers.CreateHmac(integrityKeyRaw) + decoded := [28]byte{} + hmacBuf := [28]byte{} return &DoubleClickPricer{ encryptionKey: encryptingFun, integrityKey: integrityFun, - scaleFactor: scaleFactor} + scaleFactor: scaleFactor, + decoded: decoded[:], + hmacBuf: hmacBuf[:]} } // Encrypt encrypts a clear price and a given seed. @@ -93,38 +104,32 @@ func (dc *DoubleClickPricer) Encrypt(seed string, price float64) string { // Decrypt decrypts an encrypted price. func (dc *DoubleClickPricer) Decrypt(encryptedPrice string) (float64, error) { - buf := make([]byte, 56) - priceInMicros, err := dc.DecryptRaw([]byte(encryptedPrice), buf) + priceInMicros, err := dc.DecryptRaw([]byte(encryptedPrice)) price := float64(priceInMicros) / dc.scaleFactor return price, err } // DecryptRaw decrypts an encrypted price. // It returns the price as integer in micros without applying a scaleFactor -// You must pass a buffer (56 bytes) for decoder so that can reused again to avoid allocation -func (dc *DoubleClickPricer) DecryptRaw(encryptedPrice []byte, buf []byte) (uint64, error) { - // first 28 bytes of buf are used to decode price, second 28 for a hmac sum buffer - decoded := buf[:28] - hmacBuf := buf[28:] - +func (dc *DoubleClickPricer) DecryptRaw(encryptedPrice []byte) (uint64, error) { // Decode base64 url // Just to be safe remove padding if it was added by mistake encryptedPrice = bytes.TrimRight(encryptedPrice, "=") if len(encryptedPrice) != 38 { return 0, ErrWrongSize } - _, err := base64.RawURLEncoding.Decode(decoded, encryptedPrice) + _, err := base64.RawURLEncoding.Decode(dc.decoded, encryptedPrice) if err != nil { return 0, err } // Get elements - iv := decoded[0:16] - p := binary.BigEndian.Uint64(decoded[16:24]) - signature := binary.BigEndian.Uint32(decoded[24:28]) + iv := dc.decoded[0:16] + p := binary.BigEndian.Uint64(dc.decoded[16:24]) + signature := binary.BigEndian.Uint32(dc.decoded[24:28]) // pad = hmac(e_key, iv) - padBytes := helpers.HmacSum(dc.encryptionKey, iv, nil, hmacBuf)[:8] + padBytes := helpers.HmacSum(dc.encryptionKey, iv, nil, dc.hmacBuf)[:8] pad := binary.BigEndian.Uint64(padBytes) // priceMicro = p pad @@ -133,7 +138,7 @@ func (dc *DoubleClickPricer) DecryptRaw(encryptedPrice []byte, buf []byte) (uint binary.BigEndian.PutUint64(priceMicro[:], priceInMicros) // conf_sig = hmac(i_key, data || iv) - confirmationSignatureBytes := helpers.HmacSum(dc.integrityKey, priceMicro[:], iv, hmacBuf)[:4] + confirmationSignatureBytes := helpers.HmacSum(dc.integrityKey, priceMicro[:], iv, dc.hmacBuf)[:4] confirmationSignature := binary.BigEndian.Uint32(confirmationSignatureBytes) // success = (conf_sig == sig) diff --git a/doubleclick/doubleclick_pricer_test.go b/doubleclick/doubleclick_pricer_test.go index ab00cd8..5905676 100644 --- a/doubleclick/doubleclick_pricer_test.go +++ b/doubleclick/doubleclick_pricer_test.go @@ -449,22 +449,21 @@ func TestDecryptAlloc(t *testing.T) { mallocs += memstats.Mallocs allocBytes += memstats.Alloc - assert.Equal(t, uint64(3), mallocs) - assert.Equal(t, uint64(128), allocBytes) + assert.Equal(t, uint64(2), mallocs) + assert.Equal(t, uint64(64), allocBytes) } func TestDecryptRawAlloc(t *testing.T) { pricer := buildPricer() encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" encryptedPriceBytes := []byte(encryptedPrice) // don't inline - buf := make([]byte, 56) mallocs := testing.AllocsPerRun(1, func() { - _, _ = pricer.DecryptRaw(encryptedPriceBytes, buf) + _, _ = pricer.DecryptRaw(encryptedPriceBytes) }) assert.Equal(t, float64(1), mallocs) } -// BenchmarkDecrypt-8 1831339 649.4 ns/op +// BenchmarkDecrypt-8 1831339 598.6 ns/op func BenchmarkDecrypt(b *testing.B) { pricer := buildPricer() encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" @@ -473,13 +472,12 @@ func BenchmarkDecrypt(b *testing.B) { } } -// BenchmarkDecryptRaw-8 2003535 583.1 ns/op +// BenchmarkDecryptRaw-8 2003535 556.7 ns/op func BenchmarkDecryptRaw(b *testing.B) { pricer := buildPricer() encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" - buf := make([]byte, 56) encryptedPriceBytes := []byte(encryptedPrice) // don't inline for i := 0; i < b.N; i++ { - _, _ = pricer.DecryptRaw(encryptedPriceBytes, buf) + _, _ = pricer.DecryptRaw(encryptedPriceBytes) } } From 77b6fc82d8536588b3c6ee51eb4b368f90fde9c6 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Mon, 10 Apr 2023 20:31:14 +0300 Subject: [PATCH 17/17] pricers_pool.go Add ready to use pricers pool --- doubleclick/pricers_pool.go | 33 ++++++++++++++++++++++++++++++++ doubleclick/pricers_pool_test.go | 17 ++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 doubleclick/pricers_pool.go create mode 100644 doubleclick/pricers_pool_test.go diff --git a/doubleclick/pricers_pool.go b/doubleclick/pricers_pool.go new file mode 100644 index 0000000..ba05724 --- /dev/null +++ b/doubleclick/pricers_pool.go @@ -0,0 +1,33 @@ +package doubleclick + +import "sync" + +type PricersPool struct { + pool *sync.Pool + encryptionKeyRaw, integrityKeyRaw []byte + scaleFactor float64 +} + +func NewPricersPool(encryptionKeyRaw, integrityKeyRaw []byte, scaleFactor float64) *PricersPool { + return &PricersPool{ + pool: &sync.Pool{}, + encryptionKeyRaw: encryptionKeyRaw, + integrityKeyRaw: integrityKeyRaw, + scaleFactor: scaleFactor, + } +} + +func (pl *PricersPool) AcquirePricer() *DoubleClickPricer { + var pricer *DoubleClickPricer + oldPricer := pl.pool.Get() + if oldPricer != nil { + pricer = oldPricer.(*DoubleClickPricer) + } else { + pricer = NewDoubleClickPricerFromRawKeys(pl.encryptionKeyRaw, pl.integrityKeyRaw, pl.scaleFactor) + } + return pricer +} + +func (pl *PricersPool) ReleasePricer(pricer *DoubleClickPricer) { + pl.pool.Put(pricer) +} diff --git a/doubleclick/pricers_pool_test.go b/doubleclick/pricers_pool_test.go new file mode 100644 index 0000000..014e5d8 --- /dev/null +++ b/doubleclick/pricers_pool_test.go @@ -0,0 +1,17 @@ +package doubleclick + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNewPricersPool(t *testing.T) { + pp := NewPricersPool(encryptionKeyRaw, integrityKeyRaw, 1000000) + pricer := pp.AcquirePricer() + defer pp.ReleasePricer(pricer) + encryptedPrice := "anCGGFJApcfB6ZGc6mindhpTrYXHY4ONo7lXpg" + encryptedPriceBytes := []byte(encryptedPrice) // don't inline + priceInMicros, err := pricer.DecryptRaw(encryptedPriceBytes) + assert.Equal(t, uint64(1354000), priceInMicros) + assert.Equal(t, nil, err) +}