title: Unable to decrypt after AES-GCM + Base64 in Go
body: <h1>Situation</h1>
  
  <p>I'm trying to implement a structure (the <code>CryptoService</code>) hiding en/decryption from the main program flow. I have implemented "normal" functions and base64 variants that should encode the cipher to it's base64 equivalent and visa-versa in decryption. This is done because our internal network protocol uses line-feed <code>\n</code> as delimiter.</p>
  
  <p><em>See code of implementation below</em></p>
  
  <h1>Problem</h1>
  
  <p>After writing the code below i started testing it. At first it went well and en- and decryption worked but soon I started noticing "randomly occurring" errors during the decryption process: <code>cipher: message authentication failed</code>. Now the important fact: the errors <strong>ONLY</strong> returned from the <code>DecryptBase64</code> func. But the base64 usage in go is pretty straight-forward and not much to worry about so I don't have any idea where the problems lies.</p>
  
  <h1>Code</h1>
  
  <p>Below you see the code for my <code>CryptoService</code> implementation and the associated test file. I have tried to clean the code up as much as possible (remove comments, additional input checks, etc.) without removing context. Nevertheless it's much code so thanks to all that read it - really appreciate your help!</p>
  
  <p><strong><code>cryptoservice.go</code></strong></p>
  
  <pre><code>type CryptoService struct {
      gcm cipher.AEAD
  }
  
  func NewCryptoService(key []byte) (cs *CryptoService, err error) {
      block, err := aes.NewCipher(key)
      if err != nil {
          return nil, err
      }
      gcm, err := cipher.NewGCM(block)
      if err != nil {
          return nil, err
      }
      return &amp;CryptoService{
          gcm: gcm,
      }, nil
  }
  
  func (cs CryptoService) Encrypt(plain []byte) (cipher []byte, err error) {
      nonce := make([]byte, cs.gcm.NonceSize())
      _, err = io.ReadFull(rand.Reader, nonce)
      if err != nil {
          return nil, err
      }
  
      cipher = cs.gcm.Seal(nil, nonce, plain, nil)
      cipher = append(nonce, cipher...)
  
      return cipher, nil
  }
  
  func (cs CryptoService) EncryptBase64(plain []byte) (base64Cipher []byte, err error) {
      cipher, err := cs.Encrypt(plain)
      if err != nil {
          return nil, err
      }
  
      base64Cipher = make([]byte, base64.StdEncoding.EncodedLen(len(cipher)))
      base64.StdEncoding.Encode(base64Cipher, cipher)
  
      return
  }
  
  func (cs CryptoService) Decrypt(cipher []byte) (plain []byte, err error) {
      nonce := cipher[0:cs.gcm.NonceSize()]
      ciphertext := cipher[cs.gcm.NonceSize():]
  
      plain, err = cs.gcm.Open(nil, nonce, ciphertext, nil)
      if err != nil {
          return nil, err
      }
  
      return
  }
  
  func (cs CryptoService) DecryptBase64(base64Cipher []byte) (plain []byte, err error) {
      cipher := make([]byte, base64.StdEncoding.DecodedLen(len(base64Cipher)))
      _, err = base64.StdEncoding.Decode(cipher, base64Cipher)
      if err != nil {
          return nil, err
      }
  
      return cs.Decrypt(cipher)
  }
  </code></pre>
  
  <p><strong><code>cryptoservice_test.go</code></strong></p>
  
  <ul>
  <li><code>TestCryptoService_EncryptDecryptRoundtrip</code> works fine</li>
  <li><code>TestCryptoService_EncryptBase64DecryptBase64Roundtrip</code> fails "sometimes" (also note that it doesn't always fail on the same test-cases)</li>
  </ul>
  
  <p>Additional info: The <code>FastRandomString(n int)</code> func in the Dynamic-Test-Case creation is effectively only a copy-and-past from the accepted answer at: <a href="https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang">How to generate a random string of a fixed length in golang?</a></p>
  
  <pre><code>func TestCryptoService_EncryptDecryptRoundtrip(t *testing.T) {
      tests := []struct {
          name   string
          aeskey string
          text   string
      }{
          {"Simple 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Text"},
          {"Simple 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Some random content"},
          {"Simple 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."},
  
          {"Dynamic 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(32)},
          {"Dynamic 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024)},
          {"Dynamic 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 64)},
          {"Dynamic 4", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 256)},
      }
      for _, tt := range tests {
          t.Run(tt.name, func(t *testing.T) {
              for i := 0; i &lt; 1000; i++ {
                  key, _ := hex.DecodeString(tt.aeskey)
                  cs, _ := NewCryptoService(key)
                  cipher, err := cs.Encrypt([]byte(tt.text))
                  if err != nil {
                      t.Errorf("CryptoService.Encrypt() error = %v", err)
                      return
                  }
  
                  plain, err := cs.Decrypt(cipher)
                  if err != nil {
                      t.Errorf("CryptoService.Decrypt() error = %v", err)
                      return
                  }
  
                  plainStr := string(plain)
                  if plainStr != tt.text {
                      t.Errorf("CryptoService.Decrypt() plain = %v, want = %v", plainStr, tt.text)
                      return
                  }
              }
          })
      }
  }
  
  func TestCryptoService_EncryptBase64DecryptBase64Roundtrip(t *testing.T) {
      tests := []struct {
          name   string
          aeskey string
          text   string
      }{
          {"Simple 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Text"},
          {"Simple 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Some random content"},
          {"Simple 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."},
  
          {"Dynamic 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(32)},
          {"Dynamic 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024)},
          {"Dynamic 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 64)},
          {"Dynamic 4", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 256)},
      }
      for _, tt := range tests {
          t.Run(tt.name, func(t *testing.T) {
              for i := 0; i &lt; 1000; i++ {
                  key, _ := hex.DecodeString(tt.aeskey)
                  cs, _ := NewCryptoService(key)
                  cipher, err := cs.EncryptBase64([]byte(tt.text))
                  if err != nil {
                      t.Errorf("CryptoService.EncryptBase64() error = %v", err)
                      return
                  }
  
                  plain, err := cs.DecryptBase64(cipher)
                  if err != nil {
                      t.Errorf("CryptoService.DecryptBase64() error = %v", err)
                      return
                  }
  
                  plainStr := string(plain)
                  if plainStr != tt.text {
                      t.Errorf("CryptoService.DecryptBase64() plain = %v, want = %v", plainStr, tt.text)
                      return
                  }
              }
          })
      }
  }
  </code></pre>