title: Double Click Crypto in Golang
body: <p>doc: <a href="https://developers.google.com/ad-exchange/rtb/response-guide/decrypt-price#sample_code" rel="nofollow noreferrer">https://developers.google.com/ad-exchange/rtb/response-guide/decrypt-price#sample_code</a></p>
  
  <p>There is no official "Double Click Crypto" example code in golang, so I try to implement by myself. But I can't pass the test in doc. Please help me!</p>
  
  <pre><code>skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=  // Encryption key (e_key)
  
  arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=  // Integrity key (i_key)
  
  WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA  // 100 CPI micros
  
  WEp8sQAAAACwF6CtLJrXSRFBM8UiTTIyngN-og  // 1900 CPI micros
  
  WEp8nQAAAAADG-y45xxIC1tMWuTjzmDW6HtroQ  // 2700 CPI micros
  </code></pre>
  
  <p>Here is my code:</p>
  
  <pre><code>package main
  
  // https://developers.google.com/ad-exchange/rtb/response-guide/decrypt-price?hl=zh-CN
  
  import (
      "bytes"
      "crypto/hmac"
      "crypto/sha1"
      "encoding/base64"
      "encoding/binary"
      "fmt"
  )
  
  var base64Codec = base64.URLEncoding.WithPadding(base64.NoPadding)
  
  func safeXORBytes(dst, a, b []byte) int {
      n := len(a)
      if len(b) &lt; n {
          n = len(b)
      }
      for i := 0; i &lt; n; i++ {
          dst[i] = a[i] ^ b[i]
      }
      return n
  }
  
  func EncryptPrice(eKey []byte, iKey []byte, price uint64, iv []byte) (finalMessage string, err error) {
      if len(iv) != 16 {
          err = fmt.Errorf("len(iv) = %d != 16", len(iv))
          return
      }
  
      h1 := hmac.New(sha1.New, eKey)
      h1.Write(iv)
      pad := h1.Sum(nil)[:8]
  
      priceBytes := make([]byte, 8)
      binary.BigEndian.PutUint64(priceBytes, price)
  
      encPrice := make([]byte, 8)
      n := safeXORBytes(encPrice, priceBytes, pad)
      if n != 8 {
          err = fmt.Errorf("safeXORBytes n != %d", n)
          return
      }
  
      h2 := hmac.New(sha1.New, iKey)
      h2.Write(priceBytes)
      h2.Write(iv)
      signature := h2.Sum(nil)[:4]
  
      finalMessage = base64Codec.EncodeToString(append(append(iv, encPrice...), signature...))
      return
  }
  
  func DecryptPrice(eKey []byte, iKey []byte, finalMessage string) (price uint64, err error) {
      finalMessageBytes, err := base64Codec.DecodeString(finalMessage)
      if err != nil {
          return
      }
      if len(finalMessageBytes) != 28 {
          err = fmt.Errorf("len(finalMessageBytes) = %d != 28", len(finalMessageBytes))
          return
      }
  
      iv := finalMessageBytes[:16]
      encPrice := finalMessageBytes[16:24]
      signature := finalMessageBytes[24:]
  
      h1 := hmac.New(sha1.New, eKey)
      h1.Write(iv)
      pad := h1.Sum(nil)[:8]
  
      priceBytes := make([]byte, 8)
      n := safeXORBytes(priceBytes, encPrice, pad)
      if n != 8 {
          err = fmt.Errorf("safeXORBytes n != %d", n)
          return
      }
  
      h2 := hmac.New(sha1.New, iKey)
      h2.Write(priceBytes)
      h2.Write(iv)
      confSignature := h2.Sum(nil)[:4]
  
      if bytes.Compare(confSignature, signature) != 0 {
          err = fmt.Errorf("sinature mismatch: confSignature = %s, sinature = %s", confSignature, signature)
          return
      }
  
      price = binary.BigEndian.Uint64(priceBytes)
      return
  }
  </code></pre>
  
  <hr>
  
  <pre><code>package main
  
  import (
      "github.com/stretchr/testify/assert"
      "testing"
  )
  
  func TestEncryptPrice(t *testing.T) {
      eKeyBase64Encoded := "skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o"
      eKey, err := base64Codec.DecodeString(eKeyBase64Encoded)
      assert.Nil(t, err)
  
      iKeyBase64Encoded := "arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo"
      iKey, err := base64Codec.DecodeString(iKeyBase64Encoded)
      assert.Nil(t, err)
  
      finalMessage, err := EncryptPrice(eKey, iKey, uint64(100), []byte{88, 74, 124, 193, 0, 0, 0, 0, 103, 21, 222, 68, 144, 29, 164, 215})
      assert.Nil(t, err)
      assert.Equal(t, "WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA", finalMessage)
  }
  
  func TestDecryptPrice(t *testing.T) {
      eKeyBase64Encoded := "skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o"
      eKey, err := base64Codec.DecodeString(eKeyBase64Encoded)
      assert.Nil(t, err)
  
      iKeyBase64Encoded := "arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo"
      iKey, err := base64Codec.DecodeString(iKeyBase64Encoded)
      assert.Nil(t, err)
  
      price, err := DecryptPrice(eKey, iKey, "WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA")
      assert.Nil(t, err)
      assert.Equal(t, uint64(100), price)
  }
  </code></pre>
  
  <hr>
  
  <p>test result:</p>
  
  <pre><code>--- FAIL: TestEncryptPrice (0.00s)
          Error Trace:    price_test.go:19
      Error:          Not equal:
                      expected: "WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA"
                      actual: "WEp8wQAAAABnFd5EkB2k1wf7jAcG7gZ9tPUnAA"
  --- FAIL: TestDecryptPrice (0.00s)
          Error Trace:    price_test.go:32
      Error:          Expected nil, but got: &amp;errors.errorString{s:"sinature mismatch: confSignature = \xbe\xaa\xf6h, sinature = T\xe7\x86\xcc"}
          Error Trace:    price_test.go:33
      Error:          Not equal:
                      expected: 0x64
                      actual: 0x0
  FAIL
  </code></pre>