browserify-steam-totp
Version:
Steam-style generate totp codes by secret
153 lines • 12.3 kB
JavaScript
import { Buffer } from 'buffer';
import axios from 'axios';
import CryptoJS from 'crypto-js';
export class SteamAuthenticator {
_DIGITS = 5;
_PERIOD = 30;
_ALPHABET = '23456789BCDFGHJKMNPQRTVWXY';
/**
* Get the current time in seconds, optionally with a time offset
* @param timeOffset Time offset in seconds (optional)
* @returns The current time in seconds `number`
*/
getTime(timeOffset) {
return Math.floor(Date.now() / 1000) + (timeOffset || 0);
}
/**
* Generate a one-time authentication code (TOTP)
* @param secret The secret key as a string
* @param timeOffset Time offset in seconds (optional)
* @returns The generated one-time authentication code `string`
*/
generateAuthCode(secret, timeOffset = 0) {
const keyData = this.bufferizeSecret(secret);
const time = Math.floor(this.getTime(timeOffset) / this._PERIOD);
const buffer = Buffer.allocUnsafe(8);
buffer.writeUInt32BE(0, 0);
buffer.writeUint32BE(time, 4);
const hmac = CryptoJS.HmacSHA1(CryptoJS.lib.WordArray.create(buffer), keyData);
const hmacResult = CryptoJS.enc.Hex.parse(hmac.toString());
const hmacBytes = [];
for (let i = 0; i < hmacResult.words.length * 4; i++) {
hmacBytes.push((hmacResult.words[i >> 2] >> (24 - (i % 4) * 8)) & 0xff);
}
const start = hmacBytes[hmacBytes.length - 1] & 0x0f;
let material = ((hmacBytes[start] & 0x7f) << 24) |
((hmacBytes[start + 1] & 0xff) << 16) |
((hmacBytes[start + 2] & 0xff) << 8) |
(hmacBytes[start + 3] & 0xff);
let code = '';
for (let i = 0; i < this._DIGITS; i++) {
code += this._ALPHABET[material % this._ALPHABET.length];
material = Math.floor(material / this._ALPHABET.length);
}
return code;
}
/**
* Generate a confirmation key for Steam
* @param identitySecret The secret key for Steam authentication
* @param time Unix time in seconds
* @param tag The tag for the key (e.g., 'conf')
* @returns The confirmation key `string`
*/
generateConfirmationKey(identitySecret, time, tag) {
let dataLen = 8;
if (tag) {
dataLen += tag.length > 32 ? 32 : tag.length;
}
const buffer = Buffer.allocUnsafe(dataLen);
if (typeof buffer.writeBigUInt64BE === 'function') {
buffer.writeBigUInt64BE(BigInt(time), 0);
}
else {
buffer.writeUInt32BE(0, 0);
buffer.writeUInt32BE(time, 4);
}
if (tag) {
buffer.write(tag, 8);
}
const hmac = CryptoJS.HmacSHA1(CryptoJS.lib.WordArray.create(buffer), CryptoJS.lib.WordArray.create(Buffer.from(identitySecret, 'base64')));
return CryptoJS.enc.Base64.stringify(hmac);
}
/**
* Generate the device ID based on the SteamID
* @param steamid The user's SteamID
* @returns The device ID `string`
*/
getDeviceId(steamid) {
const hash = CryptoJS.SHA1(steamid.toString())
.toString(CryptoJS.enc.Hex)
.replace(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12}).*$/, '$1-$2-$3-$4-$5');
return `android:${hash}`;
}
/**
* Asynchronously get the time offset from Steam servers
* @returns An object with the time offset and the request time
*/
async getTimeOffsetAsync() {
try {
const start = Date.now();
const response = await axios.post('https://api.steampowered.com/ITwoFactorService/QueryTime/v1/', null, {
headers: {
'Content-Length': '0',
},
});
const data = response.data.response;
if (!data || !data.server_time) {
throw new Error('Malformed response');
}
const end = Date.now();
const offset = data.server_time - this.getTime();
return {
offset,
time: end - start,
};
}
catch (error) {
throw new Error(error.message || 'Request failed');
}
}
/**
* Get the time offset from Steam servers using a callback
* @param callback The callback function to return the error or result
*/
getTimeOffset(callback) {
const start = Date.now();
axios
.post('https://api.steampowered.com/ITwoFactorService/QueryTime/v1/', null, {
headers: {
'Content-Length': '0',
},
})
.then((response) => {
const data = response.data.response;
if (!data || !data.server_time) {
return callback(new Error('Malformed response'));
}
const end = Date.now();
const offset = data.server_time - this.getTime();
callback(null, offset, end - start);
})
.catch((error) => {
callback(new Error(error.message || 'Request failed'));
});
}
/**
* Convert the secret string into a format that can be used by CryptoJS
* @param secret The secret string (in hex or base64)
* @returns The parsed secret as a CryptoJS `WordArray`
*/
bufferizeSecret(secret) {
if (typeof secret === 'string') {
if (secret.match(/[0-9a-f]{40}/i)) {
return CryptoJS.enc.Hex.parse(secret);
}
else {
return CryptoJS.enc.Base64.parse(secret);
}
}
return secret;
}
}
export const SteamTotp = new SteamAuthenticator();
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RlYW10b3RwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi9zdGVhbXRvdHAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUVoQyxPQUFPLEtBQUssTUFBTSxPQUFPLENBQUM7QUFDMUIsT0FBTyxRQUFRLE1BQU0sV0FBVyxDQUFDO0FBRWpDLE1BQU0sT0FBTyxrQkFBa0I7SUFDWixPQUFPLEdBQUcsQ0FBQyxDQUFDO0lBQ1osT0FBTyxHQUFHLEVBQUUsQ0FBQztJQUNiLFNBQVMsR0FBRyw0QkFBNEIsQ0FBQztJQUUxRDs7OztPQUlHO0lBQ0gsT0FBTyxDQUFDLFVBQW1CO1FBQ3pCLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFVLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDM0QsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsZ0JBQWdCLENBQUMsTUFBYyxFQUFFLFVBQVUsR0FBRyxDQUFDO1FBQzdDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFN0MsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUVqRSxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3JDLE1BQU0sQ0FBQyxhQUFhLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQzNCLE1BQU0sQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBRTlCLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxRQUFRLENBQzVCLFFBQVEsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFDckMsT0FBTyxDQUNSLENBQUM7UUFDRixNQUFNLFVBQVUsR0FBRyxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFFM0QsTUFBTSxTQUFTLEdBQUcsRUFBRSxDQUFDO1FBQ3JCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxVQUFVLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDcEQsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUM7U0FDekU7UUFFRCxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsU0FBUyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUM7UUFFckQsSUFBSSxRQUFRLEdBQ1YsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDakMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ3JDLENBQUMsQ0FBQyxTQUFTLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNwQyxDQUFDLFNBQVMsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUM7UUFFaEMsSUFBSSxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ2QsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLEVBQUUs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