Skip to content

Why is Passphrase strength "only" 116bit? #477

Description

@dimitry12

If I understand passphrase generation correctly, it's 6 groups of 4 characters. Characters come from [A-Z1-9]-range with 1IOS5U excluded to avoid ambiguity. That's 29 distinct characters. $log_2 (29^{24})$ is approximately 116.6.

Given long-term nature of Certify-key, why not follow NIST recommended >=192 bits when storing it - for key-passphrase and/or symmetric encryption at rest?

That would require going from 6 groups to 10 groups, providing 194 bits of entropy:

export CERTIFY_PASS=$(LC_ALL=C tr -dc 'A-Z1-9' < /dev/urandom | \
  tr -d "1IOS5U" | fold -w 50 | sed "-es/./ /"{1..46..5} | \
  cut -c2- | tr " " "-" | head -1) ; printf "\n$CERTIFY_PASS\n\n"

Activity

  1. drduh commented on Mar 20, 2025

    @drduh
    Owner

    I am not familiar with this recommendation - can you cite the specific NIST requirement we could/should adhere to?

  2. dimitry12 commented on Mar 20, 2025

    @dimitry12
    Author

    I could only trace directly to NIST the >=128bits distinction on page 59 of https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r5.pdf

    Image

    In this context, passphrase strength in the recipe is in-between. 7 groups of 4 characters would get us to 136 bits.

  3. drduh commented on Apr 4, 2025

    @drduh
    Owner

    thanks @dimitry12

    the private material being stored offline - and recommended volume encryption steps - are meant to be compensating controls on reduced passphrase strength.

    increasing the length would certainly buff it, and we should recommend as much, but I want to be careful making it too long, as it may ultimately exhaust and discourage the operator.

    let me know what you think; meanwhile, I will see if the template can be improved.

  4. added a commit that references this issue on Apr 6, 2025
    0b018bf
  5. drduh commented on Apr 25, 2025

    @drduh
    Owner

    @dimitry12 expanded the passphrase generation command with options to increase the length, change group size and delimiter character. please give it a test - thank you!

    https://github.com/drduh/YubiKey-Guide/pull/493/files

  6. dimitry12 commented on Apr 25, 2025

    @dimitry12
    Author

    @drduh Thank you for introducing flexibility in generating the passphrase and linking to this conversation!

    While the default passphrase (6 groups of 4 characters each) is still below NIST-suggested strength for long-term storage, you do instruct to keep the backup "offline in a secure and durable location."

    Anyone who considers deviating from this advice -- say, wishes to backup online or has no access to a secure offline location -- now has an easier time generating a better passphrase and understanding how long it should be.

  7. forbytten commented on May 27, 2025

    @forbytten
    Contributor

    Hi @dimitry12,

    I would like to offer an alternative interpretation of the NIST standard you link to:

    1. The NIST table you highlighted only refers to the underlying encryption key. For AES-128, used by GPG/gpg-agent to encrypt the private key, the encryption key has 128 bits of entropy. For AES-256, used by LUKS2, the key has 256 bits of entropy. Both are considered strong by today's computing standards, albeit AES-256 is preferred, but only the latter is expected to be Quantum Computing resistant, due to Grover's algorithm which may effectively halve the bit strength.

    2. The encryption key is derived from the user's passphrase via a KDF (key derivation function). The NIST document you link to also mentions this on page 109:

    Image

    Unfortunately, SP 800-132 that it references hasn't been updated since 2010. These days, Argon2id is the KDF recommended by most sources, which is also what LUKS2 uses.

    GPG uses gpg-agent to encrypt the private keys and the latter uses a weak KDF and since it only stores the key using AES-128, you can only use a password/passphrase with a maximum of 128 bits entropy. Anything greater won't increase security. However, with the model the YubiKey-Guide follows, this isn't a huge concern, as the subkeys have a 1y expiry and should only reside on the YubiKey in day to day operations and the certify key should only reside on the LUKS2 backup.

    That said, if you wish to completely remove the KDF from your threat model, you can choose a password/passphrase with the same entropy as the resulting encryption key. So 128 bits for AES-128 and 256 bits for AES-256.

  8. dimitry12 commented on May 29, 2025

    @dimitry12
    Author

    Thank you @forbytten! This definitely adds to my understanding of the process.

    GPG uses gpg-agent to encrypt the private keys and the latter uses a weak KDF and since it only stores the key using AES-128, you can only use a password/passphrase with a maximum of 128 bits entropy. Anything greater won't increase security.

    This is very good to know.

    Do you have a reference for the length of the key derived by LUKS2 from passphrase?

  9. forbytten commented on May 30, 2025

    @forbytten
    Contributor

    @dimitry12 no worries.

    Do you have a reference for the length of the key derived by LUKS2 from passphrase?

    The built-in help lists the compiled-in defaults. The last two lines indicate LUKS uses aes-xts-plain64 by default with a 256 bit key and XTS mode requires two keys so it actually uses two 256 bit keys. This example is using cryptsetup 2.7.0.

    $ cryptsetup --help|grep -A20 compiled-in
    Default compiled-in metadata format is LUKS2 (for luksFormat action).
    
    LUKS2 external token plugin support is enabled.
    LUKS2 external token plugin path: /lib/x86_64-linux-gnu/cryptsetup.
    
    Default compiled-in key and passphrase parameters:
            Maximum keyfile size: 8192kB, Maximum interactive passphrase length 512 (characters)
    Default PBKDF for LUKS1: pbkdf2, iteration time: 2000 (ms)
    Default PBKDF for LUKS2: argon2id
            Iteration time: 2000, Memory required: 1048576kB, Parallel threads: 4
    
    Default compiled-in device cipher parameters:
            loop-AES: aes, Key 256 bits
            plain: aes-xts-plain64, Key: 256 bits, Password hashing: sha256
            LUKS: aes-xts-plain64, Key: 256 bits, LUKS header hashing: sha256, RNG: /dev/urandom
            LUKS: Default keysize with XTS mode (two internal keys) will be doubled.
    

    After creating a LUKS2 container, you can also check the header to confirm, where the key length is the combined length of the two 256 bit keys:

    $ sudo cryptsetup luksDump disk.img|grep -A 5 Keyslots:
    Keyslots:
      0: luks2
            Key:        512 bits
            Priority:   normal
            Cipher:     aes-xts-plain64
            Cipher key: 512 bits
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions