Skip to content

Instantly share code, notes, and snippets.

@Ne3tCode
Created June 20, 2023 18:56
Show Gist options
  • Save Ne3tCode/fc43e0211c04c5f1b8a345272bd89b8c to your computer and use it in GitHub Desktop.
Save Ne3tCode/fc43e0211c04c5f1b8a345272bd89b8c to your computer and use it in GitHub Desktop.
[UserScript] Steam Mobile Confirmations
// ==UserScript==
// @name Steam Community Mobile Trade Confirmations
// @namespace www.doctormckay.com
// @description Enables mobile trade confirmations in the web browser
// @match https://steamcommunity.com/mobileconf/conf*
// @version 1.2.0
// @author DoctorMcKay, Nephrite
// @grant none
// @run-at document-end
// ==/UserScript==
/* eslint-disable no-redeclare */
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
// http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js
// core
var CryptoJS=CryptoJS||function(g,l){var e={},d=e.lib={},m=function(){},k=d.Base={extend:function(a){m.prototype=this;var c=new m;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
p=d.WordArray=k.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=l?c:4*a.length},toString:function(a){return(a||n).stringify(this)},concat:function(a){var c=this.words,q=a.words,f=this.sigBytes;a=a.sigBytes;this.clamp();if(f%4)for(var b=0;b<a;b++)c[f+b>>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535<q.length)for(b=0;b<a;b+=4)c[f+b>>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=g.ceil(c/4)},clone:function(){var a=k.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b<a;b+=4)c.push(4294967296*g.random()|0);return new p.init(c,a)}}),b=e.enc={},n=b.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++){var d=c[f>>>2]>>>24-8*(f%4)&255;b.push((d>>>4).toString(16));b.push((d&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f+=2){b[f>>>3]|=parseInt(a.substr(f,
2),16)<<24-4*(f%8)};return new p.init(b,c/2)}},j=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++)b.push(String.fromCharCode(c[f>>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f++)b[f>>>2]|=(a.charCodeAt(f)&255)<<24-8*(f%4);return new p.init(b,c)}},h=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(j.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return j.parse(unescape(encodeURIComponent(a)))}},
r=d.BufferedBlockAlgorithm=k.extend({reset:function(){this._data=new p.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,f=c.sigBytes,d=this.blockSize,e=f/(4*d),e=a?g.ceil(e):g.max((e|0)-this._minBufferSize,0);a=e*d;f=g.min(4*a,f);if(a){for(var k=0;k<a;k+=d)this._doProcessBlock(b,k);k=b.splice(0,a);c.sigBytes-=f}return new p.init(k,f)},clone:function(){var a=k.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});d.Hasher=r.extend({cfg:k.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){r.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,d){return(new a.init(d)).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return(new s.HMAC.init(a,
d)).finalize(b)}}});var s=e.algo={};return e}(Math);
// sha1
(function(){var g=CryptoJS,l=g.lib,e=l.WordArray,d=l.Hasher,m=[],l=g.algo.SHA1=d.extend({_doReset:function(){this._hash=new e.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(d,e){for(var b=this._hash.words,n=b[0],j=b[1],h=b[2],g=b[3],l=b[4],a=0;80>a;a++){if(16>a)m[a]=d[e+a]|0;else{var c=m[a-3]^m[a-8]^m[a-14]^m[a-16];m[a]=c<<1|c>>>31}c=(n<<5|n>>>27)+l+m[a];c=20>a?c+((j&h|~j&g)+1518500249):40>a?c+((j^h^g)+1859775393):60>a?c+((j&h|j&g|h&g)-1894007588):c+((j^h^
g)-899497514);l=g;g=h;h=j<<30|j>>>2;j=n;n=c}b[0]=b[0]+n|0;b[1]=b[1]+j|0;b[2]=b[2]+h|0;b[3]=b[3]+g|0;b[4]=b[4]+l|0},_doFinalize:function(){var d=this._data,e=d.words,b=8*this._nDataBytes,g=8*d.sigBytes;e[g>>>5]|=128<<24-g%32;e[(g+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(g+64>>>9<<4)+15]=b;d.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=d.clone.call(this);e._hash=this._hash.clone();return e}});g.SHA1=d._createHelper(l);g.HmacSHA1=d._createHmacHelper(l)})();
// hmac
(function(){var g=CryptoJS,l=g.enc.Utf8;g.algo.HMAC=g.lib.Base.extend({init:function(e,d){e=this._hasher=new e.init;"string"==typeof d&&(d=l.parse(d));var g=e.blockSize,k=4*g;d.sigBytes>k&&(d=e.finalize(d));d.clamp();for(var p=this._oKey=d.clone(),b=this._iKey=d.clone(),n=p.words,j=b.words,h=0;h<g;h++){n[h]^=1549556828;j[h]^=909522486};p.sigBytes=b.sigBytes=k;this.reset()},reset:function(){var e=this._hasher;e.reset();e.update(this._iKey)},update:function(e){this._hasher.update(e);return this},finalize:function(e){var d=
this._hasher;e=d.finalize(e);d.reset();return d.finalize(this._oKey.clone().concat(e))}})})();
// http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js
// base64
(function(){var h=CryptoJS,j=h.lib.WordArray;h.enc.Base64={stringify:function(b){var e=b.words,f=b.sigBytes,c=this._map;b.clamp();b=[];for(var a=0;a<f;a+=3)for(var d=(e[a>>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g<f;g++)b.push(c.charAt(d>>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d<
e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
/*
End of CryptoJS
*/
/* eslint-enable no-redeclare */
var storage = {
setValue: function(key, value) {
localStorage.setItem(key, value);
},
getValue: function(key, def) {
return localStorage.getItem(key) || def;
},
deleteValue: function(key) {
localStorage.removeItem(key);
}
};
var g_DefaultDeviceID = "android:" + CryptoJS.SHA1(window.g_steamID).toString().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');
var g_DeviceID = null;
function error(msg) {
storage.setValue("errormsg", msg);
window.location.href = "/mobileconf/conf?options";
}
if (window.location.href.match(/mobileconf/)) {
window.SGHandler = {"getResultStatus": function() { return "busy"; }, "getResultValue": function() { return ""; }};
window.GetValueFromLocalURL = function(url, timeout, success, error, fatal) {
console.log('url: ' + url);
var tag = url.match(/op=conftag&arg1=(\w+)/)[1];
getKey(tag, function(time, key) {
success("p=" + encodeURIComponent(g_DeviceID || g_DefaultDeviceID) + "&a=" + window.g_steamID + "&k=" + encodeURIComponent(key) + "&t=" + time + "&m=android&tag=" + tag);
});
};
getAccountName(function(accountName) {
if (!accountName) {
error("Couldn't get your account name.");
return;
}
g_DeviceID = storage.getValue(accountName + ":device_id", "").trim();
if (!window.location.search || window.location.search == "?options") {
var identitySecret = storage.getValue(accountName + ":identity_secret", "").trim();
if (window.location.search == "?options" || !identitySecret) {
var errormsg = storage.getValue("errormsg");
var $error = window.$J('<p style="color: #c00"></p>');
if (errormsg) {
$error.text(errormsg);
storage.deleteValue("errormsg");
}
var $identitySecret = window.$J('<p>Enter your identity secret: <input type="text" size="100" placeholder="blahblahblahblah=" style="padding: 3px; border-color: #222; color: #ccc" value="' + identitySecret + '" /></p>');
var $deviceID = window.$J('<p>Enter your DeviceID: <input type="text" size="100" placeholder="' + g_DefaultDeviceID + ' (autogenerated)" style="padding: 3px; border-color: #222; color: #ccc" value="' + g_DeviceID + '" /></p>');
var $save = window.$J('<p><button type="button" id="gm_save">Save</button></p>');
var $empty = window.$J('#mobileconf_empty');
$empty.html($error).append($identitySecret).append($deviceID).append($save);
$save.find('button').click(function() {
storage.setValue(accountName + ":identity_secret", $identitySecret.find('input').val().trim());
g_DeviceID = $deviceID.find('input').val().trim();
storage.setValue(accountName + ":device_id", g_DeviceID);
redirectToConf();
});
} else {
redirectToConf();
}
} else {
if (!g_DeviceID) {
g_DeviceID = g_DefaultDeviceID;
}
window.$J('#mobileconf_empty').append('<div style="margin-top: 20px"><a href="/mobileconf/conf?options" style="text-decoration: underline">Change identity secret</a></div>');
}
});
window.$J('#mobileconf_done').append('<div style="margin-top: 20px"><a href="/mobileconf/conf" style="text-decoration: underline">Back / Reload</a></div>');
}
function redirectToConf(suppressDialog) {
if (!suppressDialog) {
window.ShowBlockingWaitDialog('Loading...', 'Loading your confirmations...');
}
getKey("conf", function(time, key) {
window.location.href = "/mobileconf/conf?p=" + encodeURIComponent(g_DeviceID || g_DefaultDeviceID) + "&a=" + window.g_steamID + "&k=" + encodeURIComponent(key) + "&t=" + time + "&m=android&tag=conf";
});
}
function getAccountName(callback) {
var accountName = window.$J('#account_dropdown .persona').text().trim();
if (accountName) {
callback(accountName);
return;
}
// It's not on this page
window.$J.get('/', function(html) {
accountName = html.match(/<span [^>]*class="persona online"[^>]*>([^<]+)<\/span>/);
if (!accountName) {
callback(null);
} else {
callback(accountName[1].trim());
}
});
}
function getKey(tag, callback) {
getAccountName(function(accountName) {
if (!accountName) {
error("Couldn't get your account name.");
return;
}
var identitySecret = storage.getValue(accountName + ":identity_secret");
if (!identitySecret || !identitySecret.length) {
error("Couldn't get your identity secret.");
return;
}
var time = Math.round(Date.now() / 1000);
var key = getConfirmationKey(identitySecret, time, tag);
callback(time, key);
});
}
function getConfirmationKey(identitySecret, time, tag) {
var requiredLength = 8;
if (tag) {
if (tag.length > 32) {
requiredLength += 32;
} else {
requiredLength += tag.length;
}
}
var buffer = new Uint8Array(requiredLength);
for (var idx = 7; idx >= 0; --idx) {
buffer[idx] = time & 0xFF;
time >>>= 8;
}
if (tag) {
tag = Array.from(tag, function(c){return c.charCodeAt(0)});
buffer.set(tag, 8);
}
buffer = Array.from(buffer, function(n){return n.toString(16).padStart(2, '0')}).join('');
var hash = CryptoJS.HmacSHA1(CryptoJS.enc.Hex.parse(buffer), CryptoJS.enc.Base64.parse(identitySecret));
return hash.toString(CryptoJS.enc.Base64);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment