-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Enable AES encryption on Browser WASM #70915
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
Conversation
|
Note regarding the 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. |
|
Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones Issue DetailsThis ports the C# implementation of AES from https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/mscorlib/system/security/cryptography/rijndaelmanagedtransform.cs, marks the APIs as supported on Browser, and enables AES tests on Browser WASM. Contributes to #40074 The AES SubtleCrypto support will come in a separate PR. (While in draft, skip the first two commits - those are from a separate PR. Will be rebased out once that PR is merged.) It is easiest to review the first 4 commits separately. They copy the reference source, get it formatted and compiling.
|
...mon/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherOneShotTests.cs
Outdated
Show resolved
Hide resolved
...m.Security.Cryptography/src/System/Security/Cryptography/RijndaelManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
800ecd5 to
74ddd51
Compare
...m.Security.Cryptography/src/System/Security/Cryptography/RijndaelManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
...m.Security.Cryptography/src/System/Security/Cryptography/RijndaelManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
...m.Security.Cryptography/src/System/Security/Cryptography/RijndaelManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
...m.Security.Cryptography/src/System/Security/Cryptography/RijndaelManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
...m.Security.Cryptography/src/System/Security/Cryptography/RijndaelManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
...m.Security.Cryptography/src/System/Security/Cryptography/RijndaelManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
...m.Security.Cryptography/src/System/Security/Cryptography/RijndaelManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
...m.Security.Cryptography/src/System/Security/Cryptography/RijndaelManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
1. Add copyright 2. Remove unnecessary attributes 3. Mark class as internal 4. Remove FEATURE_CRYPTO define. Only support CipherMode.CBC and PaddingMode.PKCS7. 5. Remove Contracts and replace with Debug.Assert.
And hook it up to AesImplementation for Browser.
Mark Aes APIs as supported on Browser.
…upported on Browser
Remove unnecessary parameters and fields that aren't supported.
Okay. That makes sense. |
I just tested this out quickly, and it appears to work (on the encryption side). Here's roughly what it would look like to append 2 full blocks together: function getMessage() {
const message = "a".repeat(32);
const enc = new TextEncoder();
return enc.encode(message);
}
/*
Encrypt the plainText twice in two separate blocks.
*/
async function encryptMessage(key: CryptoKey) {
const plainText = getMessage();
iv = window.crypto.getRandomValues(new Uint8Array(16));
ciphertext = await window.crypto.subtle.encrypt(
{
name: "AES-CBC",
iv
},
key,
plainText
);
// since the message is a whole block and we are using PKCS7, we are guaranteed the whole last block is all padding (all values are `0x10`)
const ciphertextNoPadding = new Uint8Array(ciphertext).slice(0, ciphertext.byteLength - 16);
// store the last block of the encrypted message as the IV for the next block
const nextIv = ciphertextNoPadding.subarray(ciphertextNoPadding.length - 16, ciphertextNoPadding.length);
// encrypt the same message again
const ciphertext2 = await window.crypto.subtle.encrypt(
{
name: "AES-CBC",
iv: nextIv
},
key,
plainText
);
ciphertext = new ArrayBuffer(ciphertextNoPadding.byteLength + ciphertext2.byteLength);
const ciphertextBytes = new Uint8Array(ciphertext);
ciphertextBytes.set(ciphertextNoPadding);
ciphertextBytes.set(new Uint8Array(ciphertext2), ciphertextNoPadding.byteLength);
}
Update: I think I just thought of a way and am testing it now. When decrypting, we have the "last block" of cipher text that would have been used to encrypt the padding block. We can use that "last block" as the IV, and we already have the key. So all we need to do is encrypt a block of Update2: I got it working as described above, here is the rough code: async function decryptMessage(key: CryptoKey) {
const ciphertextBytes = new Uint8Array(ciphertext);
const firstBlock = ciphertextBytes.slice(0, 16);
const secondBlock = ciphertextBytes.slice(16, ciphertextBytes.length);
const fullPaddingBlock = new Uint8Array([0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,]);
const fullPaddingBlockEncrypted: ArrayBuffer = await window.crypto.subtle.encrypt(
{
name: "AES-CBC",
iv: firstBlock
},
key,
fullPaddingBlock
);
const firstBlockWithPadding = new Uint8Array(firstBlock.length + 16);
firstBlockWithPadding.set(firstBlock);
firstBlockWithPadding.set(new Uint8Array(fullPaddingBlockEncrypted).slice(0, 16), firstBlock.length);
const decrypted1: ArrayBuffer = await window.crypto.subtle.decrypt(
{
name: "AES-CBC",
iv
},
key,
firstBlockWithPadding
);
const decrypted2: ArrayBuffer = await window.crypto.subtle.decrypt(
{
name: "AES-CBC",
iv: firstBlock
},
key,
secondBlock
);
const decrypted = new Uint8Array(decrypted1.byteLength + decrypted2.byteLength);
decrypted.set(new Uint8Array(decrypted1));
decrypted.set(new Uint8Array(decrypted2), decrypted1.byteLength);
} |
Encrypt and Decrypt index arrays are constant values. Use CopyTo in more places.
Instead, reuse the UniversalCrypto infrastructure to do padding.
|
After chatting with @bartonjs, he instructed me on how to handle With this knowledge, I can completely simplify the managed implementation by cutting out the "depad buffer" and the padding logic. I believe this PR is ready for review. Once this goes in, I can send a new PR for the SubtleCrypto side of things. |
src/libraries/System.Security.Cryptography/src/Resources/Strings.resx
Outdated
Show resolved
Hide resolved
...ies/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.Apple.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricPadding.cs
Outdated
Show resolved
Hide resolved
- clean up unnecessary changes
...System.Security.Cryptography/src/System/Security/Cryptography/AesManagedTransform.Browser.cs
Show resolved
Hide resolved
...ibraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricAlgorithm.cs
Outdated
Show resolved
Hide resolved
...s/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs
Show resolved
Hide resolved
...ests/System/Security/Cryptography/AlgorithmImplementations/Symmetric/SymmetricOneShotBase.cs
Show resolved
Hide resolved
src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs
Show resolved
Hide resolved
src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs
Show resolved
Hide resolved
...libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.cs
Outdated
Show resolved
Hide resolved
...System.Security.Cryptography/src/System/Security/Cryptography/AesManagedTransform.Browser.cs
Show resolved
Hide resolved
...System.Security.Cryptography/src/System/Security/Cryptography/AesManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
...System.Security.Cryptography/src/System/Security/Cryptography/AesManagedTransform.Browser.cs
Outdated
Show resolved
Hide resolved
Remove UnsupportedOSPlatform("browser") from CipherMode and the SymmetricAlgorithm base class. These create too many false positives and aren't technically accurate since other implementations may support these on browser.
This ports the C# implementation of AES from https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/mscorlib/system/security/cryptography/rijndaelmanagedtransform.cs, marks the APIs as supported on Browser, and enables AES tests on Browser WASM.
Contributes to #40074
The AES SubtleCrypto support will come in a separate PR.
It is easiest to review the commits separately. I copied the reference source, get it formatted and compiling. Then enable new functionality and refactor in separate commits to make it easier to review.