text: const tape = require('tape')
  
  const m = new Uint8Array(Buffer.from("Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."))
  
  const firstkey = new Uint8Array([
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f
  ])
  
  const nonce = new Uint8Array([
    0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43,
    0x44, 0x45, 0x46, 0x47
  ])
  
  const exp = [
    new Uint8Array([
      0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
      0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
      0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
      0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
      0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
      0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
      0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
      0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
      0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
      0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
      0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
      0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
      0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
      0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
      0x61, 0x16, 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09,
      0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60,
      0x06, 0x91]),
    new Uint8Array([
      0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
      0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
      0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
      0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
      0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
      0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
      0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
      0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
      0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
      0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
      0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
      0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
      0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
      0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
      0x61, 0x16, 0x6a, 0x23, 0xa4, 0x68, 0x1f, 0xd5,
      0x94, 0x56, 0xae, 0xa1, 0xd2, 0x9f, 0x82, 0x47,
      0x72, 0x16]),
    new Uint8Array([
      0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
      0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
      0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
      0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
      0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
      0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
      0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
      0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
      0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
      0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
      0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
      0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
      0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
      0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
      0x61, 0x16, 0x6a, 0x23, 0xa4, 0x68, 0x1f, 0xd5,
      0x94, 0x56, 0xae, 0xa1, 0xd2, 0x9f, 0x82, 0x47,
      0x72, 0x16])
  ]
  
  module.exports = function (sodium) {
    tape('crypto_aead_chacha20poly1305_ietf', function (t) {
      const ad = new Uint8Array([ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 ])
      const c = new Uint8Array(m.byteLength + sodium.crypto_aead_chacha20poly1305_ietf_ABYTES)
      const detached_c = new Uint8Array(m.byteLength)
      const mac = new Uint8Array(sodium.crypto_aead_chacha20poly1305_ietf_ABYTES)
      const m2 = new Uint8Array(m.byteLength)
      let found_clen
      let found_maclen
      let m2len
      let i
  
      found_clen = sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, ad, null, nonce, firstkey)
      t.equal(found_clen, m.byteLength + sodium.crypto_aead_chacha20poly1305_ietf_ABYTES, 'found_clen is properly set')
      t.same(c, exp[0], 'ciphertext matches expected result')
  
      found_clen = sodium.crypto_aead_chacha20poly1305_ietf_encrypt_detached(detached_c,
                                                         mac,
                                                         m,
                                                         ad,
                                                         null, nonce, firstkey)
  
      t.same(detached_c, c.subarray(0, m.byteLength), 'detached ciphertext passes')
      t.same(mac, c.subarray(m.byteLength), 'detached mac passes')
      m2len = sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, c, ad, nonce, firstkey)
  
      t.assert(m2len >= 0, 'sodium.crypto_aead_chacha20poly1305_ietf_decrypt() succeeded')
      t.equal(m2len, m.byteLength, 'm2len is properly set')
      t.same(m2, m, 'm == m2')
      m2.fill(0)
  
      t.doesNotThrow(() => sodium.crypto_aead_chacha20poly1305_ietf_decrypt_detached(m2, null, detached_c, mac, ad, nonce, firstkey),
          'sodium.crypto_aead_chacha20poly1305_ietf_decrypt_detached() succeeded')
  
      t.same(m, m2, 'detached m == m2')
  
      for (i = 0; i < c.byteLength; i++) {
        c[i] ^= (i + 1)
        try {
          sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, c, ad, nonce, firstkey)
          t.fail()
        } catch (e) {
          if (Buffer.compare(m, m2.subarray(0, m.byteLength)) === 0) t.fail('message can be forged')
        }
        c[i] ^= (i + 1)
      }
  
      found_clen = sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, new Uint8Array(0), null, nonce, firstkey)
      t.equal(found_clen, c.byteLength, 'clen is properly set (adlen=0)')
      t.same(c, exp[1], 'ciphertext matches expected result')
  
      m2len = sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, c, new Uint8Array(0), nonce, firstkey)
  
      t.assert(m2len >= 0, 'sodium.crypto_aead_chacha20poly1305_ietf_decrypt() passed (adlen=0)')
      t.equal(m2len, m.byteLength, 'm2len is properly set (adlen=0)\n')
  
      t.ok(Buffer.compare(m, m2.subarray(0, m.byteLength)) === 0, 'm == m2 (adlen=0)')
  
      t.throws(() => sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, new Uint8Array(0), new Uint8Array(0), nonce, firstkey),
        "sodium.crypto_aead_chacha20poly1305_ietf_decrypt() shouldn't work with an empty ciphertext")
  
      c.set(m)
      found_clen = sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c, c.subarray(0, m.byteLength), new Uint8Array(0), null, nonce, firstkey)
  
      t.equal(found_clen, c.byteLength, 'clen is properly set (adlen=0)')
      t.same(c, exp[2], 'ciphertext matches expected result')
  
      m2len = sodium.crypto_aead_chacha20poly1305_ietf_decrypt(c.subarray(0, m.byteLength),
                                                               null, c, new Uint8Array(0), nonce, firstkey)
  
      t.assert(m2len >= 0, 'sodium.crypto_aead_chacha20poly1305_ietf_decrypt() passed (adlen=0)')
      t.equal(m2len, m.byteLength, 'm2len is properly set (adlen=0)')
  
      t.ok(Buffer.compare(m, c.subarray(0, m.byteLength)) === 0, 'm == c (adlen=0)\n')
  
      const c2 = new Uint8Array(c)
      const mac2 = new Uint8Array(mac)
      sodium.crypto_aead_chacha20poly1305_ietf_encrypt_detached(c, mac, c, new Uint8Array(0), null, nonce, firstkey)
      sodium.crypto_aead_chacha20poly1305_ietf_encrypt_detached(c2, mac2, c2, null, null, nonce, firstkey)
  
      t.deepEqual(c, c2, 'null ad gives correct ciphertext')
      t.deepEqual(mac, mac2, 'null ad gives correct mac')
  
      sodium.crypto_aead_chacha20poly1305_ietf_decrypt_detached(c, null, c, mac, new Uint8Array(0), nonce, firstkey)
      sodium.crypto_aead_chacha20poly1305_ietf_decrypt_detached(c2, null, c2, mac, null, nonce, firstkey)
  
      t.deepEqual(c, c2, 'null ad gives correct plaintext')
  
      t.assert(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES > 0)
      t.equal(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES, 32)
      t.assert(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES > 0)
      t.equal(sodium.crypto_aead_chacha20poly1305_ietf_NSECBYTES, 0)
      t.equal(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, 12)
      t.equal(sodium.crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX, Number.MAX_SAFE_INTEGER)
  
      t.end()
    })
  
    require('./crypto_aead_xchacha20poly1305')(sodium)
  }
  