Skip to content

Commit e30a478

Browse files
[AndroidCrypto] Fix up RSA and some EC tests (#48930)
Co-authored-by: Jeremy Barton <[email protected]>
1 parent dad9b38 commit e30a478

File tree

17 files changed

+1525
-139
lines changed

17 files changed

+1525
-139
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
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+
using System;
5+
using System.Diagnostics;
6+
using System.Runtime.InteropServices;
7+
using System.Security.Cryptography;
8+
using Microsoft.Win32.SafeHandles;
9+
10+
internal static partial class Interop
11+
{
12+
internal static partial class AndroidCrypto
13+
{
14+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RsaCreate")]
15+
internal static extern SafeRsaHandle RsaCreate();
16+
17+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RsaUpRef")]
18+
[return: MarshalAs(UnmanagedType.Bool)]
19+
internal static extern bool RsaUpRef(IntPtr rsa);
20+
21+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RsaDestroy")]
22+
internal static extern void RsaDestroy(IntPtr rsa);
23+
24+
internal static SafeRsaHandle DecodeRsaPublicKey(ReadOnlySpan<byte> buf) =>
25+
DecodeRsaPublicKey(ref MemoryMarshal.GetReference(buf), buf.Length);
26+
27+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DecodeRsaPublicKey")]
28+
private static extern SafeRsaHandle DecodeRsaPublicKey(ref byte buf, int len);
29+
30+
internal static int RsaPublicEncrypt(
31+
int flen,
32+
ReadOnlySpan<byte> from,
33+
Span<byte> to,
34+
SafeRsaHandle rsa,
35+
RsaPadding padding) =>
36+
RsaPublicEncrypt(flen, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa, padding);
37+
38+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RsaPublicEncrypt")]
39+
private static extern int RsaPublicEncrypt(
40+
int flen,
41+
ref byte from,
42+
ref byte to,
43+
SafeRsaHandle rsa,
44+
RsaPadding padding);
45+
46+
internal static int RsaPrivateDecrypt(
47+
int flen,
48+
ReadOnlySpan<byte> from,
49+
Span<byte> to,
50+
SafeRsaHandle rsa,
51+
RsaPadding padding) =>
52+
RsaPrivateDecrypt(flen, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa, padding);
53+
54+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RsaPrivateDecrypt")]
55+
private static extern int RsaPrivateDecrypt(
56+
int flen,
57+
ref byte from,
58+
ref byte to,
59+
SafeRsaHandle rsa,
60+
RsaPadding padding);
61+
62+
internal static int RsaSignPrimitive(
63+
ReadOnlySpan<byte> from,
64+
Span<byte> to,
65+
SafeRsaHandle rsa) =>
66+
RsaSignPrimitive(from.Length, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa);
67+
68+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RsaSignPrimitive")]
69+
private static extern int RsaSignPrimitive(
70+
int flen,
71+
ref byte from,
72+
ref byte to,
73+
SafeRsaHandle rsa);
74+
75+
internal static int RsaVerificationPrimitive(
76+
ReadOnlySpan<byte> from,
77+
Span<byte> to,
78+
SafeRsaHandle rsa) =>
79+
RsaVerificationPrimitive(from.Length, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa);
80+
81+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RsaVerificationPrimitive")]
82+
private static extern int RsaVerificationPrimitive(
83+
int flen,
84+
ref byte from,
85+
ref byte to,
86+
SafeRsaHandle rsa);
87+
88+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RsaSize")]
89+
internal static extern int RsaSize(SafeRsaHandle rsa);
90+
91+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RsaGenerateKeyEx")]
92+
internal static extern int RsaGenerateKeyEx(SafeRsaHandle rsa, int bits);
93+
94+
internal static RSAParameters ExportRsaParameters(SafeRsaHandle key, bool includePrivateParameters)
95+
{
96+
Debug.Assert(
97+
key != null && !key.IsInvalid,
98+
"Callers should check the key is invalid and throw an exception with a message");
99+
100+
if (key == null || key.IsInvalid)
101+
{
102+
throw new CryptographicException();
103+
}
104+
105+
bool addedRef = false;
106+
107+
try
108+
{
109+
key.DangerousAddRef(ref addedRef);
110+
111+
IntPtr n, e, d, p, dmp1, q, dmq1, iqmp;
112+
if (!GetRsaParameters(key, out n, out e, out d, out p, out dmp1, out q, out dmq1, out iqmp))
113+
{
114+
throw new CryptographicException();
115+
}
116+
117+
int modulusSize = RsaSize(key);
118+
119+
// RSACryptoServiceProvider expects P, DP, Q, DQ, and InverseQ to all
120+
// be padded up to half the modulus size.
121+
int halfModulus = modulusSize / 2;
122+
123+
RSAParameters rsaParameters = new RSAParameters
124+
{
125+
Modulus = Crypto.ExtractBignum(n, modulusSize)!,
126+
Exponent = Crypto.ExtractBignum(e, 0)!,
127+
};
128+
129+
if (includePrivateParameters)
130+
{
131+
rsaParameters.D = Crypto.ExtractBignum(d, modulusSize);
132+
rsaParameters.P = Crypto.ExtractBignum(p, halfModulus);
133+
rsaParameters.DP = Crypto.ExtractBignum(dmp1, halfModulus);
134+
rsaParameters.Q = Crypto.ExtractBignum(q, halfModulus);
135+
rsaParameters.DQ = Crypto.ExtractBignum(dmq1, halfModulus);
136+
rsaParameters.InverseQ = Crypto.ExtractBignum(iqmp, halfModulus);
137+
}
138+
139+
return rsaParameters;
140+
}
141+
finally
142+
{
143+
if (addedRef)
144+
key.DangerousRelease();
145+
}
146+
}
147+
148+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_GetRsaParameters")]
149+
[return: MarshalAs(UnmanagedType.Bool)]
150+
private static extern bool GetRsaParameters(
151+
SafeRsaHandle key,
152+
out IntPtr n,
153+
out IntPtr e,
154+
out IntPtr d,
155+
out IntPtr p,
156+
out IntPtr dmp1,
157+
out IntPtr q,
158+
out IntPtr dmq1,
159+
out IntPtr iqmp);
160+
161+
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SetRsaParameters")]
162+
[return: MarshalAs(UnmanagedType.Bool)]
163+
internal static extern bool SetRsaParameters(
164+
SafeRsaHandle key,
165+
byte[]? n,
166+
int nLength,
167+
byte[]? e,
168+
int eLength,
169+
byte[]? d,
170+
int dLength,
171+
byte[]? p,
172+
int pLength,
173+
byte[]? dmp1,
174+
int dmp1Length,
175+
byte[]? q,
176+
int qLength,
177+
byte[]? dmq1,
178+
int dmq1Length,
179+
byte[]? iqmp,
180+
int iqmpLength);
181+
182+
internal enum RsaPadding : int
183+
{
184+
Pkcs1 = 0,
185+
OaepSHA1 = 1,
186+
NoPadding = 2,
187+
}
188+
}
189+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
using System;
5+
using System.Diagnostics;
6+
using System.Security;
7+
using System.Runtime.InteropServices;
8+
9+
namespace System.Security.Cryptography
10+
{
11+
internal sealed class SafeRsaHandle : SafeHandle
12+
{
13+
public SafeRsaHandle() :
14+
base(IntPtr.Zero, ownsHandle: true)
15+
{
16+
}
17+
18+
protected override bool ReleaseHandle()
19+
{
20+
Interop.AndroidCrypto.RsaDestroy(handle);
21+
SetHandle(IntPtr.Zero);
22+
return true;
23+
}
24+
25+
public override bool IsInvalid
26+
{
27+
get { return handle == IntPtr.Zero; }
28+
}
29+
30+
internal static SafeRsaHandle DuplicateHandle(IntPtr handle)
31+
{
32+
Debug.Assert(handle != IntPtr.Zero);
33+
34+
// Reliability: Allocate the SafeHandle before calling RSA_up_ref so
35+
// that we don't lose a tracked reference in low-memory situations.
36+
SafeRsaHandle safeHandle = new SafeRsaHandle();
37+
38+
if (!Interop.AndroidCrypto.RsaUpRef(handle))
39+
{
40+
throw new CryptographicException();
41+
}
42+
43+
safeHandle.SetHandle(handle);
44+
return safeHandle;
45+
}
46+
}
47+
}

src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs

+108-3
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,56 @@ namespace System.Security.Cryptography
99
{
1010
internal sealed class RsaPaddingProcessor
1111
{
12+
// DigestInfo header values taken from https://tools.ietf.org/html/rfc3447#section-9.2, Note 1.
13+
private static readonly byte[] s_digestInfoMD5 =
14+
{
15+
0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86,
16+
0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00,
17+
0x04, 0x10,
18+
};
19+
20+
private static readonly byte[] s_digestInfoSha1 =
21+
{
22+
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03,
23+
0x02, 0x1A, 0x05, 0x00, 0x04, 0x14,
24+
};
25+
26+
private static readonly byte[] s_digestInfoSha256 =
27+
{
28+
0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48,
29+
0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04,
30+
0x20,
31+
};
32+
33+
private static readonly byte[] s_digestInfoSha384 =
34+
{
35+
0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48,
36+
0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04,
37+
0x30,
38+
};
39+
40+
private static readonly byte[] s_digestInfoSha512 =
41+
{
42+
0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48,
43+
0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04,
44+
0x40,
45+
};
46+
1247
private static readonly ConcurrentDictionary<HashAlgorithmName, RsaPaddingProcessor> s_lookup =
1348
new ConcurrentDictionary<HashAlgorithmName, RsaPaddingProcessor>();
1449

1550
private readonly HashAlgorithmName _hashAlgorithmName;
1651
private readonly int _hLen;
52+
private readonly ReadOnlyMemory<byte> _digestInfoPrefix;
1753

18-
private RsaPaddingProcessor(HashAlgorithmName hashAlgorithmName, int hLen)
54+
private RsaPaddingProcessor(
55+
HashAlgorithmName hashAlgorithmName,
56+
int hLen,
57+
ReadOnlyMemory<byte> digestInfoPrefix)
1958
{
2059
_hashAlgorithmName = hashAlgorithmName;
2160
_hLen = hLen;
61+
_digestInfoPrefix = digestInfoPrefix;
2262
}
2363

2464
internal static int BytesRequiredForBitCount(int keySizeInBits)
@@ -40,14 +80,41 @@ internal static RsaPaddingProcessor OpenProcessor(HashAlgorithmName hashAlgorith
4080
{
4181
// SHA-2-512 is the biggest we expect
4282
Span<byte> stackDest = stackalloc byte[512 / 8];
83+
ReadOnlyMemory<byte> digestInfoPrefix;
84+
85+
if (hashAlgorithmName == HashAlgorithmName.MD5)
86+
{
87+
digestInfoPrefix = s_digestInfoMD5;
88+
}
89+
else if (hashAlgorithmName == HashAlgorithmName.SHA1)
90+
{
91+
digestInfoPrefix = s_digestInfoSha1;
92+
}
93+
else if (hashAlgorithmName == HashAlgorithmName.SHA256)
94+
{
95+
digestInfoPrefix = s_digestInfoSha256;
96+
}
97+
else if (hashAlgorithmName == HashAlgorithmName.SHA384)
98+
{
99+
digestInfoPrefix = s_digestInfoSha384;
100+
}
101+
else if (hashAlgorithmName == HashAlgorithmName.SHA512)
102+
{
103+
digestInfoPrefix = s_digestInfoSha512;
104+
}
105+
else
106+
{
107+
Debug.Fail("Unknown digest algorithm");
108+
throw new CryptographicException();
109+
}
43110

44111
if (hasher.TryGetHashAndReset(stackDest, out int bytesWritten))
45112
{
46-
return new RsaPaddingProcessor(hashAlgorithmName, bytesWritten);
113+
return new RsaPaddingProcessor(hashAlgorithmName, bytesWritten, digestInfoPrefix);
47114
}
48115

49116
byte[] big = hasher.GetHashAndReset();
50-
return new RsaPaddingProcessor(hashAlgorithmName, big.Length);
117+
return new RsaPaddingProcessor(hashAlgorithmName, big.Length, digestInfoPrefix);
51118
}
52119
});
53120
}
@@ -80,6 +147,44 @@ internal static void PadPkcs1Encryption(
80147
source.CopyTo(mInEM);
81148
}
82149

150+
internal void PadPkcs1Signature(
151+
ReadOnlySpan<byte> source,
152+
Span<byte> destination)
153+
{
154+
// https://tools.ietf.org/html/rfc3447#section-9.2
155+
156+
// 1. H = Hash(M)
157+
// Done by the caller.
158+
159+
// 2. Encode the DigestInfo value
160+
ReadOnlySpan<byte> digestInfoPrefix = _digestInfoPrefix.Span;
161+
int expectedLength = digestInfoPrefix[^1];
162+
163+
if (source.Length != expectedLength)
164+
{
165+
throw new CryptographicException(SR.Cryptography_SignHash_WrongSize);
166+
}
167+
168+
int tLen = digestInfoPrefix.Length + expectedLength;
169+
170+
// 3. If emLen < tLen + 11, fail
171+
if (destination.Length - 11 < tLen)
172+
{
173+
throw new CryptographicException(SR.Cryptography_KeyTooSmall);
174+
}
175+
176+
// 4. Generate emLen - tLen - 3 bytes of 0xFF as "PS"
177+
int paddingLength = destination.Length - tLen - 3;
178+
179+
// 5. EM = 0x00 || 0x01 || PS || 0x00 || T
180+
destination[0] = 0;
181+
destination[1] = 1;
182+
destination.Slice(2, paddingLength).Fill(0xFF);
183+
destination[paddingLength + 2] = 0;
184+
digestInfoPrefix.CopyTo(destination.Slice(paddingLength + 3));
185+
source.CopyTo(destination.Slice(paddingLength + 3 + digestInfoPrefix.Length));
186+
}
187+
83188
internal void PadOaep(
84189
ReadOnlySpan<byte> source,
85190
Span<byte> destination)

src/libraries/Native/Unix/Common/pal_config.h.in

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#cmakedefine01 HAVE_NON_LEGACY_STATFS
3232
#cmakedefine01 HAVE_STRCPY_S
3333
#cmakedefine01 HAVE_STRLCPY
34+
#cmakedefine01 HAVE_STRCAT_S
35+
#cmakedefine01 HAVE_STRLCAT
3436
#cmakedefine01 HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP
3537
#cmakedefine01 HAVE_POSIX_ADVISE
3638
#cmakedefine01 PRIORITY_REQUIRES_INT_WHO

0 commit comments

Comments
 (0)