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

Enable Rfc2898DeriveBytes on Browser WASM #71768

Merged
merged 4 commits into from
Jul 9, 2022
Merged

Conversation

eerhardt
Copy link
Member

@eerhardt eerhardt commented Jul 7, 2022

Marks the APIs as supported on Browser, and enables Rfc2898 tests on Browser WASM.
Use SubtleCrypto deriveBits API to implement one shot Pbkdf2.

Also, make HKDF supported on Browser.

Contributes to #40074

@dotnet-issue-labeler
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost
Copy link

ghost commented Jul 7, 2022

Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

Marks the APIs as supported on Browser, and enables Rfc2898 tests on Browser WASM.
Use SubtleCrypto deriveBits API to implement one shot Pbkdf2.

Contributes to #40074

(In draft until #71693 is merged, since this is built on top of that PR.)

Author: eerhardt
Assignees: eerhardt
Labels:

area-System.Security, new-api-needs-documentation

Milestone: -

@teo-tsirpanis teo-tsirpanis added the arch-wasm WebAssembly architecture label Jul 7, 2022
@ghost
Copy link

ghost commented Jul 7, 2022

Tagging subscribers to 'arch-wasm': @lewing
See info in area-owners.md if you want to be subscribed.

Issue Details

Marks the APIs as supported on Browser, and enables Rfc2898 tests on Browser WASM.
Use SubtleCrypto deriveBits API to implement one shot Pbkdf2.

Contributes to #40074

(In draft until #71693 is merged, since this is built on top of that PR.)

Author: eerhardt
Assignees: eerhardt
Labels:

arch-wasm, area-System.Security, new-api-needs-documentation

Milestone: -

Marks the APIs as supported on Browser, and enables Rfc2898 tests on Browser WASM.
Use SubtleCrypto deriveBits API to implement one shot Pbkdf2.

Contributes to dotnet#40074
{
using (Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(
password.ToArray(),
salt.ToArray(),
Copy link
Member

Choose a reason for hiding this comment

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

There's no way to stay in the span universe all the way down? Are we accepting arrays from public entrypoints, passing them around as span, and then ToArray'ing here?

Copy link
Member

@vcsjones vcsjones Jul 7, 2022

Choose a reason for hiding this comment

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

It's possible but we've avoided optimizing the non-one-shot implementation of PBKDF2 when we've talked about it in the past.

Copy link
Member Author

Choose a reason for hiding this comment

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

Note that this is the way the existing "mobile" platforms are implementing this functionality:

using (Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(
password.ToArray(),
salt.ToArray(),
iterations,
hashAlgorithmName,
clearPassword: true))
{
byte[] result = deriveBytes.GetBytes(destination.Length);
result.AsSpan().CopyTo(destination);
}

which is used by Android and non-macOS AppleCrypto (iOS, tvOS, etc.).

There's no way to stay in the span universe all the way down?

Not without some decent refactoring to Rfc2898DeriveBytes in order to make the managed algorithm completely "one shot", and then build the instance based Rfc2898DeriveBytes methods on top of it.

Are we accepting arrays from public entrypoints, passing them around as span, and then ToArray'ing here?

In one overload, yes, and in most overloads no. This method is called from these public end-points:

public static byte[] Pbkdf2(byte[] password, byte[] salt, int iterations, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, int outputLength) { throw null; }
public static byte[] Pbkdf2(System.ReadOnlySpan<byte> password, System.ReadOnlySpan<byte> salt, int iterations, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, int outputLength) { throw null; }
public static void Pbkdf2(System.ReadOnlySpan<byte> password, System.ReadOnlySpan<byte> salt, System.Span<byte> destination, int iterations, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public static byte[] Pbkdf2(System.ReadOnlySpan<char> password, System.ReadOnlySpan<byte> salt, int iterations, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, int outputLength) { throw null; }
public static void Pbkdf2(System.ReadOnlySpan<char> password, System.ReadOnlySpan<byte> salt, System.Span<byte> destination, int iterations, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public static byte[] Pbkdf2(string password, byte[] salt, int iterations, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, int outputLength) { throw null; }

Only the top overload is taking 2 byte[]s, passing them through as spans, and then ToArray'ing here. The bottom overload takes a string, which turns into UTF8 bytes in either a rented array or stackalloc'd span, and then passed in here.

Copy link
Member

Choose a reason for hiding this comment

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

I think it would be simpler to implement a one shot in managed code with IncrementalHash instead of trying to get the streaming PBKDF2 to allocate less. I have a branch with this. Happy to upstream it as a follow up to this.

Copy link
Member Author

Choose a reason for hiding this comment

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

I have a branch with this. Happy to upstream it as a follow up to this.

I think that would be great!

@eerhardt eerhardt marked this pull request as ready for review July 7, 2022 23:17
@eerhardt eerhardt requested a review from lewing as a code owner July 7, 2022 23:17
@@ -256,6 +256,24 @@ async function sign(type, key, data) {
return Array.from(new Uint8Array(signResult));
}

async function derive_bits(password, salt, iterations, hashAlgorithm, lengthInBytes) {
Copy link
Member

Choose a reason for hiding this comment

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

It feels like this function should either a) take PBKDF2 as a parameter, or b) include it in the name; since it's not applying a general/generic KDF, but a specific one.

Copy link
Member Author

Choose a reason for hiding this comment

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

The approach I've been taking (for sign - HMAC - and encrypt_decrypt - AES-CBC) is to name the methods the same as the SubtleCrypto APIs, and then taking whatever parameters it needs from the managed code for now. And when we need to add more parameters/options in the future, we would add the parameters/refactor then.

This way it keeps it simple for now, and if we don't need to add anymore in the future it keeps it simple then too.

Adding the parameters now doesn't guarantee that other params can be used in the future without adding more parameters and needing to refactor then as well. For example, if we wanted to add ECDH, we would need to pass a public key as well.

At least that was my reasoning. Thoughts? I can change this approach to one of the above, if you want.

Copy link
Member

Choose a reason for hiding this comment

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

Hm. Since the calling code and the acting code get delivered together I guess the signature is allowed to be modified as needed, so... I suppose it works.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, if these APIs were public or somehow permanent, I would take a much different approach.

@bartonjs
Copy link
Member

bartonjs commented Jul 7, 2022

LGTM, other than the concern about the naming/signature of derive_bits.

@eerhardt
Copy link
Member Author

eerhardt commented Jul 8, 2022

/azp run runtime-wasm

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@eerhardt
Copy link
Member Author

eerhardt commented Jul 8, 2022

AOT wasm failures are #71838.

@eerhardt eerhardt merged commit 8f75cc9 into dotnet:main Jul 9, 2022
@eerhardt eerhardt deleted the Pbkdf2 branch July 9, 2022 02:51
@ghost ghost locked as resolved and limited conversation to collaborators Aug 8, 2022
@bartonjs bartonjs added the needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration label Aug 26, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
arch-wasm WebAssembly architecture area-System.Security needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration new-api-needs-documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants