mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
128 lines
4.8 KiB
Swift
128 lines
4.8 KiB
Swift
import Foundation
|
|
import Postbox
|
|
import MtProtoKit
|
|
import SwiftSignalKit
|
|
|
|
public struct UploadedSecureIdFile: Equatable {
|
|
let id: Int64
|
|
let parts: Int32
|
|
let md5Checksum: String
|
|
public let fileHash: Data
|
|
let encryptedSecret: Data
|
|
}
|
|
|
|
public enum UploadSecureIdFileResult {
|
|
case progress(Float)
|
|
case result(UploadedSecureIdFile, Data)
|
|
}
|
|
|
|
public enum UploadSecureIdFileError {
|
|
case generic
|
|
}
|
|
|
|
private struct EncryptedSecureIdFile {
|
|
let data: Data
|
|
let hash: Data
|
|
let encryptedSecret: Data
|
|
}
|
|
|
|
private func encryptedSecureIdFile(context: SecureIdAccessContext, data: Data) -> EncryptedSecureIdFile? {
|
|
guard let fileSecret = generateSecureSecretData() else {
|
|
return nil
|
|
}
|
|
|
|
let paddedFileData = paddedSecureIdData(data)
|
|
let fileHash = sha256Digest(paddedFileData)
|
|
let fileSecretHash = sha512Digest(fileSecret + fileHash)
|
|
let fileKey = fileSecretHash.subdata(in: 0 ..< 32)
|
|
let fileIv = fileSecretHash.subdata(in: 32 ..< (32 + 16))
|
|
guard let encryptedFileData = encryptSecureData(key: fileKey, iv: fileIv, data: paddedFileData, decrypt: false) else {
|
|
return nil
|
|
}
|
|
|
|
let secretHash = sha512Digest(context.secret + fileHash)
|
|
let secretKey = secretHash.subdata(in: 0 ..< 32)
|
|
let secretIv = secretHash.subdata(in: 32 ..< (32 + 16))
|
|
guard let encryptedSecretData = encryptSecureData(key: secretKey, iv: secretIv, data: fileSecret, decrypt: false) else {
|
|
return nil
|
|
}
|
|
|
|
return EncryptedSecureIdFile(data: encryptedFileData, hash: fileHash, encryptedSecret: encryptedSecretData)
|
|
}
|
|
|
|
func decryptedSecureIdFileSecret(context: SecureIdAccessContext, fileHash: Data, encryptedSecret: Data) -> Data? {
|
|
let secretHash = sha512Digest(context.secret + fileHash)
|
|
let secretKey = secretHash.subdata(in: 0 ..< 32)
|
|
let secretIv = secretHash.subdata(in: 32 ..< (32 + 16))
|
|
guard let fileSecret = encryptSecureData(key: secretKey, iv: secretIv, data: encryptedSecret, decrypt: true) else {
|
|
return nil
|
|
}
|
|
guard verifySecureSecret(fileSecret) else {
|
|
return nil
|
|
}
|
|
return fileSecret
|
|
}
|
|
|
|
func decryptedSecureIdFile(context: SecureIdAccessContext, encryptedData: Data, fileHash: Data, encryptedSecret: Data) -> Data? {
|
|
guard let fileSecret = decryptedSecureIdFileSecret(context: context, fileHash: fileHash, encryptedSecret: encryptedSecret) else {
|
|
return nil
|
|
}
|
|
|
|
let fileSecretHash = sha512Digest(fileSecret + fileHash)
|
|
let fileKey = fileSecretHash.subdata(in: 0 ..< 32)
|
|
let fileIv = fileSecretHash.subdata(in: 32 ..< (32 + 16))
|
|
guard let paddedFileData = encryptSecureData(key: fileKey, iv: fileIv, data: encryptedData, decrypt: true) else {
|
|
return nil
|
|
}
|
|
|
|
let checkFileHash = sha256Digest(paddedFileData)
|
|
if fileHash != checkFileHash {
|
|
return nil
|
|
}
|
|
|
|
guard let unpaddedFileData = unpaddedSecureIdData(paddedFileData) else {
|
|
return nil
|
|
}
|
|
|
|
return unpaddedFileData
|
|
}
|
|
|
|
public func uploadSecureIdFile(context: SecureIdAccessContext, postbox: Postbox, network: Network, resource: MediaResource) -> Signal<UploadSecureIdFileResult, UploadSecureIdFileError> {
|
|
return postbox.mediaBox.resourceData(resource)
|
|
|> mapError { _ -> UploadSecureIdFileError in
|
|
return .generic
|
|
}
|
|
|> mapToSignal { next -> Signal<UploadSecureIdFileResult, UploadSecureIdFileError> in
|
|
if !next.complete {
|
|
return .complete()
|
|
}
|
|
|
|
guard let data = try? Data(contentsOf: URL(fileURLWithPath: next.path)) else {
|
|
return .fail(.generic)
|
|
}
|
|
|
|
guard let encryptedData = encryptedSecureIdFile(context: context, data: data) else {
|
|
return .fail(.generic)
|
|
}
|
|
|
|
return multipartUpload(network: network, postbox: postbox, source: .data(encryptedData.data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false, forceNoBigParts: false)
|
|
|> mapError { _ -> UploadSecureIdFileError in
|
|
return .generic
|
|
}
|
|
|> mapToSignal { result -> Signal<UploadSecureIdFileResult, UploadSecureIdFileError> in
|
|
switch result {
|
|
case let .progress(value):
|
|
return .single(.progress(value))
|
|
case let .inputFile(file):
|
|
if case let .inputFile(id, parts, _, md5Checksum) = file {
|
|
return .single(.result(UploadedSecureIdFile(id: id, parts: parts, md5Checksum: md5Checksum, fileHash: encryptedData.hash, encryptedSecret: encryptedData.encryptedSecret), encryptedData.data))
|
|
} else {
|
|
return .fail(.generic)
|
|
}
|
|
default:
|
|
return .fail(.generic)
|
|
}
|
|
}
|
|
}
|
|
}
|