package kdbx import ( "bytes" "encoding/hex" "testing" ) // RFC 8439 section 2.4.2. func TestChaCha20RFC8439(t *testing.T) { key := make([]byte, 32) for i := range key { key[i] = byte(i) } nonce, _ := hex.DecodeString("000000000000004a00000000") plain := []byte("Ladies and Gentlemen of the class of '99: If I could offer you " + "only one tip for the future, sunscreen would be it.") want, _ := hex.DecodeString( "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0b" + "f91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d8" + "07ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab7793736" + "5af90bbf74a35be6b40b8eedf2785e42874d") got := make([]byte, len(plain)) newChaCha20(key, nonce, 1).XORKeyStream(got, plain) if !bytes.Equal(got, want) { t.Fatalf("ciphertext =\n%x\nwant\n%x", got, want) } } // The keystream must be continuous across calls, because KDBX protects one // value at a time from a single stream. func TestChaCha20StreamIsContinuousAcrossCalls(t *testing.T) { key := make([]byte, 32) nonce := make([]byte, 12) src := make([]byte, 200) whole := make([]byte, len(src)) newChaCha20(key, nonce, 0).XORKeyStream(whole, src) piecewise := make([]byte, len(src)) c := newChaCha20(key, nonce, 0) for _, n := range []int{1, 7, 56, 64, 72} { c.XORKeyStream(piecewise[:n], src[:n]) piecewise = piecewise[n:] src = src[n:] } piecewise = make([]byte, 200) c = newChaCha20(key, nonce, 0) off := 0 for _, n := range []int{1, 7, 56, 64, 72} { c.XORKeyStream(piecewise[off:off+n], make([]byte, n)) off += n } if !bytes.Equal(whole, piecewise) { t.Fatal("keystream differs when produced in pieces") } }