UNPKG

steam-appticket-2

Version:
113 lines (100 loc) 3.81 kB
var Crypto = require('crypto'); var g_PubkeySystem = `-----BEGIN PUBLIC KEY----- MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDf7BrWLBBmLBc1OhSwfFkRf53T 2Ct64+AVzRkeRuh7h3SiGEYxqQMUeYKO6UWiSRKpI2hzic9pobFhRr3Bvr/WARvY gdTckPv+T1JzZsuVcNfFjrocejN1oWI0Rrtgt4Bo+hOneoo3S57G9F1fOpn5nsQ6 6WOiu4gZKODnFMBCiQIBEQ== -----END PUBLIC KEY-----`; /** * Verifies a signature using the Steam "System" public key. * @param {Buffer} data * @param {Buffer} signature * @param {string} algorithm */ exports.verifySignature = function(data, signature, algorithm) { var verify = Crypto.createVerify(algorithm || "RSA-SHA1"); verify.update(data); verify.end(); return verify.verify(g_PubkeySystem, signature); }; /** * Generate a 32-byte symmetric session key and encrypt it with Steam's public "System" key. * @param {Buffer} [nonce] - If provided, will be appended to the session key when encrypting * @returns {{plain: Buffer, encrypted: Buffer}} */ exports.generateSessionKey = function(nonce) { var sessionKey = Crypto.randomBytes(32); var cryptedSessionKey = Crypto.publicEncrypt(g_PubkeySystem, Buffer.concat([sessionKey, nonce || new Buffer(0)])); return { plain: sessionKey, encrypted: cryptedSessionKey }; }; /** * AES-encrypt some data with a symmetric key. * @param {Buffer} input * @param {Buffer} key * @param {Buffer} [iv] - If not provided, one will be generated randomly * @returns {Buffer} */ exports.symmetricEncrypt = function(input, key, iv) { iv = iv || Crypto.randomBytes(16); var aesIv = Crypto.createCipheriv('aes-256-ecb', key, ''); aesIv.setAutoPadding(false); aesIv.end(iv); var aesData = Crypto.createCipheriv('aes-256-cbc', key, iv); aesData.end(input); return Buffer.concat([aesIv.read(), aesData.read()]); }; /** * AES-encrypt some data with a symmetric key, and add an HMAC. * @param {Buffer} input * @param {Buffer} key */ exports.symmetricEncryptWithHmacIv = function(input, key) { // IV is HMAC-SHA1(Random(3) + Plaintext) + Random(3). (Same random values for both) var random = Crypto.randomBytes(3); var hmac = Crypto.createHmac("sha1", key.slice(0, 16)); // we only want the first 16 bytes of the key for the hmac hmac.update(random); hmac.update(input); return exports.symmetricEncrypt(input, key, Buffer.concat([hmac.digest().slice(0, 16 - random.length), random])); // the resulting IV must be 16 bytes long, so truncate the hmac to make room for the random }; /** * AES-decrypt some data with a symmetric key, and optionally check an HMAC. * @param {Buffer} input * @param {Buffer} key * @param {boolean} [checkHmac=false] - Does this data contain an HMAC for us to check? * @returns {*} */ exports.symmetricDecrypt = function(input, key, checkHmac) { var aesIv = Crypto.createDecipheriv('aes-256-ecb', key, ''); aesIv.setAutoPadding(false); aesIv.end(input.slice(0, 16)); var iv = aesIv.read(); var aesData = Crypto.createDecipheriv('aes-256-cbc', key, iv); aesData.end(input.slice(16)); var plaintext = aesData.read(); if (checkHmac) { // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC var remotePartialHmac = iv.slice(0, iv.length - 3); var random = iv.slice(iv.length - 3, iv.length); var hmac = Crypto.createHmac("sha1", key.slice(0, 16)); hmac.update(random); hmac.update(plaintext); if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { throw new Error("Received invalid HMAC from remote host."); } } return plaintext; }; /** * Decrypt something that was encrypted with AES/ECB/PKCS7 * @param {Buffer} input * @param {Buffer} key * @returns {Buffer} */ exports.symmetricDecryptECB = function(input, key) { var decipher = Crypto.createDecipheriv('aes-256-ecb', key, ''); decipher.end(input); return decipher.read(); };