Skip to content

xchain-crypto: keystore uses aes-128-ctr (only 16 of 32 derived bytes); consider aes-256-ctr #1720

Description

@underthesun49

Summary

In @xchainjs/xchain-crypto, the keystore encryption derives a 32-byte key via PBKDF2 (dklen = 32) but only the first 16 bytes are used for the cipher, and the cipher is hard-coded to aes-128-ctr. Bumping to aes-256-ctr and feeding the full 32-byte derived key would raise the key-material strength to the modern standard at no extra cost (the bytes are already derived).

Where

packages/xchain-crypto/src/crypto.ts (observed in published lib/index.js, v1.0.7):

const cipher = 'aes-128-ctr' // Encryption cipher
const dklen = 32             // Derived key length

// encrypt
const derivedKey = await pbkdf2Async(Buffer.from(password), salt, kdfParams.c, kdfParams.dklen, hashFunction)
const cipherIV = crypto.createCipheriv(cipher, derivedKey.slice(0, 16), iv) // only first 16 bytes used
// derivedKey.slice(16, 32) is reused as the MAC key

So of the 32 derived bytes, bytes 0..16 are the AES-128 key and bytes 16..32 are the MAC key.

Suggestion

Switch the cipher to aes-256-ctr and use a full 32-byte AES key (deriving a separate MAC key, e.g. by increasing dklen to 64 and splitting 32/32, or deriving the MAC key independently).

Backwards compatibility

This must be done without breaking existing keystores. Decryption already reads the cipher name from keystore.crypto.cipher, so old aes-128-ctr files can still be decrypted as long as the key-slicing for legacy files is preserved. The cleanest path is to bump the keystore version and branch decryption on it: new files use aes-256-ctr + full key, old files keep the current 16-byte slice. Happy to open a PR along these lines if the approach sounds good.

Impact

Low severity — AES-128 has no known practical break — but for software protecting seed phrases, AES-256 is the expected standard and the derived bytes are already available.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions