Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Re-enable asymmetric key generation on macOS. #16588

Merged
merged 4 commits into from
Mar 2, 2017

Conversation

bartonjs
Copy link
Member

@bartonjs bartonjs commented Mar 1, 2017

Generate RSA and ECDSA keys via a temporary keychain.

When no keychain is given, SecKeyGeneratePair will generate the keys into the
default/login keychain. Since the keys were not expected to have been in the
keychain, they permanently leak out and the keychain grows over time.

Instead, have the managed code provide a temporary keychain into which the keys
will be created. Then, export them and re-import them to the NULL keychain. The
temporary keychain can then be deleted, and the hard problem of delayed cleanup is
avoided.

Files on disk will still leak if the process terminates during the key generation
phase, but that is a much smaller window than other 'temporary file on disk' solutions.

Generate RSA and ECDSA keys via a temporary keychain.

When no keychain is given, SecKeyGeneratePair will generate the keys into the
default/login keychain.  Since the keys were not expected to have been in the
keychain, they permanently leak out and the keychain grows over time.

Instead, have the managed code provide a temporary keychain into which the keys
will be created. Then, export them and re-import them to the NULL keychain. The
temporary keychain can then be deleted, and the hard problem of delayed cleanup is
avoided.

Files on disk will still leak if the process terminates during the key generation
phase, but that is a much smaller window than other 'temporary file on disk' solutions.
@bartonjs bartonjs self-assigned this Mar 1, 2017
@bartonjs bartonjs changed the title Re-enable async key generation on macOS. Re-enable asymmetric key generation on macOS. Mar 1, 2017
}

Debug.Fail($"Unexpected result from AppleCryptoNative_EccGenerateKey: {result}");
throw new CryptographicException();
Copy link
Member

@stephentoub stephentoub Mar 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the osStatus regardless? i.e.

Debug.Assert(result == 0, $"Unexpected result from {nameof(AppleCryptoNative_EccGenerateKey)}: {result}");
throw CreateExceptionForOSStatus(osStatus);

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A non-boolean value should mean that we don't trust osStatus to report the true cause of the error (it might even still be noErr).

}

Debug.Fail($"Unexpected result from AppleCryptoNative_RsaGenerateKey: {result}");
throw new CryptographicException();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question

@@ -4,8 +4,8 @@

#include "pal_ecc.h"

extern "C" int
AppleCryptoNative_EccGenerateKey(int32_t keySizeBits, SecKeyRef* pPublicKey, SecKeyRef* pPrivateKey, int32_t* pOSStatus)
extern "C" int AppleCryptoNative_EccGenerateKey(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type should be changed from int to int32_t

@@ -7,8 +7,8 @@
static int32_t ExecuteCFDataTransform(
SecTransformRef xform, uint8_t* pbData, int32_t cbData, CFDataRef* pDataOut, CFErrorRef* pErrorOut);

extern "C" int
AppleCryptoNative_RsaGenerateKey(int32_t keySizeBits, SecKeyRef* pPublicKey, SecKeyRef* pPrivateKey, int32_t* pOSStatus)
extern "C" int AppleCryptoNative_RsaGenerateKey(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int => int32_t... maybe search and replace in these files in case there are more of 'em

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed these two int => int32_ts. There are others, which is causing clang-format to have a conniption; I'll do that as a style only change after this one goes in.


SecItemImportExportKeyParameters keyParams = {};
keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
keyParams.passphrase = CFSTR("ExportImportPassphrase");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this string? Is it part of the spec?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does CFSTR allocate anything, and if so, does it need to be freed somewhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apple's export API doesn't (under most circumstances) allow plaintext export. So I have to give it a non-null value for the export passphrase (and the same value on import to decrypt the blob).

CFSTR has the following behaviors:

The returned CFString has the following semantics:

  • Because CFSTR is not a Create or Copy function, you are not responsible for releasing the string when you no longer need it.
  • The string is not released by CFString. In other words, CFString guarantees its validity until the program terminates.
    • I assume the docs meant "not released by CFRelease", since that's the only thing that makes sense.
  • The string can be retained and released in a balanced fashion, like any other CFString.

https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFStrings/Articles/CreatingAndCopying.html

That said, I've usually been calling CFRelease (because point 2 says it doesn't hurt), so I'll do so here, too.

@@ -43,7 +43,7 @@ public bool SupportsSha2Oaep
get { return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); }
}

public bool SupportsKeyGeneration => !RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public bool SupportsKeyGeneration => true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there platforms now that don't support key generation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apple doesn't support DSA. But RSA and ECDSA are full on again now, so I'll go ahead and remove the conditionals.

@bartonjs bartonjs merged commit 37ddcd3 into dotnet:dev/apple_crypto Mar 2, 2017
@bartonjs bartonjs deleted the enable_async_keygen branch March 2, 2017 18:36
@karelz karelz modified the milestone: 2.0.0 Mar 7, 2017
@bartonjs bartonjs removed their assignment Mar 14, 2017
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
Generate RSA and ECDSA keys via a temporary keychain.

When no keychain is given, SecKeyGeneratePair will generate the keys into the
default/login keychain.  Since the keys were not expected to have been in the
keychain, they permanently leak out and the keychain grows over time.

Instead, have the managed code provide a temporary keychain into which the keys
will be created. Then, export them and re-import them to the NULL keychain. The
temporary keychain can then be deleted, and the hard problem of delayed cleanup is
avoided.

Files on disk will still leak if the process terminates during the key generation
phase, but that is a much smaller window than other 'temporary file on disk' solutions.

Commit migrated from dotnet/corefx@37ddcd3
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants