/// <summary> /// Processes the specified depot key by decrypting the data with the given depot encryption key, and then by decompressing the data. /// If the chunk has already been processed, this function does nothing. /// </summary> /// <param name="depotKey">The depot decryption key.</param> /// <exception cref="System.IO.InvalidDataException">Thrown if the processed data does not match the expected checksum given in it's chunk information.</exception> public void Process(byte[] depotKey) { if (IsProcessed) { return; } byte[] processedData = CryptoHelper.SymmetricDecrypt(Data, depotKey); processedData = ZipUtil.Decompress(processedData); byte[] dataCrc = CryptoHelper.AdlerHash(processedData); if (!dataCrc.SequenceEqual(ChunkInfo.Checksum)) { throw new InvalidDataException("Processed data checksum is incorrect! Downloaded depot chunk is corrupt or invalid/wrong depot key?"); } Data = processedData; IsProcessed = true; }
/// <summary> /// Begins a request to upload a file to the UFS. /// The <see cref="UFSClient"/> should be logged on before this point. /// Results are returned in a <see cref="UploadFileResponseCallback"/>. /// </summary> /// <param name="details">The details to use for uploading the file.</param> /// <returns>The Job ID of the request. This can be used to find the appropriate <see cref="UploadFileResponseCallback"/>.</returns> public JobID RequestFileUpload(UploadDetails details) { byte[] compressedData = ZipUtil.Compress(details.FileData); var msg = new ClientMsgProtobuf <CMsgClientUFSUploadFileRequest>(EMsg.ClientUFSUploadFileRequest); msg.SourceJobID = steamClient.GetNextJobID(); msg.Body.app_id = details.AppID; msg.Body.can_encrypt = false; msg.Body.file_name = details.FileName; msg.Body.file_size = ( uint )compressedData.Length; msg.Body.raw_file_size = ( uint )details.FileData.Length; msg.Body.sha_file = CryptoHelper.SHAHash(details.FileData); msg.Body.time_stamp = DateUtils.DateTimeToUnixTime(DateTime.UtcNow); Send(msg); return(msg.SourceJobID); }
/// <summary> /// Downloads the depot manifest specified by the given manifest ID, and optionally decrypts the manifest's filenames if the depot decryption key has been provided. /// </summary> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="manifestId">The unique identifier of the manifest to be downloaded.</param> /// <param name="server">The content server to connect to.</param> /// <param name="cdnAuthToken">CDN auth token for CDN content server endpoints.</param> /// <param name="depotKey"> /// The depot decryption key for the depot that will be downloaded. /// This is used for decrypting filenames (if needed) in depot manifests, and processing depot chunks. /// </param> /// <param name="proxyServer">Optional content server marked as UseAsProxy which transforms the request.</param> /// <returns>A <see cref="DepotManifest"/> instance that contains information about the files present within a depot.</returns> /// <exception cref="System.ArgumentNullException"><see ref="server"/> was null.</exception> /// <exception cref="HttpRequestException">An network error occurred when performing the request.</exception> /// <exception cref="SteamKitWebRequestException">A network error occurred when performing the request.</exception> public async Task <DepotManifest> DownloadManifestAsync(uint depotId, ulong manifestId, Server server, string?cdnAuthToken, byte[]?depotKey, Server?proxyServer = null) { if (server == null) { throw new ArgumentNullException(nameof(server)); } var manifestData = await DoRawCommandAsync(server, string.Format("depot/{0}/manifest/{1}/5", depotId, manifestId), cdnAuthToken, proxyServer).ConfigureAwait(false); manifestData = ZipUtil.Decompress(manifestData); var depotManifest = new DepotManifest(manifestData); if (depotKey != null) { // if we have the depot key, decrypt the manifest filenames depotManifest.DecryptFilenames(depotKey); } return(depotManifest); }
/// <summary> /// Downloads the depot manifest specified by the given manifest ID, and optionally decrypts the manifest's filenames if the depot decryption key has been provided. /// </summary> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="manifestId">The unique identifier of the manifest to be downloaded.</param> /// <returns>A <see cref="DepotManifest"/> instance that contains information about the files present within a depot.</returns> public DepotManifest DownloadManifest(uint depotId, ulong manifestId) { string cdnToken = null; depotCdnAuthKeys.TryGetValue(depotId, out cdnToken); byte[] compressedManifest = DoRawCommand(connectedServer, "depot", doAuth: true, args: string.Format("{0}/manifest/{1}/5", depotId, manifestId), authtoken: cdnToken); byte[] manifestData = ZipUtil.Decompress(compressedManifest); var depotManifest = new DepotManifest(manifestData); byte[] depotKey; if (depotKeys.TryGetValue(depotId, out depotKey)) { // if we have the depot key, decrypt the manifest filenames depotManifest.DecryptFilenames(depotKey); } return(depotManifest); }
/// <summary> /// Uploads the actual contents of a file to the UFS. /// The <see cref="UFSClient"/> should be logged on before this point, and the previous request to upload a file must have completed successfully. /// Results are returned in a <see cref="UploadFileFinishedCallback"/>. /// </summary> /// <param name="details">The details to use for uploading the file.</param> /// <returns>The Job ID of the request. This can be used to find the appropriate <see cref="UploadFileFinishedCallback"/>.</returns> public void UploadFile(UploadDetails details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } const uint MaxBytesPerChunk = 10240; byte[] compressedData = ZipUtil.Compress(details.FileData); byte[] fileHash = CryptoHelper.SHAHash(details.FileData); var buffer = new byte[MaxBytesPerChunk]; using (var ms = new MemoryStream(compressedData)) { for (long readIndex = 0; readIndex < ms.Length; readIndex += buffer.Length) { var msg = new ClientMsgProtobuf <CMsgClientUFSFileChunk>(EMsg.ClientUFSUploadFileChunk); msg.TargetJobID = details.RemoteJobID; var bytesRead = ms.Read(buffer, 0, buffer.Length); if (bytesRead < buffer.Length) { msg.Body.data = buffer.Take(bytesRead).ToArray(); } else { msg.Body.data = buffer; } msg.Body.file_start = ( uint )readIndex; msg.Body.sha_file = fileHash; Send(msg); } } }
/// <summary> /// Downloads the depot manifest specified by the given manifest ID, and optionally decrypts the manifest's filenames if the depot decryption key has been provided. /// </summary> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="manifestId">The unique identifier of the manifest to be downloaded.</param> /// <param name="host">CDN hostname.</param> /// <param name="cdnAuthToken">CDN auth token for CDN content server endpoints.</param> /// <param name="depotKey"> /// The depot decryption key for the depot that will be downloaded. /// This is used for decrypting filenames (if needed) in depot manifests, and processing depot chunks. /// </param> /// <returns>A <see cref="DepotManifest"/> instance that contains information about the files present within a depot.</returns> public DepotManifest DownloadManifest(uint depotId, ulong manifestId, string host, string cdnAuthToken, byte[] depotKey = null) { var server = new Server { Host = host, Port = 80 }; byte[] manifestData = DoRawCommand(server, "depot", doAuth: true, args: string.Format("{0}/manifest/{1}/5", depotId, manifestId), authtoken: cdnAuthToken); manifestData = ZipUtil.Decompress(manifestData); var depotManifest = new DepotManifest(manifestData); if (depotKey != null) { // if we have the depot key, decrypt the manifest filenames depotManifest.DecryptFilenames(depotKey); } return(depotManifest); }
/// <summary> /// Processes the specified depot key by decrypting the data with the given depot encryption key, and then by decompressing the data. /// If the chunk has already been processed, this function does nothing. /// </summary> /// <param name="depotKey">The depot decryption key.</param> /// <exception cref="System.IO.InvalidDataException">Thrown if the processed data does not match the expected checksum given in it's chunk information.</exception> public void Process(byte[] depotKey) { if (depotKey == null) { throw new ArgumentNullException(nameof(depotKey)); } if (IsProcessed) { return; } if (Data != null) { byte[] processedData = CryptoHelper.SymmetricDecrypt(Data, depotKey); if (processedData.Length > 1 && processedData[0] == 'V' && processedData[1] == 'Z') { processedData = VZipUtil.Decompress(processedData); } else { processedData = ZipUtil.Decompress(processedData); } byte[] dataCrc = CryptoHelper.AdlerHash(processedData); if (!dataCrc.SequenceEqual(ChunkInfo.Checksum)) { throw new InvalidDataException("Processed data checksum is incorrect! Downloaded depot chunk is corrupt or invalid/wrong depot key?"); } Data = processedData; IsProcessed = true; } if (DataStream != null) { Stream reProcessedData; using (Stream processedData = CryptoHelper.SymmetricDecrypt(DataStream, depotKey)) { int firstByte = processedData.ReadByte(); int secondByte = processedData.ReadByte(); processedData.Position = 0; if (firstByte == 86 && secondByte == 90) { reProcessedData = VZipUtil.Decompress(processedData); } else { reProcessedData = ZipUtil.Decompress(processedData); } } byte[] dataCrc = CryptoHelper.AdlerHash(reProcessedData); if (!dataCrc.SequenceEqual(ChunkInfo.Checksum)) { throw new InvalidDataException("Processed data checksum is incorrect! Downloaded depot chunk is corrupt or invalid/wrong depot key?"); } DataStream = reProcessedData; IsProcessed = true; } }