Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarification regarding length of passcode #242

Open
lenkan opened this issue Apr 8, 2024 · 8 comments
Open

Clarification regarding length of passcode #242

lenkan opened this issue Apr 8, 2024 · 8 comments
Labels
question Further information is requested

Comments

@lenkan
Copy link
Collaborator

lenkan commented Apr 8, 2024

Currently, a 22 character string is generated from "randomPasscode", but only 21 characters is used to derive an AID. Is there a reason for this discrepancy?

@lenkan
Copy link
Collaborator Author

lenkan commented Apr 8, 2024

See this example https://github.com/lenkan/signify-ts/blob/e7f46e6879b78af26bd6c9c8a657ed374dbd09e1/test/app/controller.test.ts#L59-L76 of a test that is failing on line 75. I.e., changing the last character of a generated passcode yield the same controller AID.

@SmithSamuelM
Copy link

SmithSamuelM commented Apr 8, 2024

If I remember, I beleive this was to enable use of 1password or other tools for generating the passcode without having to have Matter object support. Since Matter midpads with zeros when prepending the code to the converted raw, it makes it hard to do simply. So if instead a 16 byte random set of bytes is converted which creates a 24 character Base64 with two trailing pad chars. Stripping off the pad characters leaves 22 chars. But then one has to shift in from the left the prepad. So to avoid this shift, instead the last three characters are deleted.

Now the 2 char code for Salt plus a mid pad of 'A' (i.e. zeros) is prefixed to get a valid qb64 Salt. Since what you want is a random Salt it doesn't matter. the deleted extra trailing character in Base64 has either 4 or 6 bits of information depending on how it was generated. So the Salt has either 124 or 122 bits of entropy instead of 128 which is close enough.

So can set up 1password to generate 21 Base64 characters. Or convert to Base64 from random bytes and only use the first 21.

So the test should replicate how the actual code works in python. See habbing.py Habery.setup

        if bran and not seed:  # create seed from stretch of bran as salt
            if len(bran) < 21:
                raise ValueError(f"Bran (passcode seed material) too short.")
            bran = coring.MtrDex.Salt_128 + 'A' + bran[:21]  # qb64 salt for seed
            signer = coring.Salter(qb64=bran).signer(transferable=False,
                                                     tier=tier,
                                                     temp=temp)
            seed = signer.qb64
            if not aeid:  # aeid must not be empty event on initial creation
                aeid = signer.verfer.qb64  # lest it remove encryption

        if salt is None:  # salt for signing keys not aeid seed
            salt = coring.Salter().qb64
        else:
            salt = coring.Salter(qb64=salt).qb64

the passcode is the first 21 characters of bran.

@lenkan
Copy link
Collaborator Author

lenkan commented Apr 9, 2024

Thank you for your reply. I have not yet ventured into the Matter classes so the first paragraph is a bit hard to digest for me. My confusion mainly comes from that we are referencing both 22 and 21 character passcodes in keripy and signify. In kli passcode generate we can generate new passcodes of length 21 characters:

https://github.com/WebOfTrust/keripy/blob/main/src/keri/app/cli/commands/passcode/generate.py#L15-L32

In the docs for kli init (and I think all other kli commands interacting with the wallet), it says to pass a 22 character string.

https://github.com/WebOfTrust/keripy/blob/24cda6d853d24918a0acd27f8af7877754f06ff4/src/keri/app/cli/commands/init.py#L55-L56

In signify-ts randomPasscode(), we return a 22 character string:

export function randomPasscode(): string {
const raw = libsodium.randombytes_buf(16);
const salter = new Salter({ raw: raw });
return salter.qb64.substring(2);
}

  • Should we change the randomPasscode implementation in signify-ts to return only the first 21 characters to avoid confusion, or is there a value in having this function return the 22 characters?
  • Should the docs in keripy/keria/signify that references a 22 character passcode be rewritten to be more explicit that only the 21 first characters are being used?

I can open these issues/PRs if needed.

@lenkan
Copy link
Collaborator Author

lenkan commented Apr 9, 2024

So the Salt has either 124 or 122 bits of entropy instead of 128 which is close enough.

Got it. Thanks. Do you see any issues with this since keria is an "online" system in contrast to the kli which would not be open to attacks in the same way?

@SmithSamuelM
Copy link

The docs are out of date. I just fixed the doc strings in the python code which incorrectly stated bran as 22 characters not 21. This was a bug that was fixed a long time ago but the docs didn’t get updated, apparently.

There is no real value in 22 character string. 21 Base64 chars at 6 bits per character is 126 bits of entropy. Whereas 22 base64 characters at 6 bits per character is 132 bits of entropy. 16 bytes at 8 bits per byte is 128 bits of entropy. So when converting to Base64 the 128 bits takes 21..3 base 64 characters the remaining 4 bits in the 22nd char are padded zeros.
So we only lose 2 bits if we use 21 base64 characters. The advantage of using base64 characters is that password managers like 1password can generate random passcodes of 21 characters long that use only [A-Z,a-z,0-9]. which means 62 choices per character or log2(62) or almost 6 bits. So 21 of these is about 122 bits. So it allows paascode management outside of signify which is important for security and convenience.

The problem is that if we use 22 characters and simply prepend the CESR code of two chars onto the 22, the first of the 22 chars might have bits where they are forbidden by CESR. So we can’t simply construct a valid verifiable CESR frlly qualified salt that way. But with a ‘A’ character which is all zeros prepended to 21 then we get 22 that are valid at the loss of 2 bits.

@SmithSamuelM
Copy link

By stripping the last three chars from a 24 char Base64 conversion of a 16 byte salt we get consistent behavior with using 21 characters generated by a passcode manager. We avoid any padding or shifing issues. So 21 characters always works. We want to stript the last 3 from the 24 because the last 3 only have 2 bits of entropy. If we stripped the first three we would throw away 18 bits of entropy.

@lenkan
Copy link
Collaborator Author

lenkan commented Apr 11, 2024

I read this in the EGF for vLEI

All key-pairs MUST be generated using a cryptographic algorithm with at least 128 bits of
cryptographic strength. This includes using a source of entropy of at least 128 bits of cryptographic
strength for the salt or seed used to generate the private key of the key pair.

Cited from https://www.gleif.org/vlei/introducing-the-vlei-ecosystem-governance-framework/2023-12-15_vlei-egf-v2.0-technical-requirements-part-1-keri-infrastructure_v1.2_final.pdf

Since only a 21 character passcode is used to generate the key pair, it does not comply right? Or am I misunderstanding it?

@SmithSamuelM
Copy link

SmithSamuelM commented Apr 11, 2024 via email

@lenkan lenkan added the question Further information is requested label Oct 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants