Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
1000000 // Price scale Factor Micro
)
```
##### Encrypting a clear price
Expand Down
73 changes: 9 additions & 64 deletions doubleclick/doubleclick_pricer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import (
"crypto/md5"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"hash"
"strings"

Expand All @@ -20,17 +18,13 @@ 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
isDebugMode bool
encryptionKey hash.Hash
integrityKey hash.Hash
scaleFactor float64
}

// 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.
Expand All @@ -42,8 +36,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

Expand All @@ -56,30 +49,10 @@ 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},
encryptionKey: encryptingFun,
integrityKey: integrityFun,
scaleFactor: scaleFactor},
nil
}

Expand All @@ -91,37 +64,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 <xor> data
for i := range data {
encoded[i] = pad[i] ^ data[i]
}
if dc.isDebugMode {
fmt.Println("// enc_data = pad <xor> data")
fmt.Println("Encoded price bytes : ", encoded)
}

// final_message = WebSafeBase64Encode( iv || enc_price || signature )
return base64.RawURLEncoding.EncodeToString(append(append(iv[:], encoded[:]...), signature...)), nil
Expand All @@ -143,11 +100,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
Expand All @@ -163,13 +115,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 <xor> pad
for i := range p {
priceMicro[i] = pad[i] ^ p[i]
Expand Down
Loading