Steam doesn't show letters, just numbers
Hi, It seems that codes displayed for Steam are showing 5 digits rather than 5 alphanumeric characters. A steam code should have both a mixture of letters and numbers.
I use WinAuth for my emergency backup portable 2FA solution. It does support Steam 2FA and successfully shows alphanumeric instead of all just numbers.
-I would recommend looking at the WinAuth source code to see how the author successfully decodes Steam 2FA to show alphanumeric characters for Steam URIs.
-In order to decipher Steam OTP's, WinAuth requires &issuer=Steam in the OTPAuth URI. Please consider appending this to anything exported from Steam (I did it manually... not a big deal. Just would add support for others using WinAuth)
Code for turning the 5 numbers into the proper 5 alphanumeric for steam in these files: https://github.com/winauth/winauth/blob/master/Authenticator/SteamAuthenticator.cs https://github.com/winauth/winauth/blob/master/Authenticator/SteamClient.cs
Or these projects: https://github.com/Jessecar96/SteamDesktopAuthenticator https://github.com/winauth/winauth/
To be honest, the TOTP code generation section of the webpage was just something I added to quickly verify that the codes work. Steam uses a non-standard alphabet for their TOTP codes and it seems easier to outright remove it but if you can find a way to either hack otplib to generate Steam codes or to implement HMAC-SHA1 and the simple HOTP generation code in JavaScript and just generate them without otplib, either would work.
Although I could also just statically generate a bunch of codes per app in Python, since this is a relatively simple change and the codes are glanced at for two periods at most. Maybe I'll give that a try.
I've tried to dig through the above files to find the code for converting the numbers to letters Hopefully this helps.
/// Character set for authenticator code
/// </summary>
private static char[] STEAMCHARS = new char[] {
'2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C',
'D', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',
'R', 'T', 'V', 'W', 'X', 'Y'};
HMac hmac = new HMac(new Sha1Digest());
hmac.Init(new KeyParameter(SecretKey));
byte[] codeIntervalArray = BitConverter.GetBytes(CodeInterval);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(codeIntervalArray);
}
hmac.BlockUpdate(codeIntervalArray, 0, codeIntervalArray.Length);
byte[] mac = new byte[hmac.GetMacSize()];
hmac.DoFinal(mac, 0);
// the last 4 bits of the mac say where the code starts (e.g. if last 4 bit are 1100, we start at byte 12)
int start = mac[19] & 0x0f;
// extract those 4 bytes
byte[] bytes = new byte[4];
Array.Copy(mac, start, bytes, 0, 4);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(bytes);
}
uint fullcode = BitConverter.ToUInt32(bytes, 0) & 0x7fffffff;
// build the alphanumeric code
StringBuilder code = new StringBuilder();
for (var i=0; i<CODE_DIGITS; i++)
{
code.Append(STEAMCHARS[fullcode % STEAMCHARS.Length]);
fullcode /= (uint)STEAMCHARS.Length;
}
return code.ToString();
}