Skip to content
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

[AndroidCrypto] Implement DSA import/export and signing/verification. #49153

Merged
merged 13 commits into from
Mar 6, 2021
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// 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_DsaUpRef")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DsaUpRef(IntPtr dsa);
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved

[DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DsaDestroy")]
internal static extern void DsaDestroy(IntPtr dsa);
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved

[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))
{
throw new CryptographicException();
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
}

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