Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.Sign.cs22
-rw-r--r--src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SubtleCrypto.cs (renamed from src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SimpleDigestHash.cs)26
-rw-r--r--src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs3
-rw-r--r--src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs6
-rw-r--r--src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj10
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Aes.cs3
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.Browser.cs22
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.NonBrowser.cs10
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesManagedTransform.Browser.cs12
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesSubtleCryptoTransform.Browser.cs169
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HMACHashProvider.Browser.Native.cs2
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Browser.cs10
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Native.cs2
-rw-r--r--src/libraries/System.Security.Cryptography/tests/AesTests.Browser.cs15
-rw-r--r--src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js1
-rw-r--r--src/mono/wasm/runtime/crypto-worker.ts29
-rw-r--r--src/mono/wasm/runtime/es6/dotnet.es6.lib.js1
-rw-r--r--src/mono/wasm/runtime/exports.ts4
-rw-r--r--src/mono/wasm/runtime/workers/dotnet-crypto-worker.js78
-rw-r--r--src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c25
-rw-r--r--src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h11
21 files changed, 406 insertions, 55 deletions
diff --git a/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.Sign.cs b/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.Sign.cs
deleted file mode 100644
index 2100b3f0530..00000000000
--- a/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.Sign.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-internal static partial class Interop
-{
- internal static partial class BrowserCrypto
- {
- [LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_Sign")]
- internal static unsafe partial int Sign(
- SimpleDigest hashAlgorithm,
- byte* key_buffer,
- int key_len,
- byte* input_buffer,
- int input_len,
- byte* output_buffer,
- int output_len);
- }
-}
diff --git a/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SimpleDigestHash.cs b/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SubtleCrypto.cs
index d664276a278..f0330945eda 100644
--- a/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SimpleDigestHash.cs
+++ b/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SubtleCrypto.cs
@@ -18,8 +18,10 @@ internal static partial class Interop
Sha512,
};
+ internal static readonly bool CanUseSubtleCrypto = CanUseSubtleCryptoImpl() == 1;
+
[LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_CanUseSubtleCryptoImpl")]
- internal static partial int CanUseSubtleCryptoImpl();
+ private static partial int CanUseSubtleCryptoImpl();
[LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_SimpleDigestHash")]
internal static unsafe partial int SimpleDigestHash(
@@ -28,5 +30,27 @@ internal static partial class Interop
int input_len,
byte* output_buffer,
int output_len);
+
+ [LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_Sign")]
+ internal static unsafe partial int Sign(
+ SimpleDigest hashAlgorithm,
+ byte* key_buffer,
+ int key_len,
+ byte* input_buffer,
+ int input_len,
+ byte* output_buffer,
+ int output_len);
+
+ [LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_EncryptDecrypt")]
+ internal static unsafe partial int EncryptDecrypt(
+ int encrypting,
+ byte* key_buffer,
+ int key_len,
+ byte* iv_buffer,
+ int iv_len,
+ byte* input_buffer,
+ int input_len,
+ byte* output_buffer,
+ int output_len);
}
}
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs
index 27ee7aec3c8..8bf77c7e73b 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs
@@ -34,6 +34,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests
}
[Fact]
+ [SkipOnPlatform(TestPlatforms.Browser, "AES-192 is not supported on Browser")]
public static void RandomKeyRoundtrip_192()
{
using (Aes aes = AesFactory.Create())
@@ -485,6 +486,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests
}
[Fact]
+ [SkipOnPlatform(TestPlatforms.Browser, "AES-192 is not supported on Browser")]
public static void VerifyKnownTransform_CBC192_NoPadding()
{
TestAesTransformDirectKey(
@@ -525,6 +527,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests
}
[Fact]
+ [SkipOnPlatform(TestPlatforms.Browser, "AES-192 is not supported on Browser")]
public static void VerifyKnownTransform_CBC192_NoPadding_2()
{
TestAesTransformDirectKey(
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs
index f2a63a2cbb2..dc8940d6b38 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs
@@ -55,7 +55,10 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests
Assert.Equal(128, keySizeLimits.MinSize);
Assert.Equal(256, keySizeLimits.MaxSize);
- Assert.Equal(64, keySizeLimits.SkipSize);
+
+ // Browser's SubtleCrypto doesn't support AES-192
+ int expectedKeySkipSize = PlatformDetection.IsBrowser ? 128 : 64;
+ Assert.Equal(expectedKeySkipSize, keySizeLimits.SkipSize);
}
}
@@ -214,6 +217,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests
}
[Fact]
+ [SkipOnPlatform(TestPlatforms.Browser, "AES-192 is not supported on Browser")]
public static void VerifyKeyGeneration_192()
{
using (Aes aes = AesFactory.Create())
diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
index 7821a8e03a5..cd48ee4e222 100644
--- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
+++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
@@ -541,14 +541,13 @@
Link="Common\Interop\Browser\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)System\Sha1ForNonSecretPurposes.cs"
Link="Common\System\Sha1ForNonSecretPurposes.cs" />
- <Compile Include="$(CommonPath)Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.SimpleDigestHash.cs"
- Link="Common\Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.SimpleDigestHash.cs" />
- <Compile Include="$(CommonPath)Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.Sign.cs"
- Link="Common\Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.Sign.cs" />
+ <Compile Include="$(CommonPath)Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.SubtleCrypto.cs"
+ Link="Common\Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.SubtleCrypto.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\AesImplementation.Browser.cs" />
<Compile Include="System\Security\Cryptography\AesManagedTransform.Browser.cs" />
+ <Compile Include="System\Security\Cryptography\AesSubtleCryptoTransform.Browser.cs" />
<Compile Include="System\Security\Cryptography\AsnFormatter.Managed.cs" />
<Compile Include="System\Security\Cryptography\CapiHelper.Browser.cs" />
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.NotSupported.cs" />
@@ -583,6 +582,9 @@
<Compile Include="System\Security\Cryptography\X509Certificates\StorePal.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\X509Pal.NotSupported.cs" />
</ItemGroup>
+ <ItemGroup Condition="'$(TargetPlatformIdentifier)' != 'Browser'">
+ <Compile Include="System\Security\Cryptography\AesImplementation.NonBrowser.cs" />
+ </ItemGroup>
<ItemGroup Condition="'$(NeedOpenSslInitializer)' == 'true'">
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
Link="Common\Interop\Unix\Interop.Libraries.cs" />
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Aes.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Aes.cs
index 57658ac9ad6..b28378d4362 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Aes.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Aes.cs
@@ -11,7 +11,7 @@ namespace System.Security.Cryptography
protected Aes()
{
LegalBlockSizesValue = s_legalBlockSizes.CloneKeySizesArray();
- LegalKeySizesValue = s_legalKeySizes.CloneKeySizesArray();
+ LegalKeySizesValue = AesImplementation.s_legalKeySizes.CloneKeySizesArray();
BlockSizeValue = 128;
FeedbackSizeValue = 8;
@@ -31,6 +31,5 @@ namespace System.Security.Cryptography
}
private static readonly KeySizes[] s_legalBlockSizes = { new KeySizes(128, 128, 0) };
- private static readonly KeySizes[] s_legalKeySizes = { new KeySizes(128, 256, 64) };
}
}
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.Browser.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.Browser.cs
index 5e3f9c4eb0e..3523b919f71 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.Browser.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.Browser.cs
@@ -2,12 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
-using Internal.Cryptography;
namespace System.Security.Cryptography
{
internal sealed partial class AesImplementation
{
+ internal const int BlockSizeBytes = 16; // 128 bits
+
+ // SubtleCrypto doesn't support AES-192. http://crbug.com/533699
+ internal static readonly KeySizes[] s_legalKeySizes = { new KeySizes(128, 256, 128) };
+
private static UniversalCryptoTransform CreateTransformCore(
CipherMode cipherMode,
PaddingMode paddingMode,
@@ -19,11 +23,17 @@ namespace System.Security.Cryptography
bool encrypting)
{
ValidateCipherMode(cipherMode);
+ if (iv is null)
+ throw new CryptographicException(SR.Cryptography_MissingIV);
- Debug.Assert(blockSize == AesManagedTransform.BlockSizeBytes);
+ Debug.Assert(blockSize == BlockSizeBytes);
Debug.Assert(paddingSize == blockSize);
- return UniversalCryptoTransform.Create(paddingMode, new AesManagedTransform(key, iv, encrypting), encrypting);
+ BasicSymmetricCipher cipher = Interop.BrowserCrypto.CanUseSubtleCrypto ?
+ new AesSubtleCryptoTransform(key, iv, encrypting) :
+ new AesManagedTransform(key, iv, encrypting);
+
+ return UniversalCryptoTransform.Create(paddingMode, cipher, encrypting);
}
private static ILiteSymmetricCipher CreateLiteCipher(
@@ -37,10 +47,12 @@ namespace System.Security.Cryptography
{
ValidateCipherMode(cipherMode);
- Debug.Assert(blockSize == AesManagedTransform.BlockSizeBytes);
+ Debug.Assert(blockSize == BlockSizeBytes);
Debug.Assert(paddingSize == blockSize);
- return new AesManagedTransform(key, iv, encrypting);
+ return Interop.BrowserCrypto.CanUseSubtleCrypto ?
+ new AesSubtleCryptoTransform(key, iv, encrypting) :
+ new AesManagedTransform(key, iv, encrypting);
}
private static void ValidateCipherMode(CipherMode cipherMode)
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.NonBrowser.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.NonBrowser.cs
new file mode 100644
index 00000000000..78b74ac82ab
--- /dev/null
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.NonBrowser.cs
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Security.Cryptography
+{
+ internal sealed partial class AesImplementation
+ {
+ internal static readonly KeySizes[] s_legalKeySizes = { new KeySizes(128, 256, 64) };
+ }
+}
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesManagedTransform.Browser.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesManagedTransform.Browser.cs
index 6354214dffb..0ae66acc18e 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesManagedTransform.Browser.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesManagedTransform.Browser.cs
@@ -9,7 +9,7 @@ namespace System.Security.Cryptography
{
internal sealed class AesManagedTransform : BasicSymmetricCipher, ILiteSymmetricCipher
{
- public const int BlockSizeBytes = 16; // 128 bits
+ private const int BlockSizeBytes = AesImplementation.BlockSizeBytes;
private const int BlockSizeInts = BlockSizeBytes / 4;
private readonly bool _encrypting;
@@ -30,13 +30,7 @@ namespace System.Security.Cryptography
: base(iv: null, BlockSizeBytes, BlockSizeBytes)
{
Debug.Assert(BitConverter.IsLittleEndian, "The logic of casting Span<int> to Span<byte> below assumes little endian");
-
- if (iv.IsEmpty)
- throw new CryptographicException(SR.Cryptography_MissingIV);
-
- // we only support the standard AES block size
- if (iv.Length != BlockSizeBytes)
- throw new CryptographicException(SR.Cryptography_InvalidIVSize);
+ Debug.Assert(iv.Length == BlockSizeBytes);
_encrypting = encrypting;
_Nr = GetNumberOfRounds(key);
@@ -331,7 +325,7 @@ namespace System.Security.Cryptography
return (BlockSizeBytes > key.Length ? BlockSizeBytes : key.Length) switch
{
16 => 10, // 128 bits
- 24 => 12, // 192 bits
+ // 24 => 12, // 192 bits is not supported by SubtleCrypto, so the managed implementation doesn't support it either
32 => 14, // 256 bits
_ => throw new CryptographicException(SR.Cryptography_InvalidKeySize)
};
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesSubtleCryptoTransform.Browser.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesSubtleCryptoTransform.Browser.cs
new file mode 100644
index 00000000000..5e0cfc68641
--- /dev/null
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesSubtleCryptoTransform.Browser.cs
@@ -0,0 +1,169 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using Internal.Cryptography;
+
+namespace System.Security.Cryptography
+{
+ internal sealed class AesSubtleCryptoTransform : BasicSymmetricCipher, ILiteSymmetricCipher
+ {
+ private const int BlockSizeBytes = AesImplementation.BlockSizeBytes;
+
+ private readonly bool _encrypting;
+
+ private readonly byte[] _key;
+ private byte[]? _lastBlockBuffer;
+
+ public AesSubtleCryptoTransform(byte[] key,
+ byte[] iv,
+ bool encrypting)
+ : base(iv, BlockSizeBytes, BlockSizeBytes)
+ {
+ _encrypting = encrypting;
+
+ // iv is guaranteed to be cloned before this method, but not key
+ _key = key.CloneByteArray();
+ }
+
+ public AesSubtleCryptoTransform(ReadOnlySpan<byte> key,
+ ReadOnlySpan<byte> iv,
+ bool encrypting)
+ : base(iv.ToArray(), BlockSizeBytes, BlockSizeBytes)
+ {
+ _encrypting = encrypting;
+
+ _key = key.ToArray();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // We need to always zeroize the following fields because they contain sensitive data
+ CryptographicOperations.ZeroMemory(_key);
+ CryptographicOperations.ZeroMemory(_lastBlockBuffer);
+ }
+
+ base.Dispose(disposing);
+ }
+
+ public override int Transform(ReadOnlySpan<byte> input, Span<byte> output) =>
+ Transform(input, output, isFinal: false);
+
+ public override int TransformFinal(ReadOnlySpan<byte> input, Span<byte> output)
+ {
+ int bytesWritten = Transform(input, output, isFinal: true);
+ Reset();
+ return bytesWritten;
+ }
+
+ private int Transform(ReadOnlySpan<byte> input, Span<byte> output, bool isFinal)
+ {
+ Debug.Assert(output.Length >= input.Length);
+ Debug.Assert(input.Length % BlockSizeInBytes == 0);
+
+ if (input.IsEmpty)
+ {
+ return 0;
+ }
+
+ // Note: SubtleCrypto always uses PKCS7 padding.
+
+ // In order to implement streaming on top of SubtleCrypto's "one shot" API, we have to do the following:
+ // 1. Remember the last block of cipher text to pass as the "IV" of the next block.
+ // 2. When encrypting a complete block, PKCS7 padding will always add one block of '0x10' padding bytes. We
+ // need to strip this padding block off in between Transform calls. This is done by Interop.BrowserCrypto.EncryptDecrypt.
+ // 3. When decrypting, we need to do the inverse: append an encrypted block of '0x10' padding bytes, so
+ // SubtleCrypto will decrypt input as a complete message. This is done by Interop.BrowserCrypto.EncryptDecrypt.
+
+ return _encrypting ?
+ EncryptBlock(input, output, isFinal) :
+ DecryptBlock(input, output, isFinal);
+ }
+
+ private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output, bool isFinal)
+ {
+ int bytesWritten = EncryptDecrypt(input, output);
+
+ if (!isFinal)
+ {
+ SaveLastBlock(output.Slice(0, bytesWritten));
+ }
+
+ return bytesWritten;
+ }
+
+ private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output, bool isFinal)
+ {
+ Span<byte> lastInputBlockCopy = stackalloc byte[BlockSizeBytes];
+ if (!isFinal)
+ {
+ // Save the lastInputBlock in a temp buffer first, in case input and output are overlapped
+ // and decrypting to the output overwrites the input.
+ ReadOnlySpan<byte> lastInputBlock = input.Slice(input.Length - BlockSizeBytes);
+ lastInputBlock.CopyTo(lastInputBlockCopy);
+ }
+
+ int numBytesWritten = EncryptDecrypt(input, output);
+
+ if (!isFinal)
+ {
+ SaveLastBlock(lastInputBlockCopy);
+ }
+
+ return numBytesWritten;
+ }
+
+ private void SaveLastBlock(ReadOnlySpan<byte> buffer)
+ {
+ Debug.Assert(buffer.Length > 0 && buffer.Length % BlockSizeBytes == 0);
+
+ ReadOnlySpan<byte> lastBlock = buffer.Slice(buffer.Length - BlockSizeBytes);
+ if (_lastBlockBuffer is null)
+ {
+ _lastBlockBuffer = lastBlock.ToArray();
+ }
+ else
+ {
+ Debug.Assert(_lastBlockBuffer.Length == BlockSizeBytes);
+ lastBlock.CopyTo(_lastBlockBuffer);
+ }
+ }
+
+ private unsafe int EncryptDecrypt(ReadOnlySpan<byte> input, Span<byte> output)
+ {
+ byte[] iv = _lastBlockBuffer ?? IV!;
+
+ fixed (byte* pKey = _key)
+ fixed (byte* pIV = iv)
+ fixed (byte* pInput = input)
+ fixed (byte* pOutput = output)
+ {
+ int bytesWritten = Interop.BrowserCrypto.EncryptDecrypt(
+ _encrypting ? 1 : 0,
+ pKey, _key.Length,
+ pIV, iv.Length,
+ pInput, input.Length,
+ pOutput, output.Length);
+
+ if (bytesWritten < 0)
+ throw new Exception(SR.Unknown_Error);
+
+ return bytesWritten;
+ }
+ }
+
+ //
+ // resets the state of the transform
+ //
+
+ void ILiteSymmetricCipher.Reset(ReadOnlySpan<byte> iv) => throw new NotImplementedException(); // never invoked
+
+ private void Reset()
+ {
+ CryptographicOperations.ZeroMemory(_lastBlockBuffer);
+ _lastBlockBuffer = null;
+ }
+ }
+}
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HMACHashProvider.Browser.Native.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HMACHashProvider.Browser.Native.cs
index 819ae0eaba1..2b040191232 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HMACHashProvider.Browser.Native.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HMACHashProvider.Browser.Native.cs
@@ -19,7 +19,7 @@ namespace System.Security.Cryptography
public HMACNativeHashProvider(string hashAlgorithmId, ReadOnlySpan<byte> key)
{
- Debug.Assert(HashProviderDispenser.CanUseSubtleCryptoImpl);
+ Debug.Assert(Interop.BrowserCrypto.CanUseSubtleCrypto);
(_hashAlgorithm, _hashSizeInBytes) = SHANativeHashProvider.HashAlgorithmToPal(hashAlgorithmId);
_key = key.ToArray();
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Browser.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Browser.cs
index 3b4f42b786d..8c604bb32b1 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Browser.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Browser.cs
@@ -5,8 +5,6 @@ namespace System.Security.Cryptography
{
internal static partial class HashProviderDispenser
{
- internal static readonly bool CanUseSubtleCryptoImpl = Interop.BrowserCrypto.CanUseSubtleCryptoImpl() == 1;
-
public static HashProvider CreateHashProvider(string hashAlgorithmId)
{
switch (hashAlgorithmId)
@@ -15,7 +13,7 @@ namespace System.Security.Cryptography
case HashAlgorithmNames.SHA256:
case HashAlgorithmNames.SHA384:
case HashAlgorithmNames.SHA512:
- return CanUseSubtleCryptoImpl
+ return Interop.BrowserCrypto.CanUseSubtleCrypto
? new SHANativeHashProvider(hashAlgorithmId)
: new SHAManagedHashProvider(hashAlgorithmId);
}
@@ -30,7 +28,7 @@ namespace System.Security.Cryptography
ReadOnlySpan<byte> source,
Span<byte> destination)
{
- if (CanUseSubtleCryptoImpl)
+ if (Interop.BrowserCrypto.CanUseSubtleCrypto)
{
return HMACNativeHashProvider.MacDataOneShot(hashAlgorithmId, key, source, destination);
}
@@ -44,7 +42,7 @@ namespace System.Security.Cryptography
public static int HashData(string hashAlgorithmId, ReadOnlySpan<byte> source, Span<byte> destination)
{
- if (CanUseSubtleCryptoImpl)
+ if (Interop.BrowserCrypto.CanUseSubtleCrypto)
{
return SHANativeHashProvider.HashOneShot(hashAlgorithmId, source, destination);
}
@@ -65,7 +63,7 @@ namespace System.Security.Cryptography
case HashAlgorithmNames.SHA256:
case HashAlgorithmNames.SHA384:
case HashAlgorithmNames.SHA512:
- return CanUseSubtleCryptoImpl
+ return Interop.BrowserCrypto.CanUseSubtleCrypto
? new HMACNativeHashProvider(hashAlgorithmId, key)
: new HMACManagedHashProvider(hashAlgorithmId, key);
}
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Native.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Native.cs
index 205f46e1cd0..6c5f70489af 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Native.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Native.cs
@@ -18,7 +18,7 @@ namespace System.Security.Cryptography
public SHANativeHashProvider(string hashAlgorithmId)
{
- Debug.Assert(HashProviderDispenser.CanUseSubtleCryptoImpl);
+ Debug.Assert(Interop.BrowserCrypto.CanUseSubtleCrypto);
(_impl, _hashSizeInBytes) = HashAlgorithmToPal(hashAlgorithmId);
}
diff --git a/src/libraries/System.Security.Cryptography/tests/AesTests.Browser.cs b/src/libraries/System.Security.Cryptography/tests/AesTests.Browser.cs
index 32c253a1234..5e8eafbaa38 100644
--- a/src/libraries/System.Security.Cryptography/tests/AesTests.Browser.cs
+++ b/src/libraries/System.Security.Cryptography/tests/AesTests.Browser.cs
@@ -43,5 +43,20 @@ namespace System.Security.Cryptography.Tests
Assert.Throws<PlatformNotSupportedException>(() => aes.CreateDecryptor(s_iv, s_iv));
}
}
+
+ // Browser's SubtleCrypto doesn't support AES-192
+ [Fact]
+ public static void Aes_InvalidKeySize_192_Browser()
+ {
+ byte[] key192 = new byte[192 / 8];
+ using (Aes aes = Aes.Create())
+ {
+ Assert.False(aes.ValidKeySize(192));
+ Assert.Throws<CryptographicException>(() => aes.Key = key192);
+ Assert.Throws<CryptographicException>(() => aes.KeySize = 192);
+ Assert.Throws<ArgumentException>(() => aes.CreateEncryptor(key192, s_iv));
+ Assert.Throws<ArgumentException>(() => aes.CreateDecryptor(key192, s_iv));
+ }
+ }
}
}
diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
index 35da8f4d649..cd78c6a2240 100644
--- a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
+++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
@@ -90,6 +90,7 @@ const linked_functions = [
"dotnet_browser_can_use_subtle_crypto_impl",
"dotnet_browser_simple_digest_hash",
"dotnet_browser_sign",
+ "dotnet_browser_encrypt_decrypt",
/// mono-threads-wasm.c
#if USE_PTHREADS
diff --git a/src/mono/wasm/runtime/crypto-worker.ts b/src/mono/wasm/runtime/crypto-worker.ts
index 92171e76562..674f1700d22 100644
--- a/src/mono/wasm/runtime/crypto-worker.ts
+++ b/src/mono/wasm/runtime/crypto-worker.ts
@@ -54,6 +54,35 @@ export function dotnet_browser_sign(hashAlgorithm: number, key_buffer: number, k
return 1;
}
+const AesBlockSizeBytes = 16; // 128 bits
+
+export function dotnet_browser_encrypt_decrypt(isEncrypting: boolean, key_buffer: number, key_len: number, iv_buffer: number, iv_len: number, input_buffer: number, input_len: number, output_buffer: number, output_len: number): number {
+ mono_assert(!!mono_wasm_crypto, "subtle crypto not initialized");
+
+ if (input_len <= 0 || input_len % AesBlockSizeBytes !== 0) {
+ throw "ENCRYPT DECRYPT: data was not a full block: " + input_len;
+ }
+
+ const msg = {
+ func: "encrypt_decrypt",
+ isEncrypting: isEncrypting,
+ key: Array.from(Module.HEAPU8.subarray(key_buffer, key_buffer + key_len)),
+ iv: Array.from(Module.HEAPU8.subarray(iv_buffer, iv_buffer + iv_len)),
+ data: Array.from(Module.HEAPU8.subarray(input_buffer, input_buffer + input_len))
+ };
+
+ const response = mono_wasm_crypto.channel.send_msg(JSON.stringify(msg));
+ const result = JSON.parse(response);
+
+ if (result.length > output_len) {
+ console.info("dotnet_browser_encrypt_decrypt: about to throw!");
+ throw "ENCRYPT DECRYPT: Encrypt/Decrypt length exceeds output length: " + result.length + " > " + output_len;
+ }
+
+ Module.HEAPU8.set(result, output_buffer);
+ return result.length;
+}
+
export function init_crypto(): void {
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined"
&& typeof SharedArrayBuffer !== "undefined"
diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
index c5851007de1..a559c8f42de 100644
--- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
+++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
@@ -127,6 +127,7 @@ const linked_functions = [
"dotnet_browser_can_use_subtle_crypto_impl",
"dotnet_browser_simple_digest_hash",
"dotnet_browser_sign",
+ "dotnet_browser_encrypt_decrypt",
/// mono-threads-wasm.c
#if USE_PTHREADS
diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts
index d91a67adcfe..436e0ba347a 100644
--- a/src/mono/wasm/runtime/exports.ts
+++ b/src/mono/wasm/runtime/exports.ts
@@ -71,7 +71,8 @@ import { diagnostics } from "./diagnostics";
import {
dotnet_browser_can_use_subtle_crypto_impl,
dotnet_browser_simple_digest_hash,
- dotnet_browser_sign
+ dotnet_browser_sign,
+ dotnet_browser_encrypt_decrypt
} from "./crypto-worker";
import { mono_wasm_cancel_promise_ref } from "./cancelable-promise";
import { mono_wasm_web_socket_open_ref, mono_wasm_web_socket_send, mono_wasm_web_socket_receive, mono_wasm_web_socket_close_ref, mono_wasm_web_socket_abort } from "./web-socket";
@@ -406,6 +407,7 @@ export const __linker_exports: any = {
dotnet_browser_can_use_subtle_crypto_impl,
dotnet_browser_simple_digest_hash,
dotnet_browser_sign,
+ dotnet_browser_encrypt_decrypt,
// threading exports, if threading is enabled
...mono_wasm_threads_exports,
diff --git a/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js b/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js
index 5e27dd59b5f..f6d66c82da6 100644
--- a/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js
+++ b/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js
@@ -187,7 +187,7 @@ async function sign(type, key, data) {
}
function get_hash_name(type) {
- switch(type) {
+ switch (type) {
case 0: return "SHA-1";
case 1: return "SHA-256";
case 2: return "SHA-384";
@@ -197,6 +197,75 @@ function get_hash_name(type) {
}
}
+const AesBlockSizeBytes = 16; // 128 bits
+
+async function encrypt_decrypt(isEncrypting, key, iv, data) {
+ const algorithmName = "AES-CBC";
+ const keyUsage = isEncrypting ? ["encrypt"] : ["encrypt", "decrypt"];
+ const cryptoKey = await importKey(key, algorithmName, keyUsage);
+ const algorithm = {
+ name: algorithmName,
+ iv: new Uint8Array(iv)
+ };
+
+ const result = await (isEncrypting ?
+ crypto.subtle.encrypt(
+ algorithm,
+ cryptoKey,
+ new Uint8Array(data)) :
+ decrypt(
+ algorithm,
+ cryptoKey,
+ data));
+
+ let resultByteArray = new Uint8Array(result);
+ if (isEncrypting) {
+ // trim off the last block, which is always a padding block.
+ resultByteArray = resultByteArray.slice(0, resultByteArray.length - AesBlockSizeBytes);
+ }
+ return Array.from(resultByteArray);
+}
+
+async function decrypt(algorithm, cryptoKey, data) {
+ // crypto.subtle AES-CBC will only allow a PaddingMode of PKCS7, but we need to use
+ // PaddingMode None. To simulate this, we only decrypt full blocks of data, with an extra full
+ // padding block of 0x10 (16) bytes appended to data. crypto.subtle will see that padding block and return
+ // the fully decrypted message. To create the encrypted padding block, we encrypt an empty array using the
+ // last block of the cipher text as the IV. This will create a full block of padding bytes.
+
+ const paddingBlockIV = new Uint8Array(data).slice(data.length - AesBlockSizeBytes);
+ const empty = new Uint8Array();
+ const encryptedPaddingBlockResult = await crypto.subtle.encrypt(
+ {
+ name: algorithm.name,
+ iv: paddingBlockIV
+ },
+ cryptoKey,
+ empty
+ );
+
+ const encryptedPaddingBlock = new Uint8Array(encryptedPaddingBlockResult);
+ for (var i = 0; i < encryptedPaddingBlock.length; i++) {
+ data.push(encryptedPaddingBlock[i]);
+ }
+
+ return await crypto.subtle.decrypt(
+ algorithm,
+ cryptoKey,
+ new Uint8Array(data));
+}
+
+function importKey(key, algorithmName, keyUsage) {
+ return crypto.subtle.importKey(
+ "raw",
+ new Uint8Array(key),
+ {
+ name: algorithmName
+ },
+ false /* extractable */,
+ keyUsage);
+}
+
// Operation to perform.
async function async_call(msg) {
const req = JSON.parse(msg);
@@ -208,7 +277,12 @@ async function async_call(msg) {
else if (req.func === "sign") {
const signResult = await sign(req.type, new Uint8Array(req.key), new Uint8Array(req.data));
return JSON.stringify(signResult);
- } else {
+ }
+ else if (req.func === "encrypt_decrypt") {
+ const signResult = await encrypt_decrypt(req.isEncrypting, req.key, req.iv, req.data);
+ return JSON.stringify(signResult);
+ }
+ else {
throw "CRYPTO: Unknown request: " + req.func;
}
}
diff --git a/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c b/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c
index 60b665dced1..0c78013ac34 100644
--- a/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c
+++ b/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c
@@ -21,6 +21,17 @@ extern int32_t dotnet_browser_sign(
uint8_t* output_buffer,
int32_t output_len);
+extern int32_t dotnet_browser_encrypt_decrypt(
+ int32_t encrypting,
+ uint8_t* key_buffer,
+ int32_t key_len,
+ uint8_t* iv_buffer,
+ int32_t iv_len,
+ uint8_t* input_buffer,
+ int32_t input_len,
+ uint8_t* output_buffer,
+ int32_t output_len);
+
extern int32_t dotnet_browser_can_use_subtle_crypto_impl(void);
int32_t SystemCryptoNativeBrowser_SimpleDigestHash(
@@ -45,6 +56,20 @@ int32_t SystemCryptoNativeBrowser_Sign(
return dotnet_browser_sign(hashAlgorithm, key_buffer, key_len, input_buffer, input_len, output_buffer, output_len);
}
+int32_t SystemCryptoNativeBrowser_EncryptDecrypt(
+ int32_t encrypting,
+ uint8_t* key_buffer,
+ int32_t key_len,
+ uint8_t* iv_buffer,
+ int32_t iv_len,
+ uint8_t* input_buffer,
+ int32_t input_len,
+ uint8_t* output_buffer,
+ int32_t output_len)
+{
+ return dotnet_browser_encrypt_decrypt(encrypting, key_buffer, key_len, iv_buffer, iv_len, input_buffer, input_len, output_buffer, output_len);
+}
+
int32_t SystemCryptoNativeBrowser_CanUseSubtleCryptoImpl(void)
{
return dotnet_browser_can_use_subtle_crypto_impl();
diff --git a/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h b/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h
index c0b598edb0d..2166a4a2177 100644
--- a/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h
+++ b/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h
@@ -31,4 +31,15 @@ PALEXPORT int32_t SystemCryptoNativeBrowser_Sign(
uint8_t* output_buffer,
int32_t output_len);
+PALEXPORT int32_t SystemCryptoNativeBrowser_EncryptDecrypt(
+ int32_t encrypting,
+ uint8_t* key_buffer,
+ int32_t key_len,
+ uint8_t* iv_buffer,
+ int32_t iv_len,
+ uint8_t* input_buffer,
+ int32_t input_len,
+ uint8_t* output_buffer,
+ int32_t output_len);
+
PALEXPORT int32_t SystemCryptoNativeBrowser_CanUseSubtleCryptoImpl(void);