Skip to content

Commit a36cd09

Browse files
authored
Simplify temporary keychain password-gen
Rather than a bespoke solution that's functionally equivalent to `RNG.GetItems("ABCDEFGHIJKLMNOP"u8, dest512)`, just use RNG.GetItems, but with a bigger choice-set and a smaller output size.
1 parent 7976f78 commit a36cd09

File tree

1 file changed

+9
-19
lines changed

1 file changed

+9
-19
lines changed

src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -240,32 +240,22 @@ internal static SafeKeychainHandle CreateOrOpenKeychain(string keychainPath, boo
240240

241241
internal static unsafe SafeTemporaryKeychainHandle CreateTemporaryKeychain()
242242
{
243-
const int RandomSize = 256;
243+
// Because we use the RNG to choose from 64 items, we only get 6 bits per byte of random.
244+
// 128 * 6 is 768 bits of random, which undoubtedly vastly exceeds the space of the key
245+
// it turns into, so it's plenty.
246+
const int RandomSize = 128;
247+
ReadOnlySpan<byte> choices = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@"u8;
248+
Debug.Assert(int.IsPow2(choices.Length), $"Choice-set length {choices.Length} should be a power of 2 for perf");
244249

245250
string tempPath = Path.GetTempPath();
246251
string tmpKeychainPath = Path.EndsInDirectorySeparator(tempPath) ?
247252
$"{tempPath}{Guid.NewGuid():N}.keychain" :
248253
$"{tempPath}{Path.DirectorySeparatorChar}{Guid.NewGuid():N}.keychain";
249254

250255
// Use a random password so that if a keychain is abandoned it isn't recoverable.
251-
// We use stack to minimize lingering
252-
Span<byte> random = stackalloc byte[RandomSize];
253-
RandomNumberGenerator.Fill(random);
254-
255-
// Create hex-like UTF8 string.
256-
Span<byte> utf8Passphrase = stackalloc byte[RandomSize * 2 + 1];
257-
utf8Passphrase[RandomSize * 2] = 0; // null termination for C string.
258-
259-
for (int i = 0; i < random.Length; i++)
260-
{
261-
// Instead of true hexadecimal, we simply take lower and upper 4 bits and we offset them from ASCII 'A'
262-
// to get printable form. We dont use managed string to avoid lingering copies.
263-
utf8Passphrase[i * 2] = (byte)((random[i] & 0x0F) + 65);
264-
utf8Passphrase[i * 2 + 1] = (byte)((random[i] >> 4) & 0x0F + 65);
265-
}
266-
267-
// clear the binary bits.
268-
CryptographicOperations.ZeroMemory(random);
256+
Span<byte> utf8Passphrase = stackalloc byte[RandomSize + 1];
257+
utf8Passphrase[RandomSize] = 0; // null termination for C string.
258+
RandomNumberGenerator.GetItems(choices, utf8Passphrase.Slice(0, RandomSize));
269259

270260
SafeTemporaryKeychainHandle keychain;
271261
int osStatus;

0 commit comments

Comments
 (0)