mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-06-10 20:07:46 +02:00
feat: decrypt Chromium v10/v11 across host OS (#605)
This commit is contained in:
@@ -26,16 +26,20 @@ func decryptValue(masterKeys masterkey.MasterKeys, ciphertext []byte) ([]byte, e
|
||||
version := crypto.DetectVersion(ciphertext)
|
||||
switch version {
|
||||
case crypto.CipherV10:
|
||||
return crypto.DecryptChromium(masterKeys.V10, ciphertext)
|
||||
// v10's cipher depends on the platform that sealed it: a 32-byte AES-256 key means GCM
|
||||
// (Windows), a 16-byte AES-128 key means CBC (macOS/Linux). Dispatching on key length keeps
|
||||
// cross-host decryption OS-independent: a 32-byte key dumped on Windows decrypts here on macOS.
|
||||
if len(masterKeys.V10) == 32 {
|
||||
return crypto.DecryptChromiumGCM(masterKeys.V10, ciphertext)
|
||||
}
|
||||
return crypto.DecryptChromiumCBC(masterKeys.V10, ciphertext)
|
||||
case crypto.CipherV11:
|
||||
// v11 is Linux-only and shares v10's AES-CBC path, but uses the keyring-derived kV11Key
|
||||
// rather than the peanuts-derived kV10Key — so a Linux profile with both prefixes needs
|
||||
// distinct per-tier keys to decrypt everything.
|
||||
return crypto.DecryptChromium(masterKeys.V11, ciphertext)
|
||||
// v11 is Linux-only AES-CBC; same algorithm as Linux v10 but the key comes from the keyring
|
||||
// (kV11Key) rather than peanuts (kV10Key), so both tiers need distinct keys.
|
||||
return crypto.DecryptChromiumCBC(masterKeys.V11, ciphertext)
|
||||
case crypto.CipherV20:
|
||||
// v20 is cross-platform AES-GCM; routed through a dedicated function so Linux/macOS CI can
|
||||
// exercise the same decryption path as Windows.
|
||||
return crypto.DecryptChromiumV20(masterKeys.V20, ciphertext)
|
||||
// v20 is cross-platform AES-GCM (Chrome 127+ ABE); same wire layout as Windows v10.
|
||||
return crypto.DecryptChromiumGCM(masterKeys.V20, ciphertext)
|
||||
case crypto.CipherV12:
|
||||
// Chromium's SecretPortalKeyProvider (Flatpak / xdg-desktop-portal) — HKDF-SHA256 +
|
||||
// AES-256-GCM with a secret retrieved via org.freedesktop.portal.Desktop. Recognized here
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// TestDecryptValue_V20 is cross-platform because v20's ciphertext format
|
||||
// (AES-GCM with 12-byte nonce) is platform-independent; only the key source
|
||||
// (Chrome ABE on Windows) differs by OS. Running on Linux/macOS CI protects
|
||||
// the routing in decryptValue + crypto.DecryptChromiumV20 from regressions.
|
||||
// the routing in decryptValue + crypto.DecryptChromiumGCM from regressions.
|
||||
func TestDecryptValue_V20(t *testing.T) {
|
||||
plaintext := []byte("v20_test_value")
|
||||
nonce := []byte("v20_nonce_12") // 12-byte AES-GCM nonce
|
||||
@@ -34,3 +34,20 @@ func TestDecryptValue_V20_ShortCiphertext(t *testing.T) {
|
||||
_, err := decryptValue(masterkey.MasterKeys{V20: testAESKey}, []byte("v20"))
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// TestDecryptValue_V10_CrossHostGCM proves a v10 ciphertext sealed with a 32-byte
|
||||
// AES-256 key (a Windows-origin dump) decrypts via decryptValue on any host — the
|
||||
// core cross-OS guarantee. testAESKey is 16B, so this uses an explicit 32B key.
|
||||
func TestDecryptValue_V10_CrossHostGCM(t *testing.T) {
|
||||
key32 := []byte("0123456789abcdef0123456789abcdef") // 32 bytes
|
||||
plaintext := []byte("v10_cross_host")
|
||||
nonce := []byte("v10_nonce_12") // 12-byte AES-GCM nonce
|
||||
|
||||
gcm, err := crypto.AESGCMEncrypt(key32, nonce, plaintext)
|
||||
require.NoError(t, err)
|
||||
ciphertext := append([]byte("v10"), append(nonce, gcm...)...)
|
||||
|
||||
got, err := decryptValue(masterkey.MasterKeys{V10: key32}, ciphertext)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, plaintext, got)
|
||||
}
|
||||
|
||||
@@ -54,17 +54,19 @@ func encryptWithDPAPI(plaintext []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
func TestDecryptValue_V10_Windows(t *testing.T) {
|
||||
// Windows uses AES-GCM for v10 (not AES-CBC like macOS/Linux)
|
||||
// Windows v10 is AES-256-GCM, so the master key is 32 bytes; decryptValue routes v10 by key
|
||||
// length (32B→GCM). testAESKey is 16B, so this uses an explicit 32B key.
|
||||
key32 := []byte("0123456789abcdef0123456789abcdef") // 32 bytes
|
||||
plaintext := []byte("test_secret_value")
|
||||
nonce := []byte("123456789012") // 12-byte nonce
|
||||
|
||||
gcmEncrypted, err := crypto.AESGCMEncrypt(testAESKey, nonce, plaintext)
|
||||
gcmEncrypted, err := crypto.AESGCMEncrypt(key32, nonce, plaintext)
|
||||
require.NoError(t, err)
|
||||
|
||||
// v10 format on Windows: "v10" + nonce(12) + encrypted
|
||||
ciphertext := append([]byte("v10"), append(nonce, gcmEncrypted...)...)
|
||||
|
||||
got, err := decryptValue(masterkey.MasterKeys{V10: testAESKey}, ciphertext)
|
||||
got, err := decryptValue(masterkey.MasterKeys{V10: key32}, ciphertext)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, plaintext, got)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user