/// <summary> /// Returns secret as a Base32 string with custom formatting. /// It is up to the caller to secure given string. /// </summary> /// <param name="format">Format of Base32 string.</param> public string GetBase32Secret(SecretFormatFlags format) { this.UnprotectSecret(); try { return(ToBase32(this.SecretBuffer, this.SecretLength, format)); } finally { this.ProtectSecret(); } }
private static string ToBase32(byte[] bytes, int length, SecretFormatFlags format) { if (length == 0) { return(string.Empty); } var hasSpacing = (format & SecretFormatFlags.Spacing) == SecretFormatFlags.Spacing; var hasPadding = (format & SecretFormatFlags.Padding) == SecretFormatFlags.Padding; var isUpper = (format & SecretFormatFlags.Uppercase) == SecretFormatFlags.Uppercase; var bitLength = (length * 8); var textLength = bitLength / 5 + ((bitLength % 5) == 0 ? 0 : 1); var totalLength = textLength; var padLength = (textLength % 8 == 0) ? 0 : 8 - textLength % 8; totalLength += (hasPadding ? padLength : 0); var spaceLength = totalLength / 4 + ((totalLength % 4 == 0) ? -1 : 0); totalLength += (hasSpacing ? spaceLength : 0); var chars = new char[totalLength]; var index = 0; var bits = 0; var bitsRemaining = 0; for (int i = 0; i < length; i++) { bits = (bits << 8) | bytes[i]; bitsRemaining += 8; while (bitsRemaining >= 5) { var bitsIndex = (bits >> (bitsRemaining - 5)) & 0x1F; bitsRemaining -= 5; chars[index] = isUpper ? Base32Alphabet[bitsIndex] : char.ToLowerInvariant(Base32Alphabet[bitsIndex]); index++; if (hasSpacing && (index < chars.Length) && (bitsRemaining % 4 == 0)) { chars[index] = ' '; index++; } } } if (bitsRemaining > 0) { var bitsIndex = (bits & Base32Bitmask[bitsRemaining]) << (5 - bitsRemaining); chars[index] = isUpper ? Base32Alphabet[bitsIndex] : char.ToLowerInvariant(Base32Alphabet[bitsIndex]); index++; } if (hasPadding) { for (int i = 0; i < padLength; i++) { if (hasSpacing && (i % 4 == padLength % 4)) { chars[index] = ' '; index++; } chars[index] = '='; index++; } } return(new string(chars)); }