-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
[API Proposal]: RandomDataGenerator #73864
Comments
Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones Issue DetailsBackground and motivationWe have primitives of random number generation on While API ProposalI propose introducing a new static class, namespace System.Security.Cryptography {
public static partial class RandomDataGenerator {
// Proposed:
public static void Fill<T>(Span<T> destination, ReadOnlySpan<T> choices);
public static T[] Create<T>(int length, ReadOnlySpan<T> choices);
public static string Create(int length, ReadOnlySpan<char> choices);
// Optional helpers:
// Accelerator where 'choices' is 0-9A-F.
public static void FillHexadecimal(Span<char> destination);
public static string CreateHexadecimal(int length);
// Accelerator where 'choices' is 0-9A-z.
public static void FillAlphaNumeric(Span<char> destination);
public static string CreateAlphaNumeric(int length);
}
} Out of scope:
API Usagestring random128Bit = RandomDataGenerator.CreateHexadecimal(32);
bool[] coinFlips = RandomDataGenerator.Create(16, new bool[] { true, false });
string recoveryCode = RandomDataGenerator.Create(16, "0123456789!@#^&*ABCyougettheideaXYZ"); Alternative DesignsI do have some thoughts about the proposal:
RisksNo response
|
public static T[] Create<T>(int length, ReadOnlySpan<T> choices); A couple things about the name.
public static string CreateHexadecimal(int length);
string random128Bit = RandomDataGenerator.CreateHexadecimal(32); Assuming your math is intentional, (I'm not sure that implementing CreateHexadecimal in that way is the best answer, but it could be perf-compared to the cost of hexifying the raw RNG output to see which was better... if we cared to)
Base64 seems hard, since you'd have to talk about the amount of bytes to generate and then write
"Shuffle", unless you're an SIMD instruction, is generally random. F-Y selection like private static void Shuffle<T>(Span<T> source)
{
T temp;
for (int i = source.Length - 1; i > 0; i--)
{
int to = RandomNumberGenerator.GetInt32(i + 1);
if (to != i)
{
temp = source[to];
source[to] = source[i];
source[i] = temp;
}
}
} is random, and CSPRNG random. (It's equivalent to calling the proposed Create with integer indices from 0 .. Length - 1 and using those to reorder things). But, I don't really feel it's called for. It's way less common than things like "I need a random 12 character password from this alphabet" ( |
Seems reasonable.
Sometimes I do things intentionally. But yes, in this case I was going for characters, not bytes, of input.
That also seems reasonable.
That is one reason I had for the hex helper. It's something we could have a better implementation for instead of doing it the naive way. For alpha numeric that requires 6 bits per-character, so if we really wanted to we could build a better encoder for that. It's a shame that alpha numeric is 62 instead of 63. If only we had half a letter more in the alphabet. Expand Me// Entirely untested and thrown together. But illustrates the idea.
static void GenerateHexadecimal(Span<char> input)
{
Span<char> remaining = input;
Span<byte> randomBuffer = stackalloc byte[128];
int randomOffset = randomBuffer.Length;
while (remaining.Length >= 2)
{
if (randomOffset >= randomBuffer.Length)
{
// The offset should never overreach the buffer.
Debug.Assert(randomOffset == randomBuffer.Length);
RandomNumberGenerator.Fill(randomBuffer);
randomOffset = 0;
}
// Encode whichever is less, how much random data we have in the buffer, or how much is remaining to generate.
int encode = Math.Min(remaining.Length / 2, randomBuffer.Length - randomOffset);
HexConverter.EncodeToUtf16(randomBuffer.Slice(randomOffset, encode), remaining, HexConverter.Casing.Upper);
remaining = remaining.Slice(encode * 2);
randomOffset += encode;
}
if (!remaining.IsEmpty)
{
Debug.Assert(remaining.Length == 1);
if (randomOffset >= randomBuffer.Length)
{
// Only need one byte.
RandomNumberGenerator.Fill(randomBuffer.Slice(0, 1));
randomOffset = 0;
}
remaining[0] = HexConverter.ToCharUpper(randomBuffer[randomOffset]);
}
}
"Not random" wasn't a good choice of words for me but I think you and I agree on it, at least I don't have a use case for it. I updated the proposal. |
Valid concern, but I think this is ok. It's not really any different than The only real feedback I have on the API is FWIW, the base |
Okay. That's.. reasonable. Dropped. |
namespace System.Security.Cryptography {
public partial class RandomNumberGenerator {
// Proposed:
public static void GetItems<T>(ReadOnlySpan<T> choices, Span<T> destination);
public static T[] GetItems<T>(ReadOnlySpan<T> choices, int length);
public static string GetString(ReadOnlySpan<char> choices, int length);
// Optional helpers:
// Accelerator where 'choices' is 0-9A-F.
public static void GetHexString(Span<char> destination, bool lowercase=false);
public static string GetHexString(int stringLength, bool lowercase=false);
public static void Shuffle<T>(Span<T> values);
}
}
namespace System
{
partial class Random
{
public void GetItems<T>(ReadOnlySpan<T> choices, Span<T> destination);
public T[] GetItems<T>(T[] choices, int length);
public T[] GetItems<T>(ReadOnlySpan<T> choices, int length);
public void Shuffle<T>(Span<T> values);
public void Shuffle<T>(T[] values);
}
} |
Background and motivation
We have primitives of random number generation on
RandomNumberGenerator
. I would propose expanding some of the ways we have help developers create sufficiently random data in other forms, including strings.While
RandomNumberGenerator
has gotten more ergonomic to use to efficiently create random data, developers may choose to user other APIs that appear to create random data because of the simpler API surface, likeGuid.NewGuid()
. Personally I discourage the use ofGuid.NewGuid()
for cryptographic randomness purposes. However, the alternative goes from a simple expression to "do it yourself".API Proposal
I propose introducing a new static class,
RandomDataGenerator
. It provides static methods to create random sequences of data, either by returning an array or filling a buffer.Out of scope:
API Usage
Alternative Designs
I do have some thoughts about the proposal:
What if
T
is a mutable struct or reference type? The choice could be modified after the random data has been generated, thus going through the generated data. Is that simply "caller beware"?Many people think of randomness in terms of "bits". Consider's ruby's
SecureRandom
:r
's length is going to be 32 because the parameter is not the length of the return value, it's the number of bytes.The API above proposed returns the length of the sequence because it's generic: it's intended to work on any character set or generic items.
That leaves me thinking there are two different, but related needs here. Random bytes in different formats (hex, base64) and creating random things.
Risks
No response
The text was updated successfully, but these errors were encountered: