Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Expand Up @@ -35,6 +35,78 @@ internal static SafeEcKeyHandle EcKeyCreateByKeyParameters(
return key;
}

[LibraryImport(Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_EcKeyExportPkcs8PrivateKey")]
private static partial int EcKeyExportPkcs8PrivateKey(
SafeEcKeyHandle key,
Span<byte> destination,
int destinationLength,
out int bytesWrittenOrRequired);

internal static bool TryExportEcKeyPkcs8PrivateKey(SafeEcKeyHandle key, out ArraySegment<byte> pkcs8)
{
// Leaves enough room for a P-521 PKCS#8 encoding including the public point.
const int InitialBufferSize = 256;
const int Success = 1;
const int InsufficientBuffer = -1;

pkcs8 = default;
byte[] buffer = CryptoPool.Rent(InitialBufferSize);

try
{
int result = EcKeyExportPkcs8PrivateKey(
key,
buffer,
buffer.Length,
out int bytesWrittenOrRequired);

if (result == InsufficientBuffer)
{
int requiredSize = bytesWrittenOrRequired;

if (requiredSize <= buffer.Length)
{
throw new CryptographicException();
}

// Our opportunistic buffer size wasn't large enough - try one more time with a larger buffer.
byte[] tempBuffer = CryptoPool.Rent(requiredSize);
CryptoPool.Return(buffer);
buffer = tempBuffer;

result = EcKeyExportPkcs8PrivateKey(
key,
buffer.AsSpan(0, requiredSize),
requiredSize,
out bytesWrittenOrRequired);

if (result != Success || bytesWrittenOrRequired != requiredSize)
{
throw new CryptographicException();
}
}
else if (result != Success)
{
return false;
}
else if (bytesWrittenOrRequired <= 0 || bytesWrittenOrRequired > buffer.Length)
{
throw new CryptographicException();
}

pkcs8 = new ArraySegment<byte>(buffer, 0, bytesWrittenOrRequired);
return true;
}
finally
{
// Return what we rented if we didn't assign the `out pkcs8`.
if (pkcs8.Array is null)
{
CryptoPool.Return(buffer);
}
}
}

[LibraryImport(Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_EcKeyCreateByExplicitParameters")]
internal static partial SafeEcKeyHandle EcKeyCreateByExplicitParameters(
ECCurve.ECCurveType curveType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,117 @@ namespace System.Security.Cryptography
{
internal sealed partial class ECAndroid
{
private static readonly string[] s_validOids = [Oids.EcPublicKey];

public int ImportParameters(ECParameters parameters)
{
SafeEcKeyHandle key;

parameters.Validate();
SafeEcKeyHandle key = ImportParametersCore(parameters);

if (key is null || key.IsInvalid)
{
key?.Dispose();
throw new CryptographicException();
}

if (parameters.D is not null && parameters.Q.X is null)
{
SafeEcKeyHandle? completeKey = null;

try
{
if (!TryRecoverPublicKey(key, out ECPoint publicKey))
{
throw new CryptographicException();
}

ECParameters completeParameters = parameters;
completeParameters.Q = publicKey;
completeKey = ImportParametersCore(completeParameters);

if (completeKey is null || completeKey.IsInvalid)
{
throw new CryptographicException();
}
}
catch
{
completeKey?.Dispose();
key.Dispose();
throw;
}

key.Dispose();
key = completeKey;
}

FreeKey();
_key = new Lazy<SafeEcKeyHandle>(key);
return KeySize;
}

private static SafeEcKeyHandle ImportParametersCore(ECParameters parameters)
{
if (parameters.Curve.IsPrime)
{
key = ImportPrimeCurveParameters(parameters);
return ImportPrimeCurveParameters(parameters);
}
else if (parameters.Curve.IsCharacteristic2)

if (parameters.Curve.IsCharacteristic2)
{
key = ImportCharacteristic2CurveParameters(parameters);
return ImportCharacteristic2CurveParameters(parameters);
}
else if (parameters.Curve.IsNamed)

if (parameters.Curve.IsNamed)
{
key = ImportNamedCurveParameters(parameters);
return ImportNamedCurveParameters(parameters);
}
else

throw new PlatformNotSupportedException(
SR.Format(SR.Cryptography_CurveNotSupported, parameters.Curve.CurveType.ToString()));
}

private static bool TryRecoverPublicKey(SafeEcKeyHandle key, out ECPoint publicKey)
{
publicKey = default;

if (!Interop.AndroidCrypto.TryExportEcKeyPkcs8PrivateKey(key, out ArraySegment<byte> pkcs8))
{
throw new PlatformNotSupportedException(
SR.Format(SR.Cryptography_CurveNotSupported, parameters.Curve.CurveType.ToString()));
return false;
}

if (key == null || key.IsInvalid)
ECParameters recoveredParameters = default;

try
{
key?.Dispose();
throw new CryptographicException();
KeyFormatHelper.ReadPkcs8<ECParameters>(
s_validOids,
pkcs8.AsSpan(),
EccKeyFormatHelper.FromECPrivateKey,
out int bytesRead,
out recoveredParameters);

if (bytesRead != pkcs8.Count || recoveredParameters.Q.X is null || recoveredParameters.Q.Y is null)
{
return false;
}

publicKey = recoveredParameters.Q;
return true;
}
catch (CryptographicException)
{
return false;
}
finally
{
CryptoPool.Return(pkcs8);

FreeKey();
_key = new Lazy<SafeEcKeyHandle>(key);
return KeySize;
if (recoveredParameters.D is not null)
{
CryptographicOperations.ZeroMemory(recoveredParameters.D);
}
}
}

public static ECParameters ExportExplicitParameters(SafeEcKeyHandle currentKey, bool includePrivateParameters) =>
Expand Down
Loading
Loading