Files
Roger 7b9a973c9c fix: per-tier master-key retrievers for mixed-cipher profiles (#579)
* fix: per-tier master-key retrievers for mixed-cipher profiles
2026-04-20 17:33:38 +08:00

112 lines
3.5 KiB
Go

//go:build darwin || linux
package chromium
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/moond4rk/hackbrowserdata/crypto"
"github.com/moond4rk/hackbrowserdata/crypto/keyretriever"
)
func TestDecryptValue_V10(t *testing.T) {
plaintext := []byte("test_secret_value")
testCBCIV := bytes.Repeat([]byte{0x20}, 16)
cbcEncrypted, err := crypto.AESCBCEncrypt(testAESKey, testCBCIV, plaintext)
require.NoError(t, err)
v10Ciphertext := append([]byte("v10"), cbcEncrypted...)
tests := []struct {
name string
key []byte
want []byte
wantErrMsg string // empty = no error expected
}{
{
name: "decrypts correctly",
key: testAESKey,
want: plaintext,
},
{
name: "wrong key returns padding error",
key: []byte("wrong_key_1234!!"),
wantErrMsg: "invalid PKCS5 padding",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := decryptValue(keyretriever.MasterKeys{V10: tt.key}, v10Ciphertext)
if tt.wantErrMsg != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.wantErrMsg)
assert.Nil(t, got)
return
}
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}
func TestDecryptValue_V11(t *testing.T) {
plaintext := []byte("test_secret_value")
testCBCIV := bytes.Repeat([]byte{0x20}, 16)
cbcEncrypted, err := crypto.AESCBCEncrypt(testAESKey, testCBCIV, plaintext)
require.NoError(t, err)
v11Ciphertext := append([]byte("v11"), cbcEncrypted...)
// v11 ciphertexts route to the V11 slot (Linux's keyring-derived kV11Key) — not V10 (peanuts).
got, err := decryptValue(keyretriever.MasterKeys{V11: testAESKey}, v11Ciphertext)
require.NoError(t, err)
assert.Equal(t, plaintext, got)
}
// TestDecryptValue_V10_V11_SlotSeparation is the Linux analog of the #578 regression test: a
// profile carrying both v10 (peanuts) and v11 (keyring) ciphertexts must route each prefix to
// its own slot, not share a single key. Build-tag scoped to darwin/linux because v10/v11 use
// AES-CBC on these platforms; Windows uses AES-GCM for v10 and is covered separately by
// decrypt_windows_test.go.
func TestDecryptValue_V10_V11_SlotSeparation(t *testing.T) {
k10 := bytes.Repeat([]byte{0x10}, 16) // V10 slot key (peanuts-derived kV10Key)
k11 := bytes.Repeat([]byte{0x11}, 16) // V11 slot key (keyring-derived kV11Key)
iv := bytes.Repeat([]byte{0x20}, 16) // matches crypto.chromiumCBCIV on darwin/linux
v10plain := []byte("password-from-v10-era")
v11plain := []byte("password-from-v11-era")
v10Enc, err := crypto.AESCBCEncrypt(k10, iv, v10plain)
require.NoError(t, err)
v10Ciphertext := append([]byte("v10"), v10Enc...)
v11Enc, err := crypto.AESCBCEncrypt(k11, iv, v11plain)
require.NoError(t, err)
v11Ciphertext := append([]byte("v11"), v11Enc...)
keys := keyretriever.MasterKeys{V10: k10, V11: k11}
t.Run("v10 ciphertext decrypts via V10 slot", func(t *testing.T) {
got, err := decryptValue(keys, v10Ciphertext)
require.NoError(t, err)
assert.Equal(t, v10plain, got)
})
t.Run("v11 ciphertext decrypts via V11 slot", func(t *testing.T) {
got, err := decryptValue(keys, v11Ciphertext)
require.NoError(t, err)
assert.Equal(t, v11plain, got)
})
t.Run("swapped keys fail both directions", func(t *testing.T) {
swapped := keyretriever.MasterKeys{V10: k11, V11: k10}
_, err := decryptValue(swapped, v10Ciphertext)
require.Error(t, err, "v10 with V11's key must fail")
_, err = decryptValue(swapped, v11Ciphertext)
require.Error(t, err, "v11 with V10's key must fail")
})
}