Skip to content

Commit

Permalink
[AndroidCrypto] Implement DSA import/export and signing/verification. (
Browse files Browse the repository at this point in the history
…#49153)

Co-authored-by: Jeremy Barton <[email protected]>
  • Loading branch information
jkoritzinsky and bartonjs authored Mar 6, 2021
1 parent 50494e0 commit 17799a5
Show file tree
Hide file tree
Showing 16 changed files with 1,211 additions and 199 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,6 @@ internal static SafeBignumHandle CreateBignum(ReadOnlySpan<byte> bigEndianValue)
}
}

internal static byte[]? ExtractBignum(IntPtr bignum, int targetSize)
{
// Given that the only reference held to bignum is an IntPtr, create an unowned SafeHandle
// to ensure that we don't destroy the key after extraction.
using (SafeBignumHandle handle = new SafeBignumHandle(bignum, ownsHandle: false))
{
return ExtractBignum(handle, targetSize);
}
}

internal static unsafe byte[]? ExtractBignum(SafeBignumHandle? bignum, int targetSize)
{
if (bignum == null || bignum.IsInvalid)
Expand Down Expand Up @@ -84,10 +74,5 @@ internal sealed class SafeBignumHandle : Interop.JObjectLifetime.SafeJObjectHand
public SafeBignumHandle()
{
}

internal SafeBignumHandle(IntPtr handle, bool ownsHandle)
: base(handle, ownsHandle)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// 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;
using System.Security.Cryptography;
using Microsoft.Win32.SafeHandles;

internal static partial class Interop
{
internal static partial class AndroidCrypto
{
[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DsaGenerateKey")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DsaGenerateKey(out SafeDsaHandle dsa, int bits);

[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DsaSizeSignature")]
private static extern int DsaSizeSignature(SafeDsaHandle dsa);

/// <summary>
/// Return the maximum size of the DER-encoded key in bytes.
/// </summary>
internal static int DsaEncodedSignatureSize(SafeDsaHandle dsa)
{
int size = DsaSizeSignature(dsa);
return size;
}

[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DsaSignatureFieldSize")]
private static extern int AndroidCryptoNative_DsaSignatureFieldSize(SafeDsaHandle dsa);

/// <summary>
/// Return the size of the 'r' or 's' signature fields in bytes.
/// </summary>
internal static int DsaSignatureFieldSize(SafeDsaHandle dsa)
{
// Add another byte for the leading zero byte.
int size = AndroidCryptoNative_DsaSignatureFieldSize(dsa);
Debug.Assert(size * 2 < DsaEncodedSignatureSize(dsa));
return size;
}

[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DsaSizeP")]
private static extern int DsaSizeP(SafeDsaHandle dsa);

/// <summary>
/// Return the size of the key in bytes.
/// </summary>
internal static int DsaKeySize(SafeDsaHandle dsa)
{
int keySize = DsaSizeP(dsa);

// Assume an even multiple of 8 bytes \ 64 bits
keySize = (keySize + 7) / 8 * 8;
return keySize;
}

internal static bool DsaSign(SafeDsaHandle dsa, ReadOnlySpan<byte> hash, Span<byte> refSignature, out int outSignatureLength) =>
DsaSign(dsa, ref MemoryMarshal.GetReference(hash), hash.Length, ref MemoryMarshal.GetReference(refSignature), out outSignatureLength);

[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DsaSign")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DsaSign(SafeDsaHandle dsa, ref byte hash, int hashLength, ref byte refSignature, out int outSignatureLength);

internal static bool DsaVerify(SafeDsaHandle dsa, ReadOnlySpan<byte> hash, ReadOnlySpan<byte> signature)
{
int ret = DsaVerify(
dsa,
ref MemoryMarshal.GetReference(hash),
hash.Length,
ref MemoryMarshal.GetReference(signature),
signature.Length);

if (ret == -1)
{
throw new CryptographicException();
}

return ret == 1;
}

[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DsaVerify")]
private static extern int DsaVerify(SafeDsaHandle dsa, ref byte hash, int hashLength, ref byte signature, int signatureLength);

internal static DSAParameters ExportDsaParameters(SafeDsaHandle key, bool includePrivateParameters)
{
Debug.Assert(
key != null && !key.IsInvalid,
"Callers should check the key is invalid and throw an exception with a message");

if (key == null || key.IsInvalid)
{
throw new CryptographicException();
}

SafeBignumHandle p_bn, q_bn, g_bn, y_bn, x_bn;
int p_cb, q_cb, g_cb, y_cb, x_cb;

if (!GetDsaParameters(key,
out p_bn, out p_cb,
out q_bn, out q_cb,
out g_bn, out g_cb,
out y_bn, out y_cb,
out x_bn, out x_cb))
{
p_bn.Dispose();
q_bn.Dispose();
g_bn.Dispose();
y_bn.Dispose();
x_bn.Dispose();
throw new CryptographicException();
}

using (p_bn)
using (q_bn)
using (g_bn)
using (y_bn)
using (x_bn)
{
// Match Windows semantics where p, g and y have same length
int pgy_cb = GetMax(p_cb, g_cb, y_cb);

// Match Windows semantics where q and x have same length
int qx_cb = GetMax(q_cb, x_cb);

DSAParameters dsaParameters = new DSAParameters
{
P = Crypto.ExtractBignum(p_bn, pgy_cb)!,
Q = Crypto.ExtractBignum(q_bn, qx_cb)!,
G = Crypto.ExtractBignum(g_bn, pgy_cb)!,
Y = Crypto.ExtractBignum(y_bn, pgy_cb)!,
};

if (includePrivateParameters)
{
dsaParameters.X = Crypto.ExtractBignum(x_bn, qx_cb);
}

return dsaParameters;
}
}

[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_GetDsaParameters")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetDsaParameters(
SafeDsaHandle key,
out SafeBignumHandle p, out int p_cb,
out SafeBignumHandle q, out int q_cb,
out SafeBignumHandle g, out int g_cb,
out SafeBignumHandle y, out int y_cb,
out SafeBignumHandle x, out int x_cb);

[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DsaKeyCreateByExplicitParameters")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DsaKeyCreateByExplicitParameters(
out SafeDsaHandle dsa,
byte[] p,
int pLength,
byte[] q,
int qLength,
byte[] g,
int gLength,
byte[] y,
int yLength,
byte[]? x,
int xLength);
}
}

namespace System.Security.Cryptography
{
internal sealed class SafeDsaHandle : Interop.JObjectLifetime.SafeJObjectHandle
{
public SafeDsaHandle()
{
}

internal SafeDsaHandle(IntPtr ptr)
: base(ptr)
{
}
}
}
Loading

0 comments on commit 17799a5

Please sign in to comment.