Skip to content

Instantly share code, notes, and snippets.

@sashahilton00
Created June 26, 2017 15:12
Show Gist options
  • Save sashahilton00/54578664a0f8076d567ee21ec0d64257 to your computer and use it in GitHub Desktop.
Save sashahilton00/54578664a0f8076d567ee21ec0d64257 to your computer and use it in GitHub Desktop.
Spotify Connection and DH Handshake
//Spotify Protocol Connection Demo
//N.B. Don't forget to install the dependencies
var crypto = require('crypto');
var fs = require('fs');
var net = require('net');
var protobuf = require('protocol-buffers');
var HOST = 'lon6-accesspoint-a19.ap.spotify.com';
var PORT = 4070;
var client = new net.Socket();
var messages = protobuf(fs.readFileSync('keyexchange.proto'))
var prime = Buffer([
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9,
0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6,
0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e,
0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6,
0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e,
0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a,
0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14,
0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4,
0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff ]);
var dh = crypto.createDiffieHellman(prime);
var dhLocalKeys = dh.generateKeys();
var clientHelloMessage = messages.ClientHello.encode({
build_info: {
product: messages.Product.PRODUCT_PARTNER,
platform: messages.Platform.PLATFORM_OSX_X86_64,
version: 105600451,
},
cryptosuites_supported: [messages.Cryptosuite.CRYPTO_SUITE_SHANNON],
login_crypto_hello: {
diffie_hellman: {
gc: dhLocalKeys,
server_keys_known: 1
}
},
client_nonce: crypto.randomBytes(16),
});
var bufHeader = new Buffer([0x00, 0x04]);
var bufLength = Buffer.alloc(4);
bufLength.writeUInt32BE('0x' + (2 + 4 + clientHelloMessage.length).toString(16));
var clientHelloBuf = Buffer.concat([bufHeader, bufLength, clientHelloMessage]);
var isInitialPacket = true;
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
client.write(clientHelloBuf);
});
client.on('data', function(data) {
var response = new Buffer(data, "hex");
if (isInitialPacket) {
var sharedSecret = dh.computeSecret(messages.APResponseMessage.decode(response.slice(4, 2 * (response.readUInt32BE(0, 7)))).challenge.login_crypto_challenge.diffie_hellman.gs);
var shannonKeyBuf = new Buffer(100);
for (var i = 1; i <= 5; i++) {
var hmacSha1 = crypto.createHmac('sha1', sharedSecret);
var buf = Buffer.concat([clientHelloBuf, response]);
buf.write(i.toString(), buf.length - 1);
hmacSha1.update(buf);
shannonKeyBuf.write(hmacSha1.digest('hex'), (i - 1) * 20, 20, 'hex');
if (shannonKeyBuf[99] !== 00) {
console.log('\nKey String: ' + shannonKeyBuf.toString('hex'));
console.log('\nChallenge: ' + shannonKeyBuf.slice(0, 20).toString('hex'));
console.log('\nSend Key: ' + shannonKeyBuf.slice(20, 52).toString('hex'));
console.log('\nRecieve Key: ' + shannonKeyBuf.slice(52, 82).toString('hex'));
var clientResponsePlaintext = messages.ClientResponsePlaintext.encode({
login_crypto_response: {
diffie_hellman: {
hmac: shannonKeyBuf.slice(0, 20)
}
},
pow_response: {},
crypto_response: {}
});
client.write(clientResponsePlaintext);
}
}
} else {
console.log(response.toString('hex'));
}
client.destroy();
});
client.on('close', function() {
console.log('Connection closed');
});
syntax = "proto2";
message ClientHello {
required BuildInfo build_info = 0xa;
repeated Fingerprint fingerprints_supported = 0x14;
repeated Cryptosuite cryptosuites_supported = 0x1e;
repeated Powscheme powschemes_supported = 0x28;
required LoginCryptoHelloUnion login_crypto_hello = 0x32;
required bytes client_nonce = 0x3c;
optional bytes padding = 0x46;
optional FeatureSet feature_set = 0x50;
}
message BuildInfo {
required Product product = 0xa;
repeated ProductFlags product_flags = 0x14;
required Platform platform = 0x1e;
required uint64 version = 0x28;
}
enum Product {
PRODUCT_CLIENT = 0x0;
PRODUCT_LIBSPOTIFY= 0x1;
PRODUCT_MOBILE = 0x2;
PRODUCT_PARTNER = 0x3;
PRODUCT_LIBSPOTIFY_EMBEDDED = 0x5;
}
enum ProductFlags {
PRODUCT_FLAG_NONE = 0x0;
PRODUCT_FLAG_DEV_BUILD = 0x1;
}
enum Platform {
PLATFORM_WIN32_X86 = 0x0;
PLATFORM_OSX_X86 = 0x1;
PLATFORM_LINUX_X86 = 0x2;
PLATFORM_IPHONE_ARM = 0x3;
PLATFORM_S60_ARM = 0x4;
PLATFORM_OSX_PPC = 0x5;
PLATFORM_ANDROID_ARM = 0x6;
PLATFORM_WINDOWS_CE_ARM = 0x7;
PLATFORM_LINUX_X86_64 = 0x8;
PLATFORM_OSX_X86_64 = 0x9;
PLATFORM_PALM_ARM = 0xa;
PLATFORM_LINUX_SH = 0xb;
PLATFORM_FREEBSD_X86 = 0xc;
PLATFORM_FREEBSD_X86_64 = 0xd;
PLATFORM_BLACKBERRY_ARM = 0xe;
PLATFORM_SONOS = 0xf;
PLATFORM_LINUX_MIPS = 0x10;
PLATFORM_LINUX_ARM = 0x11;
PLATFORM_LOGITECH_ARM = 0x12;
PLATFORM_LINUX_BLACKFIN = 0x13;
PLATFORM_WP7_ARM = 0x14;
PLATFORM_ONKYO_ARM = 0x15;
PLATFORM_QNXNTO_ARM = 0x16;
PLATFORM_BCO_ARM = 0x17;
}
enum Fingerprint {
FINGERPRINT_GRAIN = 0x0;
FINGERPRINT_HMAC_RIPEMD = 0x1;
}
enum Cryptosuite {
CRYPTO_SUITE_SHANNON = 0x0;
CRYPTO_SUITE_RC4_SHA1_HMAC = 0x1;
}
enum Powscheme {
POW_HASH_CASH = 0x0;
}
message LoginCryptoHelloUnion {
optional LoginCryptoDiffieHellmanHello diffie_hellman = 0xa;
}
message LoginCryptoDiffieHellmanHello {
required bytes gc = 0xa;
required uint32 server_keys_known = 0x14;
}
message FeatureSet {
optional bool autoupdate2 = 0x1;
optional bool current_location = 0x2;
}
message APResponseMessage {
optional APChallenge challenge = 0xa;
optional UpgradeRequiredMessage upgrade = 0x14;
optional APLoginFailed login_failed = 0x1e;
}
message APChallenge {
required LoginCryptoChallengeUnion login_crypto_challenge = 0xa;
required FingerprintChallengeUnion fingerprint_challenge = 0x14;
required PoWChallengeUnion pow_challenge = 0x1e;
required CryptoChallengeUnion crypto_challenge = 0x28;
required bytes server_nonce = 0x32;
optional bytes padding = 0x3c;
}
message LoginCryptoChallengeUnion {
optional LoginCryptoDiffieHellmanChallenge diffie_hellman = 0xa;
}
message LoginCryptoDiffieHellmanChallenge {
required bytes gs = 0xa;
required int32 server_signature_key = 0x14;
required bytes gs_signature = 0x1e;
}
message FingerprintChallengeUnion {
optional FingerprintGrainChallenge grain = 0xa;
optional FingerprintHmacRipemdChallenge hmac_ripemd = 0x14;
}
message FingerprintGrainChallenge {
required bytes kek = 0xa;
}
message FingerprintHmacRipemdChallenge {
required bytes challenge = 0xa;
}
message PoWChallengeUnion {
optional PoWHashCashChallenge hash_cash = 0xa;
}
message PoWHashCashChallenge {
optional bytes prefix = 0xa;
optional int32 length = 0x14;
optional int32 target = 0x1e;
}
message CryptoChallengeUnion {
optional CryptoShannonChallenge shannon = 0xa;
optional CryptoRc4Sha1HmacChallenge rc4_sha1_hmac = 0x14;
}
message CryptoShannonChallenge {
}
message CryptoRc4Sha1HmacChallenge {
}
message UpgradeRequiredMessage {
required bytes upgrade_signed_part = 0xa;
required bytes signature = 0x14;
optional string http_suffix = 0x1e;
}
message APLoginFailed {
required ErrorCode error_code = 0xa;
optional int32 retry_delay = 0x14;
optional int32 expiry = 0x1e;
optional string error_description = 0x28;
}
enum ErrorCode {
ProtocolError = 0x0;
TryAnotherAP = 0x2;
BadConnectionId = 0x5;
TravelRestriction = 0x9;
PremiumAccountRequired = 0xb;
BadCredentials = 0xc;
CouldNotValidateCredentials = 0xd;
AccountExists = 0xe;
ExtraVerificationRequired = 0xf;
InvalidAppKey = 0x10;
ApplicationBanned = 0x11;
}
message ClientResponsePlaintext {
required LoginCryptoResponseUnion login_crypto_response = 0xa;
required PoWResponseUnion pow_response = 0x14;
required CryptoResponseUnion crypto_response = 0x1e;
}
message LoginCryptoResponseUnion {
optional LoginCryptoDiffieHellmanResponse diffie_hellman = 0xa;
}
message LoginCryptoDiffieHellmanResponse {
required bytes hmac = 0xa;
}
message PoWResponseUnion {
optional PoWHashCashResponse hash_cash = 0xa;
}
message PoWHashCashResponse {
required bytes hash_suffix = 0xa;
}
message CryptoResponseUnion {
optional CryptoShannonResponse shannon = 0xa;
optional CryptoRc4Sha1HmacResponse rc4_sha1_hmac = 0x14;
}
message CryptoShannonResponse {
optional int32 dummy = 0x1;
}
message CryptoRc4Sha1HmacResponse {
optional int32 dummy = 0x1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment