/// <summary> /// Creates a secret key with the specified number of bits of entropy and specified /// <see cref="CryptoSecureRequirement"/> to be shared with the user on wich the future valid TOTP codes will /// be based. /// </summary> /// <param name="bits">The number of bits of entropy to use.</param> /// <param name="cryptoSecureRequirement">The <see cref="CryptoSecureRequirement"/> to ensure cryptographically secure RNG's.</param> /// <returns> /// Returns a string of random values in 'Base32 alphabet' to be shared with the user / stored with the account. /// </returns> /// <exception cref="CryptographicException"> /// Thrown when the <see cref="IRngProvider"/> of the instance is not cryptographically secure and the /// <see cref="CryptoSecureRequirement"/> requires a cryptographically secure RNG. /// </exception> public string CreateSecret(int bits, CryptoSecureRequirement cryptoSecureRequirement) { if (cryptoSecureRequirement == CryptoSecureRequirement.RequireSecure && !RngProvider.IsCryptographicallySecure) { throw new CryptographicException("RNG provider is not cryptographically secure"); } int bytes = (int)Math.Ceiling((double)bits / 5); // We use 5 bits of each byte (since we have a // 32-character 'alphabet' / base32) // Note that we DO NOT actually "base32 encode" the random bytes, we simply take 5 bits from each random // byte and map these directly to letters from the base32 alphabet (effectively 'base32 encoding on the fly'). return(string.Concat(RngProvider.GetRandomBytes(bytes).Select(v => Base32.Base32Alphabet[v & 31]))); }
/// <summary> /// Creates a secret key with the specified number of bits of entropy and specified /// <see cref="CryptoSecureRequirement"/> to be shared with the user on wich the future valid TOTP codes will /// be based. /// </summary> /// <param name="bits">The number of bits of entropy to use.</param> /// <param name="cryptoSecureRequirement">The <see cref="CryptoSecureRequirement"/> to ensure cryptographically secure RNG's.</param> /// <returns> /// Returns a string of random values in 'Base32 alphabet' to be shared with the user / stored with the account. /// </returns> /// <exception cref="CryptographicException"> /// Thrown when the <see cref="IRngProvider"/> of the instance is not cryptographically secure and the /// <see cref="CryptoSecureRequirement"/> requires a cryptographically secure RNG. /// </exception> public string CreateSecret(int bits, CryptoSecureRequirement cryptoSecureRequirement) { if (cryptoSecureRequirement == CryptoSecureRequirement.RequireSecure && !this.RngProvider.IsCryptographicallySecure) throw new CryptographicException("RNG provider is not cryptographically secure"); int bytes = (int)Math.Ceiling((double)bits / 5); // We use 5 bits of each byte (since we have a // 32-character 'alphabet' / base32) // Note that we DO NOT actually "base32 encode" the random bytes, we simply take 5 bits from each random // byte and map these directly to letters from the base32 alphabet (effectively 'base32 encoding on the fly'). return string.Concat(this.RngProvider.GetRandomBytes(bytes).Select(v => Base32.Base32Alphabet[v & 31])); }