Skip to content

Commit 4222e69

Browse files
authored
Use crypto.subtle for AES on Browser WASM (#71501)
* Use crypto.subtle for AES on Browser WASM Implement the browser "native" portion for AES on Browser WASM. There are two issues to solve .NET's Aes API on crypto.subtle: 1. The .NET API supports streaming while crypto.subtle only supports "one shot" APIs. 2. The .NET API supports multiple padding modes while crypto.subtle only supports PKCS7. To solve these issues, we use the following approach: 1. We only invoke crypto.subtle with complete AES "blocks" of data. This allows us to make assumptions about the padding behavior. 2. To implement streaming, remember the last block of the previous cipher text to use as the IV for the next stream of data. 3. When encrypting, since we have a complete block of data and crypto.subtle uses PKCS7 padding, strip off the last block of cipher text which will always be a full block of padding. 4. When decrypting do the inverse of encrypting - append an encrypted block of padding to the cipher text so crypto.subtle will return the full message as plain text. Other changes: - Make a few refactoring / simplifications where necessary. - SubtleCrypto doesn't support 192 bit AES keys, so no longer support AES-192 on Browser. Contributes to #40074 * Use an empty array to create encrypted padding block.
1 parent 0e5fcea commit 4222e69

File tree

21 files changed

+406
-55
lines changed

21 files changed

+406
-55
lines changed

src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.Sign.cs

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ internal enum SimpleDigest
1818
Sha512,
1919
};
2020

21+
internal static readonly bool CanUseSubtleCrypto = CanUseSubtleCryptoImpl() == 1;
22+
2123
[LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_CanUseSubtleCryptoImpl")]
22-
internal static partial int CanUseSubtleCryptoImpl();
24+
private static partial int CanUseSubtleCryptoImpl();
2325

2426
[LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_SimpleDigestHash")]
2527
internal static unsafe partial int SimpleDigestHash(
@@ -28,5 +30,27 @@ internal static unsafe partial int SimpleDigestHash(
2830
int input_len,
2931
byte* output_buffer,
3032
int output_len);
33+
34+
[LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_Sign")]
35+
internal static unsafe partial int Sign(
36+
SimpleDigest hashAlgorithm,
37+
byte* key_buffer,
38+
int key_len,
39+
byte* input_buffer,
40+
int input_len,
41+
byte* output_buffer,
42+
int output_len);
43+
44+
[LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_EncryptDecrypt")]
45+
internal static unsafe partial int EncryptDecrypt(
46+
int encrypting,
47+
byte* key_buffer,
48+
int key_len,
49+
byte* iv_buffer,
50+
int iv_len,
51+
byte* input_buffer,
52+
int input_len,
53+
byte* output_buffer,
54+
int output_len);
3155
}
3256
}

src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public static void RandomKeyRoundtrip_128()
3434
}
3535

3636
[Fact]
37+
[SkipOnPlatform(TestPlatforms.Browser, "AES-192 is not supported on Browser")]
3738
public static void RandomKeyRoundtrip_192()
3839
{
3940
using (Aes aes = AesFactory.Create())
@@ -485,6 +486,7 @@ public static void VerifyKnownTransform_CFB128_128_NoPadding_3()
485486
}
486487

487488
[Fact]
489+
[SkipOnPlatform(TestPlatforms.Browser, "AES-192 is not supported on Browser")]
488490
public static void VerifyKnownTransform_CBC192_NoPadding()
489491
{
490492
TestAesTransformDirectKey(
@@ -525,6 +527,7 @@ public static void VerifyKnownTransform_CFB8_192_NoPadding()
525527
}
526528

527529
[Fact]
530+
[SkipOnPlatform(TestPlatforms.Browser, "AES-192 is not supported on Browser")]
528531
public static void VerifyKnownTransform_CBC192_NoPadding_2()
529532
{
530533
TestAesTransformDirectKey(

src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ public static void LegalKeySizes()
5555

5656
Assert.Equal(128, keySizeLimits.MinSize);
5757
Assert.Equal(256, keySizeLimits.MaxSize);
58-
Assert.Equal(64, keySizeLimits.SkipSize);
58+
59+
// Browser's SubtleCrypto doesn't support AES-192
60+
int expectedKeySkipSize = PlatformDetection.IsBrowser ? 128 : 64;
61+
Assert.Equal(expectedKeySkipSize, keySizeLimits.SkipSize);
5962
}
6063
}
6164

@@ -214,6 +217,7 @@ public static void VerifyKeyGeneration_128()
214217
}
215218

216219
[Fact]
220+
[SkipOnPlatform(TestPlatforms.Browser, "AES-192 is not supported on Browser")]
217221
public static void VerifyKeyGeneration_192()
218222
{
219223
using (Aes aes = AesFactory.Create())

src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -541,14 +541,13 @@
541541
Link="Common\Interop\Browser\Interop.Libraries.cs" />
542542
<Compile Include="$(CommonPath)System\Sha1ForNonSecretPurposes.cs"
543543
Link="Common\System\Sha1ForNonSecretPurposes.cs" />
544-
<Compile Include="$(CommonPath)Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.SimpleDigestHash.cs"
545-
Link="Common\Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.SimpleDigestHash.cs" />
546-
<Compile Include="$(CommonPath)Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.Sign.cs"
547-
Link="Common\Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.Sign.cs" />
544+
<Compile Include="$(CommonPath)Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.SubtleCrypto.cs"
545+
Link="Common\Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.SubtleCrypto.cs" />
548546
<Compile Include="System\Security\Cryptography\AesCcm.NotSupported.cs" />
549547
<Compile Include="System\Security\Cryptography\AesGcm.NotSupported.cs" />
550548
<Compile Include="System\Security\Cryptography\AesImplementation.Browser.cs" />
551549
<Compile Include="System\Security\Cryptography\AesManagedTransform.Browser.cs" />
550+
<Compile Include="System\Security\Cryptography\AesSubtleCryptoTransform.Browser.cs" />
552551
<Compile Include="System\Security\Cryptography\AsnFormatter.Managed.cs" />
553552
<Compile Include="System\Security\Cryptography\CapiHelper.Browser.cs" />
554553
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.NotSupported.cs" />
@@ -583,6 +582,9 @@
583582
<Compile Include="System\Security\Cryptography\X509Certificates\StorePal.NotSupported.cs" />
584583
<Compile Include="System\Security\Cryptography\X509Certificates\X509Pal.NotSupported.cs" />
585584
</ItemGroup>
585+
<ItemGroup Condition="'$(TargetPlatformIdentifier)' != 'Browser'">
586+
<Compile Include="System\Security\Cryptography\AesImplementation.NonBrowser.cs" />
587+
</ItemGroup>
586588
<ItemGroup Condition="'$(NeedOpenSslInitializer)' == 'true'">
587589
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
588590
Link="Common\Interop\Unix\Interop.Libraries.cs" />

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Aes.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public abstract class Aes : SymmetricAlgorithm
1111
protected Aes()
1212
{
1313
LegalBlockSizesValue = s_legalBlockSizes.CloneKeySizesArray();
14-
LegalKeySizesValue = s_legalKeySizes.CloneKeySizesArray();
14+
LegalKeySizesValue = AesImplementation.s_legalKeySizes.CloneKeySizesArray();
1515

1616
BlockSizeValue = 128;
1717
FeedbackSizeValue = 8;
@@ -31,6 +31,5 @@ protected Aes()
3131
}
3232

3333
private static readonly KeySizes[] s_legalBlockSizes = { new KeySizes(128, 128, 0) };
34-
private static readonly KeySizes[] s_legalKeySizes = { new KeySizes(128, 256, 64) };
3534
}
3635
}

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.Browser.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5-
using Internal.Cryptography;
65

76
namespace System.Security.Cryptography
87
{
98
internal sealed partial class AesImplementation
109
{
10+
internal const int BlockSizeBytes = 16; // 128 bits
11+
12+
// SubtleCrypto doesn't support AES-192. http://crbug.com/533699
13+
internal static readonly KeySizes[] s_legalKeySizes = { new KeySizes(128, 256, 128) };
14+
1115
private static UniversalCryptoTransform CreateTransformCore(
1216
CipherMode cipherMode,
1317
PaddingMode paddingMode,
@@ -19,11 +23,17 @@ private static UniversalCryptoTransform CreateTransformCore(
1923
bool encrypting)
2024
{
2125
ValidateCipherMode(cipherMode);
26+
if (iv is null)
27+
throw new CryptographicException(SR.Cryptography_MissingIV);
2228

23-
Debug.Assert(blockSize == AesManagedTransform.BlockSizeBytes);
29+
Debug.Assert(blockSize == BlockSizeBytes);
2430
Debug.Assert(paddingSize == blockSize);
2531

26-
return UniversalCryptoTransform.Create(paddingMode, new AesManagedTransform(key, iv, encrypting), encrypting);
32+
BasicSymmetricCipher cipher = Interop.BrowserCrypto.CanUseSubtleCrypto ?
33+
new AesSubtleCryptoTransform(key, iv, encrypting) :
34+
new AesManagedTransform(key, iv, encrypting);
35+
36+
return UniversalCryptoTransform.Create(paddingMode, cipher, encrypting);
2737
}
2838

2939
private static ILiteSymmetricCipher CreateLiteCipher(
@@ -37,10 +47,12 @@ private static ILiteSymmetricCipher CreateLiteCipher(
3747
{
3848
ValidateCipherMode(cipherMode);
3949

40-
Debug.Assert(blockSize == AesManagedTransform.BlockSizeBytes);
50+
Debug.Assert(blockSize == BlockSizeBytes);
4151
Debug.Assert(paddingSize == blockSize);
4252

43-
return new AesManagedTransform(key, iv, encrypting);
53+
return Interop.BrowserCrypto.CanUseSubtleCrypto ?
54+
new AesSubtleCryptoTransform(key, iv, encrypting) :
55+
new AesManagedTransform(key, iv, encrypting);
4456
}
4557

4658
private static void ValidateCipherMode(CipherMode cipherMode)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace System.Security.Cryptography
5+
{
6+
internal sealed partial class AesImplementation
7+
{
8+
internal static readonly KeySizes[] s_legalKeySizes = { new KeySizes(128, 256, 64) };
9+
}
10+
}

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesManagedTransform.Browser.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace System.Security.Cryptography
99
{
1010
internal sealed class AesManagedTransform : BasicSymmetricCipher, ILiteSymmetricCipher
1111
{
12-
public const int BlockSizeBytes = 16; // 128 bits
12+
private const int BlockSizeBytes = AesImplementation.BlockSizeBytes;
1313
private const int BlockSizeInts = BlockSizeBytes / 4;
1414

1515
private readonly bool _encrypting;
@@ -30,13 +30,7 @@ public AesManagedTransform(ReadOnlySpan<byte> key,
3030
: base(iv: null, BlockSizeBytes, BlockSizeBytes)
3131
{
3232
Debug.Assert(BitConverter.IsLittleEndian, "The logic of casting Span<int> to Span<byte> below assumes little endian");
33-
34-
if (iv.IsEmpty)
35-
throw new CryptographicException(SR.Cryptography_MissingIV);
36-
37-
// we only support the standard AES block size
38-
if (iv.Length != BlockSizeBytes)
39-
throw new CryptographicException(SR.Cryptography_InvalidIVSize);
33+
Debug.Assert(iv.Length == BlockSizeBytes);
4034

4135
_encrypting = encrypting;
4236
_Nr = GetNumberOfRounds(key);
@@ -331,7 +325,7 @@ private static int GetNumberOfRounds(ReadOnlySpan<byte> key)
331325
return (BlockSizeBytes > key.Length ? BlockSizeBytes : key.Length) switch
332326
{
333327
16 => 10, // 128 bits
334-
24 => 12, // 192 bits
328+
// 24 => 12, // 192 bits is not supported by SubtleCrypto, so the managed implementation doesn't support it either
335329
32 => 14, // 256 bits
336330
_ => throw new CryptographicException(SR.Cryptography_InvalidKeySize)
337331
};

0 commit comments

Comments
 (0)