Created
June 26, 2017 15:12
-
-
Save sashahilton00/54578664a0f8076d567ee21ec0d64257 to your computer and use it in GitHub Desktop.
Spotify Connection and DH Handshake
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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'); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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