(refactor): not null when attribute implementation

This commit is contained in:
Maksym Sadovnychyy 2024-11-23 22:38:43 +01:00
parent 594789f44e
commit 6153963f87
14 changed files with 171 additions and 50 deletions

View File

@ -1,6 +1,6 @@
using System; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Threading;
namespace MaksIT.Core; namespace MaksIT.Core;
@ -15,7 +15,7 @@ public static class Culture {
/// <param name="culture">The culture to set. If null or empty, the invariant culture is used.</param> /// <param name="culture">The culture to set. If null or empty, the invariant culture is used.</param>
/// <param name="errorMessage">The error message if the operation fails.</param> /// <param name="errorMessage">The error message if the operation fails.</param>
/// <returns>True if the operation was successful; otherwise, false.</returns> /// <returns>True if the operation was successful; otherwise, false.</returns>
public static bool TrySet(string? culture, out string? errorMessage) { public static bool TrySet(string? culture, [NotNullWhen(false)] out string? errorMessage) {
try { try {
var threadCulture = CultureInfo.InvariantCulture; var threadCulture = CultureInfo.InvariantCulture;

View File

@ -1,6 +1,7 @@
using System; using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace MaksIT.Core; namespace MaksIT.Core;
/// <summary> /// <summary>
@ -15,7 +16,7 @@ public static class EnvVar {
/// <param name="newPath">The new path to add.</param> /// <param name="newPath">The new path to add.</param>
/// <param name="errorMessage">The error message if the operation fails.</param> /// <param name="errorMessage">The error message if the operation fails.</param>
/// <returns>True if the operation was successful; otherwise, false.</returns> /// <returns>True if the operation was successful; otherwise, false.</returns>
public static bool TryAddToPath(string newPath, out string? errorMessage) { public static bool TryAddToPath(string newPath, [NotNullWhen(false)] out string? errorMessage) {
try { try {
var pathEnvVar = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; var pathEnvVar = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
char separator = IsWindows ? ';' : ':'; char separator = IsWindows ? ';' : ':';
@ -42,7 +43,7 @@ public static class EnvVar {
/// <param name="envTarget">The target of the environment variable (machine, user, process).</param> /// <param name="envTarget">The target of the environment variable (machine, user, process).</param>
/// <param name="errorMessage">The error message if the operation fails.</param> /// <param name="errorMessage">The error message if the operation fails.</param>
/// <returns>True if the operation was successful; otherwise, false.</returns> /// <returns>True if the operation was successful; otherwise, false.</returns>
public static bool TrySet(string envName, string envValue, string envTarget, out string? errorMessage) { public static bool TrySet(string envName, string envValue, string envTarget, [NotNullWhen(false)] out string? errorMessage) {
try { try {
EnvironmentVariableTarget target = GetEnvironmentVariableTarget(envTarget); EnvironmentVariableTarget target = GetEnvironmentVariableTarget(envTarget);
if (target == EnvironmentVariableTarget.Machine && !IsWindows) { if (target == EnvironmentVariableTarget.Machine && !IsWindows) {
@ -66,7 +67,7 @@ public static class EnvVar {
/// <param name="envTarget">The target of the environment variable (machine, user, process).</param> /// <param name="envTarget">The target of the environment variable (machine, user, process).</param>
/// <param name="errorMessage">The error message if the operation fails.</param> /// <param name="errorMessage">The error message if the operation fails.</param>
/// <returns>True if the operation was successful; otherwise, false.</returns> /// <returns>True if the operation was successful; otherwise, false.</returns>
public static bool TryUnSet(string envName, string envTarget, out string? errorMessage) { public static bool TryUnSet(string envName, string envTarget, [NotNullWhen(false)] out string? errorMessage) {
try { try {
EnvironmentVariableTarget target = GetEnvironmentVariableTarget(envTarget); EnvironmentVariableTarget target = GetEnvironmentVariableTarget(envTarget);
if (target == EnvironmentVariableTarget.Machine && !IsWindows) { if (target == EnvironmentVariableTarget.Machine && !IsWindows) {

View File

@ -1,6 +1,8 @@
using System.Runtime.InteropServices; using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using MaksIT.Core.Extensions; using MaksIT.Core.Extensions;
namespace MaksIT.Core; namespace MaksIT.Core;
/// <summary> /// <summary>
@ -18,7 +20,7 @@ public static class FileSystem {
/// <param name="overwrite">Whether to overwrite existing files.</param> /// <param name="overwrite">Whether to overwrite existing files.</param>
/// <param name="errorMessage">The error message if the operation fails.</param> /// <param name="errorMessage">The error message if the operation fails.</param>
/// <returns>True if the copy operation was successful; otherwise, false.</returns> /// <returns>True if the copy operation was successful; otherwise, false.</returns>
public static bool TryCopyToFolder(string sourcePath, string destDirPath, bool overwrite, out string? errorMessage) { public static bool TryCopyToFolder(string sourcePath, string destDirPath, bool overwrite, [NotNullWhen(false)] out string? errorMessage) {
try { try {
if (!Directory.Exists(destDirPath)) { if (!Directory.Exists(destDirPath)) {
Directory.CreateDirectory(destDirPath); Directory.CreateDirectory(destDirPath);
@ -58,7 +60,7 @@ public static class FileSystem {
/// <param name="itemPath">File or directory path.</param> /// <param name="itemPath">File or directory path.</param>
/// <param name="errorMessage">The error message if the operation fails.</param> /// <param name="errorMessage">The error message if the operation fails.</param>
/// <returns>True if the delete operation was successful; otherwise, false.</returns> /// <returns>True if the delete operation was successful; otherwise, false.</returns>
public static bool TryDeleteFileOrDirectory(string itemPath, out string? errorMessage) { public static bool TryDeleteFileOrDirectory(string itemPath, [NotNullWhen(false)] out string? errorMessage) {
try { try {
if (File.Exists(itemPath)) { if (File.Exists(itemPath)) {
File.Delete(itemPath); File.Delete(itemPath);
@ -169,7 +171,7 @@ public static class FileSystem {
var count = 1; var count = 1;
while (File.Exists(newFullPath)) { while (File.Exists(newFullPath)) {
var tempFileName = $"{fileNameOnly}({count++})"; var tempFileName = $"{fileNameOnly}({count++})";
newFullPath = Path.Combine(path, tempFileName + extension); newFullPath = Path.Combine(path, $"{tempFileName}{extension}");
} }
return newFullPath; return newFullPath;

View File

@ -8,7 +8,7 @@
<!-- NuGet package metadata --> <!-- NuGet package metadata -->
<PackageId>MaksIT.Core</PackageId> <PackageId>MaksIT.Core</PackageId>
<Version>1.1.7</Version> <Version>1.1.8</Version>
<Authors>Maksym Sadovnychyy</Authors> <Authors>Maksym Sadovnychyy</Authors>
<Company>MAKS-IT</Company> <Company>MAKS-IT</Company>
<Product>MaksIT.Core</Product> <Product>MaksIT.Core</Product>

View File

@ -1,6 +1,8 @@
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Diagnostics.CodeAnalysis;
namespace MaksIT.Core.Networking; namespace MaksIT.Core.Networking;
@ -15,7 +17,11 @@ public static class PingPort {
/// <param name="portNumber">The port number.</param> /// <param name="portNumber">The port number.</param>
/// <param name="errorMessage">The error message if the operation fails.</param> /// <param name="errorMessage">The error message if the operation fails.</param>
/// <returns>True if the host is reachable on the specified port; otherwise, false.</returns> /// <returns>True if the host is reachable on the specified port; otherwise, false.</returns>
public static bool TryHostPort(string hostUri, int portNumber, out string? errorMessage) { public static bool TryHostPort(
string hostUri,
int portNumber,
[NotNullWhen(false)] out string? errorMessage
) {
if (string.IsNullOrEmpty(hostUri)) { if (string.IsNullOrEmpty(hostUri)) {
errorMessage = "Host URI cannot be null or empty."; errorMessage = "Host URI cannot be null or empty.";
return false; return false;
@ -53,7 +59,11 @@ public static class PingPort {
/// <param name="portNumber">The port number.</param> /// <param name="portNumber">The port number.</param>
/// <param name="errorMessage">The error message if the operation fails.</param> /// <param name="errorMessage">The error message if the operation fails.</param>
/// <returns>True if the host is reachable on the specified port; otherwise, false.</returns> /// <returns>True if the host is reachable on the specified port; otherwise, false.</returns>
public static bool TryUDPPort(string hostUri, int portNumber, out string? errorMessage) { public static bool TryUDPPort(
string hostUri,
int portNumber,
[NotNullWhen(false)] out string? errorMessage
) {
if (string.IsNullOrEmpty(hostUri)) { if (string.IsNullOrEmpty(hostUri)) {
errorMessage = "Host URI cannot be null or empty."; errorMessage = "Host URI cannot be null or empty.";
return false; return false;

View File

@ -1,7 +1,8 @@
using Microsoft.Extensions.Logging; using System.Diagnostics.CodeAnalysis;
using System;
using System.Net; using System.Net;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;
namespace MaksIT.Core.Networking.Windows; namespace MaksIT.Core.Networking.Windows;
@ -15,11 +16,12 @@ public class NetworkConnection : IDisposable {
} }
public static bool TryCreate( public static bool TryCreate(
ILogger<NetworkConnection> logger, ILogger<NetworkConnection> logger,
string networkName, string networkName,
NetworkCredential credentials, NetworkCredential credentials,
out NetworkConnection? networkConnection, [NotNullWhen(true)] out NetworkConnection? networkConnection,
out string? errorMessage) { [NotNullWhen(false)] out string? errorMessage
) {
try { try {
if (!OperatingSystem.IsWindows()) { if (!OperatingSystem.IsWindows()) {
throw new PlatformNotSupportedException("NetworkConnection is only supported on Windows."); throw new PlatformNotSupportedException("NetworkConnection is only supported on Windows.");
@ -81,7 +83,7 @@ public class NetworkConnection : IDisposable {
public ResourceDisplayType DisplayType; public ResourceDisplayType DisplayType;
public int Usage; public int Usage;
public string? LocalName; public string? LocalName;
public string RemoteName; public string? RemoteName;
public string? Comment; public string? Comment;
public string? Provider; public string? Provider;
} }

View File

@ -1,5 +1,7 @@
using MaksIT.Core.Extensions; using MaksIT.Core.Extensions;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
namespace MaksIT.Core; namespace MaksIT.Core;
@ -27,7 +29,7 @@ public static class Processes {
/// <param name="silent">If true, the process will be started without creating a window.</param> /// <param name="silent">If true, the process will be started without creating a window.</param>
/// <param name="errorMessage">The error message if the operation fails.</param> /// <param name="errorMessage">The error message if the operation fails.</param>
/// <returns>True if the process started successfully; otherwise, false.</returns> /// <returns>True if the process started successfully; otherwise, false.</returns>
public static bool TryStart(string fileName, string arguments, int timeout, bool silent, out string? errorMessage) { public static bool TryStart(string fileName, string arguments, int timeout, bool silent, [NotNullWhen(false)] out string? errorMessage) {
try { try {
var processInfo = new ProcessStartInfo(fileName) { var processInfo = new ProcessStartInfo(fileName) {
Arguments = arguments, Arguments = arguments,
@ -60,7 +62,7 @@ public static class Processes {
/// <param name="process">Process name. Accepts wildcards '*' or '?'</param> /// <param name="process">Process name. Accepts wildcards '*' or '?'</param>
/// <param name="errorMessage">The error message if the operation fails.</param> /// <param name="errorMessage">The error message if the operation fails.</param>
/// <returns>True if at least one process was killed successfully; otherwise, false.</returns> /// <returns>True if at least one process was killed successfully; otherwise, false.</returns>
public static bool TryKill(string process, out string? errorMessage) { public static bool TryKill(string process, [NotNullWhen(false)] out string? errorMessage) {
bool success = false; bool success = false;
errorMessage = null; errorMessage = null;
foreach (var proc in System.Diagnostics.Process.GetProcesses()) { foreach (var proc in System.Diagnostics.Process.GetProcesses()) {

View File

@ -1,4 +1,6 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Diagnostics.CodeAnalysis;
namespace MaksIT.Core.Security; namespace MaksIT.Core.Security;
@ -9,7 +11,12 @@ public static class AESGCMUtility {
private const int IvLength = 12; // 12 bytes for AES-GCM IV private const int IvLength = 12; // 12 bytes for AES-GCM IV
private const int TagLength = 16; // 16 bytes for AES-GCM Tag private const int TagLength = 16; // 16 bytes for AES-GCM Tag
public static bool TryEncryptData(byte[] data, string base64Key, out byte[]? result, out string? errorMessage) { public static bool TryEncryptData(
byte[] data,
string base64Key,
[NotNullWhen(true)] out byte[]? result,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
var key = Convert.FromBase64String(base64Key); var key = Convert.FromBase64String(base64Key);
using (AesGcm aesGcm = new AesGcm(key, AesGcm.TagByteSizes.MaxSize)) { using (AesGcm aesGcm = new AesGcm(key, AesGcm.TagByteSizes.MaxSize)) {
@ -38,7 +45,12 @@ public static class AESGCMUtility {
} }
} }
public static bool TryDecryptData(byte[] data, string base64Key, out byte[]? decryptedData, out string? errorMessage) { public static bool TryDecryptData(
byte[] data,
string base64Key,
[NotNullWhen(true)] out byte[]? decryptedData,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
var key = Convert.FromBase64String(base64Key); var key = Convert.FromBase64String(base64Key);

View File

@ -1,7 +1,6 @@
using System; using System.Text;
using System.Collections.Generic; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
namespace MaksIT.Core.Security; namespace MaksIT.Core.Security;
@ -9,7 +8,11 @@ public static class Base32Encoder {
private static readonly char[] Base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".ToCharArray(); private static readonly char[] Base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".ToCharArray();
private const string PaddingChar = "="; private const string PaddingChar = "=";
public static bool TryEncode(byte[] data, out string? encoded, out string? errorMessage) { public static bool TryEncode(
byte[] data,
[NotNullWhen(true)] out string? encoded,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
if (data == null || data.Length == 0) { if (data == null || data.Length == 0) {
throw new ArgumentNullException(nameof(data)); throw new ArgumentNullException(nameof(data));
@ -57,7 +60,11 @@ public static class Base32Encoder {
} }
} }
public static bool TryDecode(string base32, out byte[]? decoded, out string? errorMessage) { public static bool TryDecode(
string base32,
[NotNullWhen(true)] out byte[]? decoded,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
if (string.IsNullOrEmpty(base32)) { if (string.IsNullOrEmpty(base32)) {
throw new ArgumentNullException(nameof(base32)); throw new ArgumentNullException(nameof(base32));

View File

@ -1,7 +1,14 @@
namespace MaksIT.Core.Security; using System.Diagnostics.CodeAnalysis;
namespace MaksIT.Core.Security;
public static class ChecksumUtility { public static class ChecksumUtility {
public static bool TryCalculateCRC32Checksum(byte[] data, out string? checksum, out string? errorMessage) { public static bool TryCalculateCRC32Checksum(
byte[] data,
[NotNullWhen(true)] out string? checksum,
[NotNullWhen(false)] out string? errorMessage
) {
if (Crc32.TryCompute(data, out var result, out errorMessage)) { if (Crc32.TryCompute(data, out var result, out errorMessage)) {
checksum = BitConverter.ToString(BitConverter.GetBytes(result)).Replace("-", "").ToLower(); checksum = BitConverter.ToString(BitConverter.GetBytes(result)).Replace("-", "").ToLower();
return true; return true;
@ -26,7 +33,12 @@ public static class ChecksumUtility {
} }
} }
public static bool TryCalculateCRC32ChecksumFromFileInChunks(string filePath, out string? checksum, out string? errorMessage, int chunkSize = 8192) { public static bool TryCalculateCRC32ChecksumFromFileInChunks(
string filePath,
[NotNullWhen(true)] out string? checksum,
[NotNullWhen(false)] out string? errorMessage,
int chunkSize = 8192
) {
try { try {
using var crc32 = new Crc32(); using var crc32 = new Crc32();
using var stream = File.OpenRead(filePath); using var stream = File.OpenRead(filePath);

View File

@ -1,4 +1,5 @@
using System.Security.Cryptography; using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
namespace MaksIT.Core.Security; namespace MaksIT.Core.Security;
@ -37,7 +38,11 @@ public class Crc32 : HashAlgorithm {
public override int HashSize => 32; public override int HashSize => 32;
public static bool TryCompute(byte[] buffer, out uint result, out string? errorMessage) { public static bool TryCompute(
byte[] buffer,
out uint result,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
result = Compute(DefaultPolynomial, DefaultSeed, buffer); result = Compute(DefaultPolynomial, DefaultSeed, buffer);
errorMessage = null; errorMessage = null;
@ -50,7 +55,12 @@ public class Crc32 : HashAlgorithm {
} }
} }
public static bool TryCompute(uint seed, byte[] buffer, out uint result, out string? errorMessage) { public static bool TryCompute(
uint seed,
byte[] buffer,
out uint result,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
result = Compute(DefaultPolynomial, seed, buffer); result = Compute(DefaultPolynomial, seed, buffer);
errorMessage = null; errorMessage = null;
@ -63,7 +73,13 @@ public class Crc32 : HashAlgorithm {
} }
} }
public static bool TryCompute(uint polynomial, uint seed, byte[] buffer, out uint result, out string? errorMessage) { public static bool TryCompute(
uint polynomial,
uint seed,
byte[] buffer,
out uint result,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
result = Compute(polynomial, seed, buffer); result = Compute(polynomial, seed, buffer);
errorMessage = null; errorMessage = null;

View File

@ -3,6 +3,7 @@ using System.Security.Claims;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.Diagnostics.CodeAnalysis;
namespace MaksIT.Core.Security; namespace MaksIT.Core.Security;
@ -14,7 +15,16 @@ public class JWTTokenClaims {
} }
public static class JwtGenerator { public static class JwtGenerator {
public static bool TryGenerateToken(string secret, string issuer, string audience, double expiration, string username, List<string> roles, out (string, JWTTokenClaims)? tokenData, out string? errorMessage) { public static bool TryGenerateToken(
string secret,
string issuer,
string audience,
double expiration,
string username,
List<string> roles,
[NotNullWhen(true)] out (string, JWTTokenClaims)? tokenData,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
var secretKey = GetSymmetricSecurityKey(secret); var secretKey = GetSymmetricSecurityKey(secret);
var credentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256); var credentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
@ -62,7 +72,14 @@ public static class JwtGenerator {
public static string GenerateSecret(int keySize = 32) => Convert.ToBase64String(GetRandomBytes(keySize)); public static string GenerateSecret(int keySize = 32) => Convert.ToBase64String(GetRandomBytes(keySize));
public static bool TryValidateToken(string secret, string issuer, string audience, string token, out JWTTokenClaims? tokenClaims, out string? errorMessage) { public static bool TryValidateToken(
string secret,
string issuer,
string audience,
string token,
out JWTTokenClaims? tokenClaims,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
var key = Encoding.UTF8.GetBytes(secret); var key = Encoding.UTF8.GetBytes(secret);
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();

View File

@ -1,4 +1,5 @@
using System.Security.Cryptography; using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation; using Microsoft.AspNetCore.Cryptography.KeyDerivation;
namespace MaksIT.Core.Security; namespace MaksIT.Core.Security;
@ -23,7 +24,11 @@ public static class PasswordHasher {
return Convert.ToBase64String(valueBytes); return Convert.ToBase64String(valueBytes);
} }
public static bool TryCreateSaltedHash(string value, out (string Salt, string Hash)? saltedHash, out string? errorMessage) { public static bool TryCreateSaltedHash(
string value,
[NotNullWhen(true)] out (string Salt, string Hash)? saltedHash,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
var saltBytes = CreateSaltBytes(); var saltBytes = CreateSaltBytes();
var hash = CreateHash(value, saltBytes); var hash = CreateHash(value, saltBytes);
@ -40,7 +45,13 @@ public static class PasswordHasher {
} }
} }
public static bool TryValidateHash(string value, string salt, string hash, out bool isValid, out string? errorMessage) { public static bool TryValidateHash(
string value,
string salt,
string hash,
[NotNullWhen(true)] out bool isValid,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
var saltBytes = Convert.FromBase64String(salt); var saltBytes = Convert.FromBase64String(salt);
var hashToCompare = CreateHash(value, saltBytes); var hashToCompare = CreateHash(value, saltBytes);

View File

@ -1,4 +1,5 @@
using System.Security.Cryptography; using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
namespace MaksIT.Core.Security; namespace MaksIT.Core.Security;
@ -6,7 +7,13 @@ public static class TotpGenerator {
private const int Timestep = 30; // Time step in seconds (standard is 30 seconds) private const int Timestep = 30; // Time step in seconds (standard is 30 seconds)
private const int TotpDigits = 6; // Standard TOTP length is 6 digits private const int TotpDigits = 6; // Standard TOTP length is 6 digits
public static bool TryValidate(string totpCode, string base32Secret, int timeTolerance, out bool isValid, out string? errorMessage) { public static bool TryValidate(
string totpCode,
string base32Secret,
int timeTolerance,
out bool isValid,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
// Convert the Base32 encoded secret to a byte array // Convert the Base32 encoded secret to a byte array
if (!Base32Encoder.TryDecode(base32Secret, out byte[]? secretBytes, out errorMessage)) { if (!Base32Encoder.TryDecode(base32Secret, out byte[]? secretBytes, out errorMessage)) {
@ -38,7 +45,12 @@ public static class TotpGenerator {
} }
} }
public static bool TryGenerate(string base32Secret, long timestep, out string? totpCode, out string? errorMessage) { public static bool TryGenerate(
string base32Secret,
long timestep,
[NotNullWhen(true)] out string? totpCode,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
// Convert the Base32 encoded secret to a byte array // Convert the Base32 encoded secret to a byte array
if (!Base32Encoder.TryDecode(base32Secret, out byte[]? secretBytes, out errorMessage)) { if (!Base32Encoder.TryDecode(base32Secret, out byte[]? secretBytes, out errorMessage)) {
@ -86,7 +98,10 @@ public static class TotpGenerator {
return unixTimestamp / Timestep; return unixTimestamp / Timestep;
} }
public static bool TryGenerateSecret(out string? secret, out string? errorMessage) { public static bool TryGenerateSecret(
[NotNullWhen(true)] out string? secret,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
// Example of generating a 32-character base32 secret for TOTP // Example of generating a 32-character base32 secret for TOTP
var random = new byte[20]; var random = new byte[20];
@ -108,7 +123,11 @@ public static class TotpGenerator {
} }
} }
public static bool TryGenerateRecoveryCodes(int defaultCodeCount, out List<string>? recoveryCodes, out string? errorMessage) { public static bool TryGenerateRecoveryCodes(
int defaultCodeCount,
[NotNullWhen(true)] out List<string>? recoveryCodes,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
recoveryCodes = new List<string>(); recoveryCodes = new List<string>();
@ -128,7 +147,17 @@ public static class TotpGenerator {
} }
} }
public static bool TryGenerateTotpAuthLink(string label, string username, string twoFactoSharedKey, string issuer, string? algorithm, int? digits, int? period, out string? authLink, out string? errorMessage) { public static bool TryGenerateTotpAuthLink(
string label,
string username,
string twoFactoSharedKey,
string issuer,
string? algorithm,
int? digits,
int? period,
[NotNullWhen(true)] out string? authLink,
[NotNullWhen(false)] out string? errorMessage
) {
try { try {
var queryParams = new List<string> { var queryParams = new List<string> {
$"secret={Uri.EscapeDataString(twoFactoSharedKey)}", $"secret={Uri.EscapeDataString(twoFactoSharedKey)}",