Skip to content

Draft : SPAKE2PLUS protocol flow proposal#73

Closed
silabs-Kusumit wants to merge 4 commits intoARM-software:mainfrom
silabs-Kusumit:SPAKE2-proposal
Closed

Draft : SPAKE2PLUS protocol flow proposal#73
silabs-Kusumit wants to merge 4 commits intoARM-software:mainfrom
silabs-Kusumit:SPAKE2-proposal

Conversation

@silabs-Kusumit
Copy link
Copy Markdown

This PR is intended for finalizing the expected SPAKE2+ flow using PAKE API. If flow is finalized, we'll have an actual PR with expected api changes to review later.

This PR continues from #65.

Signed-off-by: Kusumit Ghoderao <Kusumit.Ghoder@silabs.com>
@athoelke athoelke added Crypto API Issue or PR related to the Cryptography API proposal An RFC, or proposal for discussion labels Jul 6, 2023
@athoelke athoelke linked an issue Jul 7, 2023 that may be closed by this pull request
@athoelke
Copy link
Copy Markdown
Contributor

athoelke commented Jul 7, 2023

I'm trying to consider ways to handle the post-registration secrets for the API. The way that w0||w1 and w0||L work in the protocol is similar to a private and public pair of keys for signing. If the public [prover] key is known by an attacker, it does not enable them to spoof the client (in contrast with an attacker that knows the initial secret password). As noted in the earlier draft, this does not mean that a client should pass w0||L to the prover over an unsecured channel.

So, would it make any sense to:

  • Define an asymmetric key type for SPAKE2+.

    • The key pair is used as the secret input to the PAKE API as the SPAKE2+ client role. This key is w0||w1.
    • The public key is used as the secret input to the PAKE API as the SPAKE2+ server role. This key is w0||L. This can be computed from the 'private' key value.
  • Define a KDF algorithm family that takes PBKDF output w0s||w1s to generate the key pair. The public key can be exported from the key pair for transfer to the server/prover.

Either:

  1. The key type must be parameterized by the ECC family and curve size, and then the KDF can determine how to compute the key value from the w0s||w1s input based on the output key attributes. Or
  2. The KDF must be parameterized by the ECC family and curve size, and this information is stored as part of the key data, for validation that the key is valid for use with a particular PAKE operation.

It doesn't seem necessary to parameterize both the key type and the KDF, as these keys cannot be generated, they are always derived.

@silabs-hannes
Copy link
Copy Markdown

silabs-hannes commented Jul 11, 2023

I'm trying to consider ways to handle the post-registration secrets for the API. The way that w0||w1 and w0||L work in the protocol is similar to a private and public pair of keys for signing. If the public [prover] key is known by an attacker, it does not enable them to spoof the client (in contrast with an attacker that knows the initial secret password). As noted in the earlier draft, this does not mean that a client should pass w0||L to the prover over an unsecured channel.

So, would it make any sense to:

  • Define an asymmetric key type for SPAKE2+.

    • The key pair is used as the secret input to the PAKE API as the SPAKE2+ client role. This key is w0||w1.
    • The public key is used as the secret input to the PAKE API as the SPAKE2+ server role. This key is w0||L. This can be computed from the 'private' key value.
  • Define a KDF algorithm family that takes PBKDF output w0s||w1s to generate the key pair. The public key can be exported from the key pair for transfer to the server/prover.

Either:

  1. The key type must be parameterized by the ECC family and curve size, and then the KDF can determine how to compute the key value from the w0s||w1s input based on the output key attributes. Or
  2. The KDF must be parameterized by the ECC family and curve size, and this information is stored as part of the key data, for validation that the key is valid for use with a particular PAKE operation.

It doesn't seem necessary to parameterize both the key type and the KDF, as these keys cannot be generated, they are always derived.

Thinking a bit out loud..

Having specific key types for these SPAKE2+ secrets sounds like a good thing to me. It feels like any other choice would make it a bit awkward to implement.

If I understand correctly, the new KDF algorithm for SPAKE2+ would only perform modulo computations and potentially a group multiplication. The output also wouldn't be usable directly as a symmetric key like you might expect the output of the KDF APIs to be. It feels a bit wrong to include this functionality under the KDF algorithm family.

Performing the registration computation purely using the KDF APIs also mean that the user must provide some values as concatenated inputs into the PBKDF, and then later provide the same values in a more structured manner to the PAKE APIs (i.e. user IDs). The use of the password as a regular data input also doesn't really fit into how PSA Crypto wants you to treats secrets.

The sheer amount of function calls that will be needed in order to put the registration together with the actual interactive key agreement is quite significant. The computational overhead would at least probably be small compared to the time it takes to run the PBKDF part.

All this being said, I can't think of a good alternative that isn't duplicating a large part of the KDF API as part of the PAKE API. Maybe maybe it's possible to come up with some 'registration ciphersuite encoding' that supports some suggested methods to compute w0 and w1...? I haven't thought it through yet, though. What are your thoughts on something like that?

* `psa_pake_set_user()` to input ProverID
* `psa_pake_set_peer()` to input VerifierID
* `psa_pake_set_role()` to set role as client/server
* `psa_pake_input()` with *(new)* `PSA_PAKE_STEP_ADDITIONAL_DATA` step to input context (additional data).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continuing the old PR thread.

I also wondered why we weren't using psa_pake_input(), but Saketh's answer convinced me that a new API could make sense. The current set of elements input to/output from psa_pake_input()/psa_pake_output() all come from the interactive part of the protocol -- i.e. the output from one is the input to the other. The context doesn't fit as well into that current set of steps.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The context input is a part of the setup for the SPAKE2+ operation. Therefore I have added psa_pake_set_context() for adding the context data to the PAKE operation.


Computing these values is considered Out of Scope for PAKE API as this a registration phase which could happen out of actual protocol flow.

Since W0 & W1 and L serve as registartion records and verification value, these values can be treated as secrets.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continuation of the old PR thread (and a bit of this comment as well).

I checked with the Matter protocol, and there a commissionee (verifier) should get (w0, L) installed at manufacturing time. When that device is later commissioned to a network, the password is communicated to the commissioner (the prover) OOB. The commissioner computes (w0, w1), and moves on with the rest of the protocol. This process will only happen a single time for that device, so there is no need to store the intermediate result (w0, w1) to the key store.

It also makes sense to me to have a client be able to compute and communicate (w0, L) instead of the password (yay augmented PAKE!).

So I agree that it would be best if it is possible to optionally input the password instead of the values derived using it. It would also be nice if you didn't have to import the password before usage, similar to psa_key_derivation_input_bytes().

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I agree that it would be best if it is possible to optionally input the password instead of the values derived using it. It would also be nice if you didn't have to import the password before usage, similar to psa_key_derivation_input_bytes().

At the moment, when a secret input is provided to a KDF via psa_key_derivation_input_bytes(), this prevents the application using psa_key_derivation_output_key(). See the documentation for PSA_KEY_DERIVATION_INPUT_SECRET, PSA_KEY_DERIVATION_INPUT_PASSWORD, and psa_key_derivation_output_key().

If we provide a high-level function, such as psa_pake_registration(), that outputs a key that can be stored in the key-store, then it might be appropriate to permit a non-key input of the password, because storing a SPAKE2+ password in the keystore is not required, and not recommended. However, I am not sure we should relax the 'secret key input for secret key output' requirement that the PAKE or key derivation operations currently have.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice, a concrete PBKDF is required, with specific input parameters (for both the construction of the input keying material, the salt and any iteration count), and a specific length for the w0s||w1s output string.

Matter does define exactly how this part of the protocol must run, including the absence of identity-strings, and how the PBKDF2 parameters are shared and included in the transcript context.

If we define a higher-level API, such as psa_pake_registration(), that matches the Matter requirements, does this become a Matter-specific API? - or do we add enough parameters to support other application uses that do have identities, and generate different lengths of intermediate w0s||w1s output?

and

At the moment, when a secret input is provided to a KDF via psa_key_derivation_input_bytes(), this prevents the application using psa_key_derivation_output_key()

I have defined the psa_pake_registration() to take as input a PBKDF operation and output key attributes. The PBKDF operation will be initialized OOB and can accept a non-key password as input. We can import the non-key output of the KDF with attributes provided as input and store the key id in the PAKE operation.

@silabs-Kusumit
Copy link
Copy Markdown
Author

Define an asymmetric key type for SPAKE2+.

  • The key pair is used as the secret input to the PAKE API as the SPAKE2+ client role. This key is w0||w1.
  • The public key is used as the secret input to the PAKE API as the SPAKE2+ server role. This key is w0||L. This can be computed from the 'private' key value.

I agree that this will be helpful for the implementation.

@silabs-Kusumit
Copy link
Copy Markdown
Author

silabs-Kusumit commented Jul 12, 2023

  • Define a KDF algorithm family that takes PBKDF output w0s||w1s to generate the key pair. The public key can be exported from the key pair for transfer to the server/prover.

Adding on to @silabs-hannes comment,

I think that we can add a new API psa_pake_registration which takes in parameters such as the ECC family and curve size of the output key and the password, computes the PBKDF intermediate, does the point multiplication and finally stores w0||w1 in the key store. This will internally call the KDF APIs.

Since the registration can happen much before the actual protocol is executed, I think that we should not include the psa_pake_operation_t as a parameter and treat this as a one-off API call.

@athoelke
Copy link
Copy Markdown
Contributor

[Apologies for the delay in responding, but now back from a work conference ...]

Thanks for the feedback on the key-pair idea, this does seem to be a valid approach for SPAKE2+.

I said:

Either:

  1. The key type must be parameterized by the ECC family and curve size, and then the KDF can determine how to compute the key value from the w0s||w1s input based on the output key attributes. Or
  2. The KDF must be parameterized by the ECC family and curve size, and this information is stored as part of the key data, for validation that the key is valid for use with a particular PAKE operation.

After some further thinking about it, parameterizing the key type (option 1) makes the most sense, and also fist well with the idea of providing a higher level registration function to derive the private and public keys from the passcode.

@athoelke
Copy link
Copy Markdown
Contributor

The sheer amount of function calls that will be needed in order to put the registration together with the actual interactive key agreement is quite significant. The computational overhead would at least probably be small compared to the time it takes to run the PBKDF part.

and then...

I think that we can add a new API psa_pake_registration which takes in parameters such as the ECC family and curve size of the output key and the password, computes the PBKDF intermediate, does the point multiplication and finally stores w0||w1 in the key store. This will internally call the KDF APIs.

My initial thoughts of providing just a low-level building block (a KDF to convert w0s||w1s to w0||w1 and/or w0||L), were prompted by the lack of specificity in the SPAKE2+ definition:

  • The document leaves much of the conversion from a passcode/secret to the w0s||w1s string as an exercise for the application layer, and provides a suggested use of a PBKDF with inclusion of the identity strings in the derivation
  • There is no precise specification of how long the w0s and w0s strings should be, and a suggestion that some extra length can be valuable

In practice, a concrete PBKDF is required, with specific input parameters (for both the construction of the input keying material, the salt and any iteration count), and a specific length for the w0s||w1s output string.

Matter does define exactly how this part of the protocol must run, including the absence of identity-strings, and how the PBKDF2 parameters are shared and included in the transcript context.

If we define a higher-level API, such as psa_pake_registration(), that matches the Matter requirements, does this become a Matter-specific API? - or do we add enough parameters to support other application uses that do have identities, and generate different lengths of intermediate w0s||w1s output?

API element names

Should we name the API elements (algorithms in particular) using PSA_xxx_SPAKE2_PLUS, which has the risk of collision with the not-entirely-compatible later drafts of SPAKE2+?

Or qualify the name using a suffix such as PSA_xxx_SPAKE2_PLUS_DRAFT2 or PSA_xxx_SPAKE2_PLUS_V2?

Or just make this version of the API sufficient for Matter only (as that is the primary use case), and use PSA_xxx_SPAKE2_PLUS_MATTER or perhaps even PSA_xxx_MATTERV1_PAKE?

The best answer partly depends on whether we try to make the API more flexible for other uses of SPAKE2+, or more specific to the Matter application usage.

2. `W0||L` as secret key on Verifier side.
2. Rename `psa_pake_set_password_key()` as `psa_pake_input_key()` to input mutiple secret keys and define steps for different keys
1. For W0 and W1 on Prover side
2. For W0 and L on Verifier side
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please be careful here. A key is needed for driver dispatch. Connecting PAKE to a driver is already a complex task (see discussion here: Mbed-TLS/mbedtls#7445). If you add several variants with multiple keys driver dispatch may become impossible to handle.
For the same reason it would be advantageous to have a clean sequence of calls:

  1. psa_pake_setup
  2. psa_pake_set_role/psa_pake_set_user/psa_pake_set_peer
  3. psa_pake_set_password_key
  4. psa_pake_input/psa_pake_output


Expected PAKE API Flow :
------------------------------------
![](SPAKE2PLUS.svg)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handling Y (= shareV) and cB (= confirmP) in two separate input/output passes with different step types (PSA_PAKE_STEP_KEY_SHARE and PSA_PAKE_STEP_CONFIRM) would be cleaner and simplify usage in cases where the two are transferred in separate protocol elements. Packing them together in a single package is still straight forward by calling both inputs or outputs immediately after each other.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handling Y (= shareV) and cB (= confirmP) in two separate input/output passes with different step types (PSA_PAKE_STEP_KEY_SHARE and PSA_PAKE_STEP_CONFIRM) would be cleaner and simplify usage in cases where the two are transferred in separate protocol elements. Packing them together in a single package is still straight forward by calling both inputs or outputs immediately after each other.

Agreed. I think this diagram copies the style of the J-PAKE sequence diagram - which does not explicitly show every call to psa_pake_input() or psa_pake_output() when multiple parameters are input or output from the operation.

But the associated text should specify that these values are input and output individually, and describe any ordering requirements.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will update the flow description to include this

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added an example Matter implementation which demonstrates the flow of the PAKE API.

@athoelke
Copy link
Copy Markdown
Contributor

athoelke commented Jul 20, 2023

While comparing draft-2 and draft-8 for SPAKE2+, I've also noticed that neither the Matter specification, nor the draft-2 for SPAKE2+ specify the length of the confirmation keys KcA and KcB, that are derived during the KDF step, and used in the final MAC to compute the confirmation values.

Matter does specify that the final confirmation values are computed using HMAC-SHA256, with keys KcA and KcB.

Draft-8 of SPAKE2+ (not referenced by Matter) adds requirements and recommendations for the confirmation key sizes: in the case of HMAC, it recommends a confirmation key size equal to the output of the hash function. Unless there is clarification elsewhere, this seems a reasonable GUESS as to what Matter implementations are expected to do?

@athoelke
Copy link
Copy Markdown
Contributor

athoelke commented Aug 7, 2023

I've picked up previously raised issues with the PAKE API (#86 #87 #88 #89), and in response to #86 (and the related concern with the key derivation API #85) have proposed an API to extract the shared secret from the PAKE operation as a derivation key object.

With an API like that, it might be possible to propose a different approach to SPAKE2+ (see #73), where the PAKE operation finishes with the output of K_main as the shared secret. The caller is able to implement the key schedule and confirmation process without any further use of the group operations or other secret data - the data used to generate the confirmation values are the key shares that are sent in the initial SPAKE2+ messages.

As the primary differences between draft 2 and draft 8 of SPAKE2+ are in the key schedule, this would also move the SPAKE2+ version issue into the application space, and outside of the PAKE operation implementation.

@athoelke
Copy link
Copy Markdown
Contributor

athoelke commented Aug 7, 2023

With an API like that, it might be possible to propose a different approach to SPAKE2+ (see #73), where the PAKE operation finishes with the output of K_main as the shared secret. The caller is able to implement the key schedule and confirmation process without any further use of the group operations or other secret data - the data used to generate the confirmation values are the key shares that are sent in the initial SPAKE2+ messages.

Reviewing the draft08 and draft02 for SPAKE2+ shows that this is not adequate. Draft02 splits the transcript hash, called K_main in draft08, into two keys, Ka and Ke.

So we would need to be able to extract the shared secret from SPAKE2+ sequentially into multiple keys, in a similar way to being able to output multiple keys from a key derivation operation. So for Matter-v1/SPAKE2+-draft02 we would want to be able to do something like this:

psa_key_id_t Ka, KcA, KcB, Ke;
psa_key_attributes_t att;
psa_key_derivation_operation_t kdf;
const uint8_t kdf_info[16] = "ConfirmationKeys"

// attributes for Ka (SPAKE2+ draft02 required by Matter)
att = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&att, PSA_KEY_TYPE_DERIVE);
psa_set_key_bits(&att, PSA_HASH_LENGTH(PSA_ALG_SHA256)*8/2);
psa_set_key_usage_flags(&att, PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&att, PSA_ALG_HKDF(PSA_ALG_SHA256));
psa_pake_shared_secret(&pake, &att, &Ka);

// Derive KcA/KcB from Ka
kdf = PSA_KEY_DERIVATION_OPERATION_INIT;
psa_key_derivation_setup(&kdf, PSA_ALG_HKDF(PSA_ALG_SHA256));
psa_key_derivation_input_key(&kdf, PSA_KEY_DERIVATION_INPUT_SECRET, Ka);
psa_key_derivation_input_bytes(&kdf, PSA_KEY_DERIVATION_INPUT_INFO, &kdf_info, sizeof(kdf_info));
// attributes for KcA and KcB (assume length is half of basic HKDF output - same as HASH output)
att = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&att, PSA_KEY_TYPE_HMAC);
psa_set_key_bits(&att, PSA_HASH_LENGTH(PSA_ALG_SHA256)*8/2);
psa_set_key_usage_flags(&att, PSA_KEY_USAGE_SIGN);
psa_set_key_algorithm(&att, PSA_ALG_HMAC(PSA_ALG_SHA256));
psa_key_derivation_output_key(&kdf, &att, &KcA);
psa_key_derivation_output_key(&kdf, &att, &KcB);
psa_key_derivation_abort(&kdf);
psa_destroy_key(Ka);

// Now compute MACs, exchange, compare and confirm...

psa_destroy_key(KcA);
psa_destroy_key(KcB);

// Finally, extract Ke from PAKE

// attributes for Ke (in this example Ke is used derive session keys using HKDF)
att = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&att, PSA_KEY_TYPE_DERIVE);
psa_set_key_bits(&att, PSA_HASH_LENGTH(PSA_ALG_SHA256)*8/2);
psa_set_key_usage_flags(&att, PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&att, PSA_ALG_HKDF(PSA_ALG_SHA256));
psa_pake_shared_secret(&pake, &att, &Ke);
pas_pake_abort(&pake);

