/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
go/chat/signencrypt/codec_test.go
726 строк
25 KB
chrisnojima-zoom
Version 670 - clean2 (#29122)
08 июн 2026, 19:31
Не верифицирован
08 июн 2026, 19:31
1943197
Код
Авторство
О чём код?
package signencrypt import ( "bytes" "crypto/ed25519" "encoding/hex" "errors" "io" "strings" "testing" "golang.org/x/crypto/nacl/secretbox" "github.com/keybase/client/go/kbcrypto" "github.com/stretchr/testify/require" ) var plaintextInputs = []string{ "", "1", "short", strings.Repeat("long", 1000000), } type arbitraryMsg struct { Message string `codec:"m" json:"m"` } type arbitraryNested struct { Fun arbitraryMsg `codec:"f" json:"f"` Boring arbitraryMsg `codec:"b" json:"b"` } var associatedDataInputs = []any{ "", nil, map[string]map[float64]map[string]string{"first": {2.22000222: {"third": "fourth"}}}, strings.Repeat("long", 1000000), arbitraryNested{ Fun: arbitraryMsg{Message: "🤠"}, Boring: arbitraryMsg{Message: "📄"}, }, } func zeroSecretboxKey() SecretboxKey { var key [SecretboxKeySize]byte // all zeroes return &key } func zeroNonce() Nonce { var nonce [NonceSize]byte // all zeroes return &nonce } func zeroChunkNonce(chunkNum uint64) SecretboxNonce { return makeChunkNonce(zeroNonce(), chunkNum) } func zeroVerifyKey() VerifyKey { var key [ed25519.PublicKeySize]byte // Generated from libsodium's crypto_sign_seed_keypair with a zero seed. copy(key[:], ";j'\xbc\xce\xb6\xa4-b\xa3\xa8\xd0*o\rse2\x15w\x1d\xe2C\xa6:\xc0H\xa1\x8bY\xda)") return &key } func zeroSignKey() SignKey { var key [ed25519.PrivateKeySize]byte // Generated from libsodium's crypto_sign_seed_keypair with a zero seed. copy(key[:], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;j'\xbc\xce\xb6\xa4-b\xa3\xa8\xd0*o\rse2\x15w\x1d\xe2C\xa6:\xc0H\xa1\x8bY\xda)") return &key } func testingPrefix() kbcrypto.SignaturePrefix { return kbcrypto.SignaturePrefixTesting } func zeroEncoder() *Encoder { return NewEncoder(zeroSecretboxKey(), zeroSignKey(), testingPrefix(), zeroNonce()) } func zeroDecoder() *Decoder { return NewDecoder(zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroNonce()) } func zeroSealWhole(plaintext []byte) []byte { return SealWhole(plaintext, zeroSecretboxKey(), zeroSignKey(), testingPrefix(), zeroNonce()) } func zeroOpenWhole(plaintext []byte) ([]byte, error) { return OpenWhole(plaintext, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroNonce()) } func zeroSealWithAssociatedData(plaintext []byte, associatedData any) []byte { res, err := SealWithAssociatedData(plaintext, associatedData, zeroSecretboxKey(), zeroSignKey(), testingPrefix(), zeroNonce()) if err != nil { // this should never actually error panic(err) } return res } func zeroOpenWithAssociatedData(plaintext []byte, associatedData any) ([]byte, error) { return OpenWithAssociatedData(plaintext, associatedData, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroNonce()) } func assertErrorType(t *testing.T, err error, expectedType ErrorType) { if err == nil { t.Fatal("expected an error, but error was nil") } concreteError, ok := err.(Error) if !ok { t.Fatal("failed to cast to Error") } if concreteError.Type != expectedType { t.Fatalf("expected error type %d but found %d", expectedType, concreteError.Type) } } func TestPacketRoundtrips(t *testing.T) { for index, input := range plaintextInputs { // Vary the chunk number, just for fun. chunkNum := uint64(index) //nolint:gosec // G115: Test code, index from range is non-negative sealed := sealPacket( []byte(input), zeroSecretboxKey(), zeroSignKey(), testingPrefix(), zeroChunkNonce(chunkNum)) opened, err := openPacket( sealed, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroChunkNonce(chunkNum)) if err != nil { t.Fatal(err) } if !bytes.Equal([]byte(input), opened) { t.Fatal("opened bytes don't equal the input") } if int64(len(sealed)) != getPacketLen(int64(len(input))) { t.Fatalf("Expected len %d but found %d", getPacketLen(int64(len(input))), len(sealed)) } } } func TestWholeRoundtrips(t *testing.T) { for _, input := range plaintextInputs { sealed := zeroSealWhole([]byte(input)) opened, err := zeroOpenWhole(sealed) if err != nil { t.Fatal(err) } if !bytes.Equal([]byte(input), opened) { t.Fatal("opened bytes don't equal the input") } if int64(len(sealed)) != GetSealedSize(int64(len(input))) { t.Fatalf("Expected len %d but found %d", GetSealedSize(int64(len(input))), len(sealed)) } } } func TestByteAtATimeRoundtrips(t *testing.T) { for _, input := range plaintextInputs { encoder := zeroEncoder() var sealed []byte for i := 0; i < len(input); i++ { output := encoder.Write([]byte{input[i]}) sealed = append(sealed, output...) } lastOutput := encoder.Finish() sealed = append(sealed, lastOutput...) var opened []byte decoder := zeroDecoder() for i := 0; i < len(sealed); i++ { output, err := decoder.Write([]byte{sealed[i]}) if err != nil { t.Fatal(err) } opened = append(opened, output...) } lastOutput, err := decoder.Finish() if err != nil { t.Fatal(err) } opened = append(opened, lastOutput...) if !bytes.Equal([]byte(input), opened) { t.Fatal("opened bytes don't equal the input") } if int64(len(sealed)) != GetSealedSize(int64(len(input))) { t.Fatalf("Expected len %d but found %d", GetSealedSize(int64(len(input))), len(sealed)) } } } func TestReaderWrapperRoundtrips(t *testing.T) { for _, input := range plaintextInputs { inputBuffer := bytes.NewBuffer([]byte(input)) encodingReader := NewEncodingReader( zeroSecretboxKey(), zeroSignKey(), testingPrefix(), zeroNonce(), inputBuffer) encoded, err := io.ReadAll(encodingReader) if err != nil { t.Fatalf("errors shouldn't be possible for encoding: %s", err) } encodedBuffer := bytes.NewBuffer(encoded) decodingReader := NewDecodingReader( zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroNonce(), encodedBuffer) decoded, err := io.ReadAll(decodingReader) if err != nil { t.Fatalf("error during decoding: %s", err) } if !bytes.Equal([]byte(input), decoded) { t.Fatal("decoded bytes don't equal the input") } if int64(len(encoded)) != GetSealedSize(int64(len(input))) { t.Fatalf("Expected encoded len %d but found %d", GetSealedSize(int64(len(input))), len(encoded)) } } } func TestBadSecretbox(t *testing.T) { // Test several different cases. First, a secretbox that's too short to even // contain an authenticator (just one byte for the secretbox). shortPacket := []byte{0xc6, 0, 0, 0, 1, 42} _, err := openPacket(shortPacket, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroChunkNonce(0)) assertErrorType(t, err, BadSecretbox) // Then also test a secretbox that's long enough to be real, but has an // invalid authenticator (just a bunch of constant bytes). badAuthenticatorPacket := []byte{0xc6, 0, 0, 0, 100} for range 100 { badAuthenticatorPacket = append(badAuthenticatorPacket, 42) } _, err = openPacket(badAuthenticatorPacket, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroChunkNonce(0)) assertErrorType(t, err, BadSecretbox) // Test a correct packet opened with the wrong chunk number. var rightChunkNum uint64 = 5 var wrongChunkNum uint64 = 6 correctPacket := sealPacket([]byte{}, zeroSecretboxKey(), zeroSignKey(), testingPrefix(), zeroChunkNonce(rightChunkNum)) _, err = openPacket(correctPacket, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroChunkNonce(wrongChunkNum)) assertErrorType(t, err, BadSecretbox) } func TestShortSignature(t *testing.T) { // Signatures are 64 bytes, so this slice is too short to be one. shortSignedChunk := []byte{1, 2, 3, 4, 5, 6, 7} var chunkNum uint64 = 999 chunkNonce := makeChunkNonce(zeroNonce(), chunkNum) packet := secretbox.Seal(nil, shortSignedChunk, chunkNonce, zeroSecretboxKey()) _, err := openPacket(packet, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroChunkNonce(chunkNum)) assertErrorType(t, err, ShortSignature) } func TestInvalidSignature(t *testing.T) { // A chunk that's long enough to contain a signature, but isn't valid (just // a bunch of zeroes). invalidSignedChunk := bytes.Repeat([]byte{42}, 100) var chunkNum uint64 = 999 chunkNonce := makeChunkNonce(zeroNonce(), chunkNum) packet := secretbox.Seal(nil, invalidSignedChunk, chunkNonce, zeroSecretboxKey()) _, err := openPacket(packet, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroChunkNonce(chunkNum)) assertErrorType(t, err, BadSignature) } func TestErrorsReturnedFromDecoder(t *testing.T) { // We need bad bytes long enough to trigger an open. This indirectly tests // that the exact packet length is enough for that. badPacket := bytes.Repeat([]byte{0}, int(getPacketLen(DefaultPlaintextChunkLength))) decoder := zeroDecoder() _, err := decoder.Write(badPacket) assertErrorType(t, err, BadSecretbox) // Make sure we get the same error again for any subsequent writes, even // empty ones. _, err = decoder.Write([]byte{}) assertErrorType(t, err, BadSecretbox) // And also for Finish(). _, err = decoder.Finish() assertErrorType(t, err, BadSecretbox) // And make sure we get the same error independently for an all at once // decode. _, err = zeroOpenWhole(badPacket) assertErrorType(t, err, BadSecretbox) } func TestErrorsReturnedFromDecoderDuringFinish(t *testing.T) { // There are two errors that might have to be returned by OpenWhole. This // tests the second path, where an error occurs first during Finish(). badSealed := zeroSealWhole([]byte("foobar")) // Flip the very last bit. badSealed[len(badSealed)-1] ^= 1 _, err := zeroOpenWhole(badSealed) assertErrorType(t, err, BadSecretbox) } func throwawayBuffer() []byte { var buf [4096]byte return buf[:] } // Similar to TestErrorsReturnedFromDecoder above, but for the reader. func TestErrorsReturnedFromDecodingReader(t *testing.T) { badPacket := bytes.Repeat([]byte{0}, int(getPacketLen(DefaultPlaintextChunkLength))) reader := NewDecodingReader( zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroNonce(), bytes.NewBuffer(badPacket)) n, err := reader.Read(throwawayBuffer()) require.Equal(t, n, 0) assertErrorType(t, err, BadSecretbox) // Make sure we get the same error again for any subsequent reads, even // empty ones. n, err = reader.Read(throwawayBuffer()) require.Equal(t, n, 0) assertErrorType(t, err, BadSecretbox) } // Similar to TestErrorsReturnedFromDecoderDuringFinish above, but for the reader. func TestErrorsReturnedFromReadingDecoderDuringFinish(t *testing.T) { badSealed := zeroSealWhole([]byte("foobar")) // Flip the very last bit. badSealed[len(badSealed)-1] ^= 1 reader := NewDecodingReader( zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroNonce(), bytes.NewBuffer(badSealed)) n, err := reader.Read(throwawayBuffer()) require.Equal(t, n, 0) assertErrorType(t, err, BadSecretbox) } func TestReencryptedPacketFails(t *testing.T) { // Make sure that a packet can't be (legitimately) decrypted and then // (illegitimately) reencrypted for another symmetric key, or with any // other modified encryption metadata. This isn't proof that someone can't // break the format in some clever way, but it's a sanity check that we're // preventing at least the attacks we think we are. // First create a valid packet. var originalChunkNum uint64 // = 0, but lint doesn't let us write it :p originalNonce := zeroNonce() originalEncryptionKey := zeroSecretboxKey() originalSignKey := zeroSignKey() originalVerifyKey := zeroVerifyKey() packet := sealPacket([]byte("foo"), originalEncryptionKey, originalSignKey, testingPrefix(), makeChunkNonce(originalNonce, originalChunkNum)) // Now strip off the outer layer of encryption, as a recipient would. originalChunkNonce := makeChunkNonce(originalNonce, originalChunkNum) unboxedSig, valid := secretbox.Open(nil, packet, originalChunkNonce, originalEncryptionKey) if !valid { t.Fatal("expected this secretbox to open cleanly") } // Here's the attack: reencrypt the packet under a *different* key. newEncryptionKey := zeroSecretboxKey() newEncryptionKey[0] = 42 rekeyedPacket := secretbox.Seal(nil, unboxedSig, originalChunkNonce, newEncryptionKey) // This new packet will have a bad secretbox if someone tries to decrypt it // with the old key, of course. _, err := openPacket(rekeyedPacket, originalEncryptionKey, originalVerifyKey, testingPrefix(), makeChunkNonce(originalNonce, originalChunkNum)) assertErrorType(t, err, BadSecretbox) // And here's the part we really care about: If someone tries to decrypt // the packet with the *new* key, unboxing will succeed, but it should now // give a bad *signature* error. This is the whole point of asserting the // symmetric key inside the sig. _, err = openPacket(rekeyedPacket, newEncryptionKey, originalVerifyKey, testingPrefix(), makeChunkNonce(originalNonce, originalChunkNum)) assertErrorType(t, err, BadSignature) // Another test along the same lines: it should also be a signature error if the chunk number changes. var newChunkNum uint64 = 1 newChunkNumNonce := makeChunkNonce(originalNonce, newChunkNum) renumberedPacket := secretbox.Seal(nil, unboxedSig, newChunkNumNonce, originalEncryptionKey) _, err = openPacket(renumberedPacket, originalEncryptionKey, originalVerifyKey, testingPrefix(), makeChunkNonce(originalNonce, newChunkNum)) assertErrorType(t, err, BadSignature) // And: it should be a signature error if the caller's nonce changes. newNonce := zeroNonce() newNonce[0] = 42 newChunkNonce := makeChunkNonce(newNonce, originalChunkNum) renoncedPacket := secretbox.Seal(nil, unboxedSig, newChunkNonce, originalEncryptionKey) _, err = openPacket(renoncedPacket, originalEncryptionKey, originalVerifyKey, testingPrefix(), makeChunkNonce(newNonce, originalChunkNum)) assertErrorType(t, err, BadSignature) } func TestTruncatedFails(t *testing.T) { // Another sanity check test. This isn't proof that truncation is always // detectable, but it exercises the simplest cases. // One full packet's worth and then a little bit more. plaintext := bytes.Repeat([]byte{0}, int(DefaultPlaintextChunkLength+42)) sealed := zeroSealWhole(plaintext) // Try truncating in the middle of a packet. truncated := sealed[:999] _, err := zeroOpenWhole(truncated) assertErrorType(t, err, BadSecretbox) // And try truncating at the first packet boundary. We still expect a // BadSecretbox error, because secretbox.Open will fail on an empty slice. packetLen := getPacketLen(DefaultPlaintextChunkLength) truncated = sealed[:packetLen] _, err = zeroOpenWhole(truncated) assertErrorType(t, err, BadSecretbox) } func TestPacketSwapInOneMessageFails(t *testing.T) { // Another sanity check test. This isn't proof that swapping is always // detectable, but it exercises the simplest cases. // Two full packets' worth. plaintext := bytes.Repeat([]byte{0}, int(DefaultPlaintextChunkLength*2)) sealed := zeroSealWhole(plaintext) // Swap the first two packets. Make sure to make *copies* of both packets, // or else the second swap will be a no-op. packetLen := getPacketLen(DefaultPlaintextChunkLength) packet1 := append([]byte{}, sealed[:packetLen]...) packet2 := append([]byte{}, sealed[packetLen:2*packetLen]...) copy(sealed, packet2) copy(sealed[packetLen:], packet1) // This should break both decoding. _, err := zeroOpenWhole(sealed) assertErrorType(t, err, BadSecretbox) } func TestPacketSwapBetweenMessagesFails(t *testing.T) { // Another sanity check test. This isn't proof that swapping is always // detectable, but it exercises the simplest cases. // One full packet's worth and then a little bit more. plaintext1 := bytes.Repeat([]byte{1}, int(DefaultPlaintextChunkLength+42)) sealed1 := zeroSealWhole(plaintext1) // Encrypt another same plaintext with a different nonce. (If we used the // same nonce, packet swapping *would* be possible, not to mention all the // crypto would be ruined.) plaintext2 := bytes.Repeat([]byte{2}, int(DefaultPlaintextChunkLength+42)) var nonce2 [16]byte nonce2[0] = 42 sealed2 := SealWhole(plaintext2, zeroSecretboxKey(), zeroSignKey(), testingPrefix(), &nonce2) // Swap the first packet between them. Make sure to make *copies* and not // just slices, or else the second swap will be a no-op. packetLen := getPacketLen(DefaultPlaintextChunkLength) firstPacket1 := append([]byte{}, sealed1[:packetLen]...) firstPacket2 := append([]byte{}, sealed2[:packetLen]...) copy(sealed1, firstPacket2) copy(sealed2, firstPacket1) // This should break both messages. _, err := zeroOpenWhole(sealed1) assertErrorType(t, err, BadSecretbox) _, err = OpenWhole(sealed2, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), &nonce2) assertErrorType(t, err, BadSecretbox) } // This type returns a random error the first time you Read from it, and then // defers to the inner reader for every read after that. type FakeIOErrorReader struct { inner io.Reader returnedErrorAlready bool } var _ io.Reader = (*FakeIOErrorReader)(nil) var fakeErrorString = "random error for the first read" func (f *FakeIOErrorReader) Read(buf []byte) (int, error) { if !f.returnedErrorAlready { f.returnedErrorAlready = true return 0, errors.New(fakeErrorString) } return f.inner.Read(buf) } func TestTransientIOErrorsInReaderWrappers(t *testing.T) { // If our DecodingReader gets a decryption error, it'll give up and fail // forever. But if either reader gets an IO error from its inner reader, it // should be willing to retry. Simulate this case on both ends, with a // FakeIOErrorReader that returns a Read error one time and then returns // real bytes on subsequent calls. plaintext := []byte("foo") plaintextBuf := bytes.NewBuffer(plaintext) fakePlaintextErrorReader := &FakeIOErrorReader{inner: plaintextBuf} encodingReader := NewEncodingReader( zeroSecretboxKey(), zeroSignKey(), testingPrefix(), zeroNonce(), fakePlaintextErrorReader) // The first read is an error. n, err := encodingReader.Read(throwawayBuffer()) if n != 0 { t.Fatalf("Expected 0 bytes, but received %d", n) } if err.Error() != fakeErrorString { t.Fatalf("Expected a fake error, but found: %s", err) } // Subsequent reads should succeed. encoded, err := io.ReadAll(encodingReader) if err != nil { t.Fatalf("no more errors expected during encoding, but found: %s", err) } // Similar test for the decoder. encodedBuffer := bytes.NewBuffer(encoded) fakeCiphertextErrorReader := &FakeIOErrorReader{inner: encodedBuffer} decodingReader := NewDecodingReader( zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroNonce(), fakeCiphertextErrorReader) // Again, the first read is an error. n, err = decodingReader.Read(throwawayBuffer()) if n != 0 { t.Fatalf("Expected 0 bytes, but received %d", n) } if err.Error() != fakeErrorString { t.Fatalf("Expected a fake error, but found: %s", err) } // And again, subsequent reads should succeed. decoded, err := io.ReadAll(decodingReader) if err != nil { t.Fatalf("no more errors expected during decoding, but found: %s", err) } if !bytes.Equal(plaintext, decoded) { t.Fatal("decoded bytes don't equal the input") } } func shouldPanic(t *testing.T, f func()) { defer func() { err := recover() require.NotNil(t, err) }() f() } func TestCoverageHacks(t *testing.T) { // Deliberately hit lines that don't/can't come up in normal execution. err := NewError(BadSecretbox, "blah blah blah") _ = err.Error() encoder := Encoder{} encoder.ChangePlaintextChunkLenForTesting(42) // Try to seal a packet longer than the internal buffer. shouldPanic(t, func() { encoder.sealOnePacket(999) }) // Try to Finish with too much data in the buffer. encoder.buf = bytes.Repeat([]byte{0}, 999) shouldPanic(t, func() { encoder.Finish() }) decoder := Decoder{} decoder.ChangePlaintextChunkLenForTesting(42) // Try to open a packet longer than the internal buffer. shouldPanic(t, func() { _, _ = decoder.openOnePacket(999) }) // Try to Finish with too much data in the buffer. decoder.buf = bytes.Repeat([]byte{0}, 999) shouldPanic(t, func() { _, _ = decoder.Finish() }) } func TestNullInPrefix(t *testing.T) { encoder := NewEncoder(zeroSecretboxKey(), zeroSignKey(), kbcrypto.SignaturePrefix("Keybase-bad-prefix\x00"), zeroNonce()) encoder.Write([]byte("kaboom")) shouldPanic(t, func() { encoder.Finish() }) } func TestPrefixDifference(t *testing.T) { // Test that different prefixes fail verification for index, input := range plaintextInputs { // Vary the chunk number, just for fun. chunkNum := uint64(index) //nolint:gosec // G115: Test code, index from range is non-negative sealed := sealPacket( []byte(input), zeroSecretboxKey(), zeroSignKey(), testingPrefix(), zeroChunkNonce(chunkNum)) // Use the correct prefix opened, err := openPacket( sealed, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix(), zeroChunkNonce(chunkNum)) if err != nil { t.Fatal(err) } if !bytes.Equal([]byte(input), opened) { t.Fatal("opened bytes don't equal the input") } // Use the wrong prefix _, err = openPacket( sealed, zeroSecretboxKey(), zeroVerifyKey(), testingPrefix()+"other", zeroChunkNonce(chunkNum)) assertErrorType(t, err, BadSignature) } } func TestVectors(t *testing.T) { if len(testVectors) < 1 { t.Fatalf("missing test vectors") } for i, v := range testVectors { if !v.chunked { t.Fatalf("i%d: non-chunked tests not supported yet", i) } sealedRef, err := hex.DecodeString(v.sealedHex) if err != nil { t.Fatalf("i:%d sealedHex is invalid hex: %v", i, err) } // Test seal encoder := zeroEncoder() encoder.Write([]byte(v.plaintext)) sealed := encoder.Finish() if !bytes.Equal(sealedRef, sealed) { t.Fatalf("i:%d sealed bytes not equal\n got: %x\nexpected: %x", i, sealed, sealedRef) } // Test open decoder := zeroDecoder() _, err = decoder.Write(sealedRef) require.NoError(t, err) opened, err := decoder.Finish() if err != nil { t.Fatalf("i:%d error opening: %v", i, err) } if !bytes.Equal([]byte(v.plaintext), opened) { t.Fatalf("i:%d opened bytes not equal\n got: %x\nexpected: %x", i, opened, v.plaintext) } } } var testVectors = []struct { chunked bool plaintext string sealedHex string }{ { chunked: true, plaintext: `The KID format Version 1 of Keybase KIDs are 35-bytes long, and take the form: ┌──────────┬──────────┬─────────────────────────────────┬──────────────┐ │ version │ key type │ payload │ '0a' trailer │ │ (1 byte) │ (1 byte) │ (32 bytes) │ (1 byte) │ └──────────┴──────────┴─────────────────────────────────┴──────────────┘ The fields are described as follows: `, sealedHex: `9c488f76be8f8f5eb84e37737017ce5dc92ea5c4752b6af99dd17df6f71d625252344511a903d0a8bfeac4574c52c1ecdfdba71beb95c8d9b60e0bd1bb4c4f83742d7b46c7d827c6a79397cd4dedd8a52d769e92798608a4389f46722f4f45391862a323f3006ec74f1b9d92d709291a17216119445b1dce49912f59b00eeb74af2e6779623de2b5d8e229bc2934dbf8d98c5dfd558dca8080fad3bf217e25f313ddaa3cc0cb193cd7561d8be207aa11b44822b6fd80dabcb817683883c44ee5cab7390ce13103cd098c5f7a9c2e36bf62462d163fbd78efb429e90f141d579f01eeeb33713c40b86069da04d53f9aa33ecadd7af28556573e76a11b88d27253cd90f743b3c8087bbdf18b1f3f3b8d1d7adc15f0a4021590812b822b9d38e6e79a59168dfb51be1ded8c47cc228b59d75ea1e1f61f7a26fb6d0e4992cffb0cf4709e3d9f9ad6252719fa795acc8f71bacf3e32bcf35b55f899d3768eb3dc5147eb96dd8c09f7818487bc5ab15d3d0ded506bc596a0a182236138010e28cda2e1dd63cf6706707888174562949bc6a75aa22823c4a82ecfec3ae30b1081465d46c3596c21017520f7ef2b63b7a7b733f2b32a7f00746dba953805048ef2af1cab77eb12c29227f42aaaedc2c394120fde461ab6c078b503fcaa73f20be05bef9c5e6718d49904295fc32f753316789cb61a65507ba8800eac82856dda77cfd961518887caeda8ccce8b16e2750911272daccff1c9a104a1481552d8340975ee6fe9c9e371a7053267b67ef97903d9f4a8071f85667f67cc09730e0789c3e230f529d1c4caeb047642e225063d5c305a1d03a1941c18f15b9e36692e41bf3340f1e9876db480974cbf41eedaaacd01ca6d62d270f4c8f0df25c1d781b1eaeac0b1d3887fd5e07f12c5bf576fe1e99471c8894f8981d0fb86e7ac860f9b2da2e0654520ac9b53cf3949a01d5866a06f7d8ad8865042d96d2cae118f9ab5980ada48a720e47b0ade9e984ef2e12904ca41ef30f2ff0464107042aca152ffd5b7081fd481fe76aa23f04d840f43a6e2f17ae5dea74298730c7ffce42bafe108cc70b5839a9ebb28cd8318d03529d680d75a68cc3dbf261c43eebc698bcf4c6f90`, }, } func TestAssociatedData(t *testing.T) { for _, associatedData := range associatedDataInputs { for _, input := range plaintextInputs { sealed := zeroSealWithAssociatedData([]byte(input), associatedData) opened, err := zeroOpenWithAssociatedData(sealed, associatedData) if err != nil { t.Fatal(err) } if !bytes.Equal([]byte(input), opened) { t.Fatal("opened bytes don't equal the input") } } } plaintext := "This is one time where television really fails to capture the true excitement of a large squirrel predicting the weather." associatedData := "groundhog day" sealed := zeroSealWithAssociatedData([]byte(plaintext), associatedData) opened, err := zeroOpenWithAssociatedData(sealed, associatedData) require.NoError(t, err) require.Equal(t, opened, []byte(plaintext)) // tweaking the associated data should fail to open incorrectAssociatedData := "groundhog day 2, the repeatening" opened, err = zeroOpenWithAssociatedData(sealed, incorrectAssociatedData) require.True(t, bytes.Equal(opened, []byte{})) assertErrorType(t, err, AssociatedDataMismatch) }