/// <summary> /// Synchronously marks the producer/consumer queue as complete for adding. /// </summary> public void CompleteAdding() { using (_mutex.Lock()) { if (_completed.IsCancellationRequested) { return; } _completed.Cancel(); _completedOrNotEmpty.NotifyAll(); } }
internal MercuryPacket Receive(NetworkStream a, CancellationToken cts) { using (recvLock.Lock(cts)) { recvCipher.nonce(Utils.toByteArray(recvNonce)); Interlocked.Increment(ref recvNonce); var headerBytes = new byte[3]; a.ReadComplete(headerBytes, 0, headerBytes.Length); recvCipher.decrypt(headerBytes); var cmd = headerBytes[0]; var payloadLength = (short)((headerBytes[1] << 8) | (headerBytes[2] & 0xFF)); var payloadBytes = new byte[payloadLength]; a.ReadComplete(payloadBytes, 0, payloadBytes.Length); recvCipher.decrypt(payloadBytes); var mac = new byte[4]; a.ReadComplete(mac, 0, mac.Length); var expectedMac = new byte[4]; recvCipher.finish(expectedMac); return(new MercuryPacket((MercuryPacket.Type)cmd, payloadBytes)); } }
private void SendUnchecked(MercuryPacket.Type cmd, byte[] payload, CancellationToken cts) { using (sendLock.Lock(cts)) { var a = conn.Result.NetworkStream; var payloadLengthAsByte = BitConverter.GetBytes((short)payload.Length).Reverse().ToArray(); using var yetAnotherBuffer = new MemoryStream(3 + payload.Length); yetAnotherBuffer.WriteByte((byte)cmd); yetAnotherBuffer.Write(payloadLengthAsByte, 0, payloadLengthAsByte.Length); yetAnotherBuffer.Write(payload, 0, payload.Length); sendCipher.nonce(Utils.toByteArray(sendNonce)); Interlocked.Increment(ref sendNonce); var bufferBytes = yetAnotherBuffer.ToArray(); sendCipher.encrypt(bufferBytes); var fourBytesBuffer = new byte[4]; sendCipher.finish(fourBytesBuffer); a.Write(bufferBytes, 0, bufferBytes.Length); a.Write(fourBytesBuffer, 0, fourBytesBuffer.Length); a.Flush(); } }
/// <summary> /// Synchronously enters the monitor. Returns a disposable that leaves the monitor when disposed. This method may block the calling thread. /// </summary> /// <param name="cancellationToken">The cancellation token used to cancel the enter. If this is already set, then this method will attempt to enter the monitor immediately (succeeding if the monitor is currently available).</param> public IDisposable Enter(CancellationToken cancellationToken) { return(_asyncLock.Lock(cancellationToken)); }
/// <summary> /// <exception cref="IOException"/> /// <exception cref="SpotifyAuthenticatedException"/> /// <exception cref="AccessViolationException"/> /// </summary> private void Connect() { #region ClientHello Setup ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var clientHello = new ClientHello { BuildInfo = new BuildInfo { Platform = Platform.Win32X86, Product = Product.Client, ProductFlags = { ProductFlags.ProductFlagNone }, Version = 112800721 } }; clientHello.CryptosuitesSupported.Add(Cryptosuite.Shannon); clientHello.LoginCryptoHello = new LoginCryptoHelloUnion { DiffieHellman = new LoginCryptoDiffieHellmanHello { Gc = ByteString.CopyFrom(keys.PublicKeyArray()), ServerKeysKnown = 1 } }; var nonce = new byte[16]; (new Random()).NextBytes(nonce); clientHello.ClientNonce = ByteString.CopyFrom(nonce); clientHello.Padding = ByteString.CopyFrom(new byte[1] { (byte)30 }); var clientHelloBytes = clientHello.ToByteArray(); var a = conn.Result.NetworkStream; a.WriteByte(0x00); a.WriteByte(0x04); a.WriteByte(0x00); a.WriteByte(0x00); a.WriteByte(0x00); a.Flush(); var length = 2 + 4 + clientHelloBytes.Length; var bytes = BitConverter.GetBytes(length); a.WriteByte(bytes[0]); a.Write(clientHelloBytes, 0, clientHelloBytes.Length); a.Flush(); var buffer = new byte[1000]; var len = int.Parse(a.Read(buffer, 0, buffer.Length).ToString()); var tmp = new byte[len]; Array.Copy(buffer, tmp, len); tmp = tmp.Skip(4).ToArray(); var accumulator = new MemoryStream(); accumulator.WriteByte(0x00); accumulator.WriteByte(0x04); var lnarr = Utils.toByteArray(length); accumulator.Write(lnarr, 0, lnarr.Length); accumulator.Write((byte[])clientHelloBytes, 0, clientHelloBytes.Length); var lenArr = Utils.toByteArray(len); accumulator.Write(lenArr, 0, lenArr.Length); accumulator.Write((byte[])tmp, 0, tmp.Length); #endregion ClientHello Setup //Read APResponseMessage #region APResponse var binaryData = accumulator.ToArray(); var apResponseMessage = APResponseMessage.Parser.ParseFrom(tmp); var sharedKey = Utils.toByteArray(keys.ComputeSharedKey(apResponseMessage .Challenge.LoginCryptoChallenge.DiffieHellman.Gs.ToByteArray())); // Check gs_signature var rsa = new RSACryptoServiceProvider(); var rsaKeyInfo = new RSAParameters { Modulus = new BigInteger(1, serverKey).ToByteArrayUnsigned(), Exponent = BigInteger.ValueOf(65537).ToByteArrayUnsigned() }; //Set to the public key values. //Import key parameters into RSA. rsa.ImportParameters(rsaKeyInfo); var gs = apResponseMessage.Challenge.LoginCryptoChallenge.DiffieHellman.Gs.ToByteArray(); var sign = apResponseMessage.Challenge.LoginCryptoChallenge.DiffieHellman.GsSignature.ToByteArray(); if (!rsa.VerifyData(gs, sign, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1)) { throw new AccessViolationException("Failed to verify APResponse"); } // Solve challenge binaryData = accumulator.ToArray(); using var data = new MemoryStream(); var mac = new HMACSHA1(sharedKey); mac.Initialize(); for (var i = 1; i < 6; i++) { mac.TransformBlock(binaryData, 0, binaryData.Length, null, 0); var temp = new[] { (byte)i }; mac.TransformBlock(temp, 0, temp.Length, null, 0); mac.TransformFinalBlock(new byte[0], 0, 0); var final = mac.Hash; data.Write(final, 0, final.Length); mac = new HMACSHA1(sharedKey); } var dataArray = data.ToArray(); mac = new HMACSHA1(Arrays.CopyOfRange(dataArray, 0, 0x14)); mac.TransformBlock(binaryData, 0, binaryData.Length, null, 0); mac.TransformFinalBlock(new byte[0], 0, 0); var challenge = mac.Hash; var clientResponsePlaintext = new ClientResponsePlaintext { LoginCryptoResponse = new LoginCryptoResponseUnion { DiffieHellman = new LoginCryptoDiffieHellmanResponse { Hmac = ByteString.CopyFrom(challenge) } }, PowResponse = new PoWResponseUnion(), CryptoResponse = new CryptoResponseUnion() }; var clientResponsePlaintextBytes = clientResponsePlaintext.ToByteArray(); len = 4 + clientResponsePlaintextBytes.Length; a.WriteByte(0x00); a.WriteByte(0x00); a.WriteByte(0x00); var bytesb = BitConverter.GetBytes(len); a.WriteByte(bytesb[0]); a.Write(clientResponsePlaintextBytes, 0, clientResponsePlaintextBytes.Length); a.Flush(); try { var scrap = new byte[4]; conn.Result.NetworkStream.ReadTimeout = 300; var read = conn.Result.NetworkStream.Read(scrap, 0, scrap.Length); if (read == scrap.Length) { length = (scrap[0] << 24) | (scrap[1] << 16) | (scrap[2] << 8) | (scrap[3] & 0xFF); var payload = new byte[length - 4]; conn.Result.NetworkStream.ReadComplete(payload, 0, payload.Length); var failed = APResponseMessage.Parser.ParseFrom(payload)?.LoginFailed; throw new SpotifyAuthenticatedException(failed); } else if (read > 0) { throw new Exception("Read unknown data!"); } } catch (Exception x) { // ignored } finally { conn.Result.NetworkStream.ReadTimeout = Timeout.Infinite; } using (authLock.Lock()) { sendCipher = new Shannon(); sendCipher.key(Arrays.CopyOfRange(data.ToArray(), 0x14, 0x34)); recvCipher = new Shannon(); recvCipher.key(Arrays.CopyOfRange(data.ToArray(), 0x34, 0x54)); authLockEventWaitHandle.Set(); } #endregion APResponse Debug.WriteLine("Connected successfully"); }