Kusumit Ghoderao added 2 commits September 1, 2023 12:03
Signed-off-by: Kusumit Ghoderao <Kusumit.Ghoder@silabs.com>
Signed-off-by: Kusumit Ghoderao <Kusumit.Ghoder@silabs.com>
@silabs-Kusumit
Copy link
Copy Markdown
Author

Thanks for the feedback on the key-pair idea, this does seem to be a valid approach for SPAKE2+.

I have added the key pair description to the proposal.

@athoelke
Copy link
Copy Markdown
Contributor

athoelke commented Sep 7, 2023

I had a slightly different idea in mind for the flow from the PBKDF through the SPAKE2+ key pair and into the pake operation. In particular I wanted to separate the registration flow from the pake operation flow.

It seems that are several use case flows with an augmented PAKE, that justify defining a specific key type for SPAKE2+ in the API. For example:

  1. passcode entered into both devices separately at time of protocol execution:

    a. Prover processes passcode and initiates protocol:

    sk = Spake2P_derive_key_pair(PBKDF2(passcode))
    Spake2P_prover_start(sk)
    

    b. Verifier processes passcode and initiates protocol:

    pk = Spake2P_derive_public_key(PBKDF2(passcode))
    Spake2P_verifier_start(pk)
    
  2. Like Matter: Verifier key provisioned when Verifier is commissioned, user enters passcode into new device at registration:

    a. One-off process. Verifier processes passcode and stores verifier key:

    pk = Spake2P_derive_public_key(PBKDF2(passcode))
    Save(pk)
    

    b. Prover processes passcode and initiates protocol:

    sk = Spake2P_derive_key_pair(PBKDF2(passcode))
    Spake2P_prover_start(sk)
    

    c. Verifier retrieves pk and initiates protocol:

    Load(pk)
    Spake2P_verifier_start(pk)
    
  3. At registration, Prover processes user passcode and supplies verifier key to Verifier, which is stored for future use

    a. One-off process. Prover processes passcode, stores prover key, and forwards verifier key to Verifier:

    sk = Spake2P_derive_key_pair(PBKDF2(passcode))
    Save(sk)
    pkdata = Export_public_key(sk)
    Send_to_verifier(pkdata)
    

    b. One-off process. Verifier saves verifier key:

    pkdata = Receive_from_Prover()
    pk = Import_public_key(pkdata)
    Save(pk)
    

    c. On future connections, prover retrieves sk (or regenerates from user entered passcode) and initiates protocol:

    sk = Load(sk)
    Spake2P_prover_start(sk)
    

    d. Verifier retrieves pk and initiates protocol:

    Load(pk)
    Spake2P_verifier_start(pk)
    

In use case (2), we shouldn't store the passcode in the Verifier, when we can store the derived w0||L 'verifier key' instead, from which it is not possible to reasonably determine the passcode. In use case (3), the Verifier never has access to the passcode, and only ever handles the verifier key.

To enable those use cases, we need an API that exposes the verifier key. An API that just transfers the PBKDF2 output into a PAKE operation is not sufficient.

Defining w0||w1 and w0||L as a key pair, with the obvious key-pair to public-key and public-key export and import operations fits these use cases well. (In the key store, the key-pair could be stored as w0||w1, as L can be computed from w1.)

So assume we will define a key pair for SPAKE2+, perhaps:

#define PSA_KEY_TYPE_SPAKE2P_KEY_PAIR
#define PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY

These do need some parameterization (e.g. on the ECC curve?)? Or use the pake_primitive as the input to the KEY_PAIR/PUBLIC_KEY macros for consistency with other parts of PAKE?

I think that the parameterization of the key type is necessary as the export format for a SPAKE2+ public key depends on the pake primitive.

We will need some APIs to support the Spake2P_derive_public_key() and Spake2P_derive_key_pair() operations that output keys.

This operation needs some bytes from the PBKDF (the number depends on the PAKE algorithm and chosen cipher-suite), and generates an appropriate key for use with the pake operation. There are various options for how we present this in the API:

  1. This operation has similar properties to other key derivations, so we could define that psa_key_derivation_output_key(), when provided key attributes for a SPAKE2+ key-pair, will implement the SPAKE2+ algorithm for converting w0s||w1s into w0||w1 (the prover key tuple). If provided attributes for a SPAKE2+ public-key, the output key is w0||L instead. The number of bytes of the KDF used to create the key is an implementation detail (and would follow the spec).

    This approach requires that all important information can be conveyed in the key attributes (the pake algorithm, and the relevant cipher-suite details)

  2. Instead of 'hiding' the SPAKE2+ key conversion logic in psa_key_derivation_output_key(), we could provide a standalone psa_pake_registration() function that takes the pbkdf2 output and generates the key pair. There are various options for transferring the pbkdf output:

    • The function could accept the kdf operation and extract the appropriate number of bytes within the cryptoprocessor, to make the SPAKE2+ key
    • The function could accept a byte array, and the application has to extract the [right amount of] data from the kdf operation
    • The function could accept a key id, and the application has to extract the [right amount of] kdf data into a temporary KEY_TYPE_DERIVE key

    If we want to enable SPAKE2+ registration to be done with data from a key derivation algorithm that is not available via the Crypto API, then we would need at least one of the last two options. If we don't envisage this, then we can reduce application complexity by only considering the first one.

    For this approach, the PAKE algorithm and cipher-suite details could either be provided in the key attributes, or be provided as explicit parameters to the function? We cannot really dispose with the attributes altogether, as different use cases for SPAKE2+ might want a persistent key or a volatile key.

In keeping with the style of the PSA Crypto API, I am inclined to go with (1), and use psa_key_derivation_output_key() for this, and not require a new API.

The PAKE operation already has a suitable API for inputing the prover or verifier key, psa_pake_set_password_key(). For SPAKE2+, we can then require that this is a PSA_KEY_TYPE_SPAKE2P_XXX key as appropriate for the role.

A remaining question is whether there is value in simplifying the application code when passcode derivation is done at the same time as the PAKE protocol - by retaining the ability to transfer the pbkdf2 output directly into the PAKE, and avoiding the creation and destruction of a temporary key.

@athoelke
Copy link
Copy Markdown
Contributor

athoelke commented Sep 8, 2023

With an API like that, it might be possible to propose a different approach to SPAKE2+ (see #73), where the PAKE operation finishes with the output of K_main as the shared secret. The caller is able to implement the key schedule and confirmation process without any further use of the group operations or other secret data - the data used to generate the confirmation values are the key shares that are sent in the initial SPAKE2+ messages.

Reviewing the draft08 and draft02 for SPAKE2+ shows that this is not adequate. Draft02 splits the transcript hash, called K_main in draft08, into two keys, Ka and Ke.

So we would need to be able to extract the shared secret from SPAKE2+ sequentially into multiple keys, in a similar way to being able to output multiple keys from a key derivation operation. So for Matter-v1/SPAKE2+-draft02 we would want to be able to do something like this:

Although I like the fact that this approach manages to avoid the draft02 vs draft02-as-used-in-Matter vs draft08 concerns, the result leaves a lot of work to the application code. I am not entirely happy with this, so I think the proposed approach with output and input of confirmation values followed by extraction of the explicit/confirmed key is better.

In this case we do have a algorithm version issue, due to the differences in the confirmation protocol, and the optionality in the original draft02 specification. To address this, I think we need to define two algorithm values (although the key types can be shared):

  • PSA_ALG_SPAKE2P: This implements the latest version of the RFC draft (draft08), which I suspect is now stable. A MAC algorithm is required in the cipher-suite.
  • PSA_ALG_SPAKE2P_MATTER: This implements SPAKE2+-draft02 as parameterized by Matter-v1. In particular, it only needs to support a cipher-suite that specifies the EC curve, hash and MAC algorithms defined by Matter.

As for all other algorithms, an implementation can support neither, either, or both of these algorithms.

I do not think it is necessary, or useful to define a PSA_ALG_SPAKE2P_DRAFT2 for a general implementation of draft02.

Copy link
Copy Markdown
Contributor

@athoelke athoelke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've posted some more detailed thoughts on the key types and the API design to separate the registration and exchange flows.

### Registration

Computing these values is considered Out of Scope for PAKE API as this a registration phase which could happen out of actual protocol flow.
Propose a new API `psa_pake_registration()` which will take as input:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my longer comment in the conversation here.

I think this functionality should be provided with psa_key_derivation_output_key() when a SPAKE2+ key type is requested, followed by a call to psa_pake_set_password_key() to input that key into the pake protocol operation.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will update the proposal to include this suggestion and also update the Matter example with the same.

Key confirmation is part of the SPAKE2+ protocol. Current PSA Cryptography API 1.1 PAKE Extension only supports implicit key confirmation.

* New API `psa_pake_get_explicit_key()` is required to provide keys with explicit key confirmation. No newline at end of file
* New API `psa_pake_get_explicit_key()` which will take as input the current PAKE operation and the attributes for the explicit key and return the key id of the explicit key.
Copy link
Copy Markdown
Contributor

@athoelke athoelke Sep 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder about the naming of this API, and by extension, the existing psa_pake_get_implicit_key().

I think the API would be clearer if we called this psa_pake_get_confirmed_key() or psa_pake_output_confirmed_key()? (I like 'output' as this matches the key derivation operation, but I can see that we inject the password with a 'set' function.

As per #86, we should consider whether we want both a 'extract to key' API (which could extract only part of the output?) and a 'inject shared secret into a KDF' API (like the existing psa_pake_get_implicit_key() function).

This is both a big enough sub-topic, with implications for the existing beta API: I will raise a separate issue to have that discussion. This is now #100

psa_pake_get_explicit_key(psa_pake_operation_t *pake, psa_key_attributes_t *attributes, psa_key_id_t *explicit_key);
```

### PSA PAKE API Flow for SPAKE2+ with Matter as example
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 This is helpful to have here (and probably in the spec in some form) is helpful to think through the API design issues.

Signed-off-by: Kusumit Ghoderao <Kusumit.Ghoder@silabs.com>
@athoelke
Copy link
Copy Markdown
Contributor

I believe that this PR has now served its purpose.

I propose to close this PR without merging. However, if it would be valuable to preserve the RFC document within the main branch of the repository we can consider where such design documents could be stored?

@athoelke
Copy link
Copy Markdown
Contributor

Closing this now, as the proposed changes have all been implemented in the specification.

We can still pull the RFC document into the repo at a future date if this would be useful to keep.

@athoelke athoelke closed this Jan 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Crypto API Issue or PR related to the Cryptography API proposal An RFC, or proposal for discussion

Projects

Development

Successfully merging this pull request may close these issues.

Support for SPAKE2+ in the Crypto PAKE API

4 participants