Swiftgram/TelegramCore/UploadSecureIdFile.swift
Peter Iakovlev 152a139727 no message
2018-03-28 15:01:20 +04:00

120 lines
4.0 KiB
Swift

import Foundation
#if os(macOS)
import PostboxMac
import MtProtoKitMac
import SwiftSignalKitMac
#else
import Postbox
import MtProtoKitDynamic
import SwiftSignalKit
#endif
public struct UploadedSecureIdFile: Equatable {
let id: Int64
let parts: Int32
let md5Checksum: String
let fileHash: Data
public static func ==(lhs: UploadedSecureIdFile, rhs: UploadedSecureIdFile) -> Bool {
if lhs.id != rhs.id {
return false
}
if lhs.parts != rhs.parts {
return false
}
if lhs.md5Checksum != rhs.md5Checksum {
return false
}
if lhs.fileHash != rhs.fileHash {
return false
}
return true
}
}
public enum UploadSecureIdFileResult {
case progress(Float)
case result(UploadedSecureIdFile)
}
public enum UploadSecureIdFileError {
case generic
}
private struct EncryptedSecureIdFile {
let data: Data
let hash: Data
}
private func encryptedSecureIdFile(valueContext: SecureIdValueAccessContext, data: Data) -> EncryptedSecureIdFile? {
let paddedFileData = paddedSecureIdData(data)
let fileHash = sha256Digest(paddedFileData)
let fileSecretHash = sha512Digest(valueContext.secret + 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
}
return EncryptedSecureIdFile(data: encryptedFileData, hash: fileHash)
}
func decryptedSecureIdFile(valueContext: SecureIdValueAccessContext, encryptedData: Data, fileHash: Data) -> Data? {
let fileSecretHash = sha512Digest(valueContext.secret + 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(valueContext: SecureIdValueAccessContext, 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(valueContext: valueContext, data: data) else {
return .fail(.generic)
}
return multipartUpload(network: network, postbox: postbox, source: .data(encryptedData.data), encrypt: false, tag: nil, hintFileSize: nil, hintFileIsLarge: 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)))
} else {
return .fail(.generic)
}
default:
return .fail(.generic)
}
}
}
}