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

137 lines
6.6 KiB
Swift

import Foundation
#if os(macOS)
import PostboxMac
import MtProtoKitMac
import SwiftSignalKitMac
#else
import Postbox
import MtProtoKitDynamic
import SwiftSignalKit
#endif
public enum RequestSecureIdFormError {
case generic
}
private func parseSecureValueType(_ type: Api.SecureValueType) -> SecureIdRequestedFormField {
switch type {
case .secureValueTypeIdentity:
return .identity
case .secureValueTypeAddress:
return .address
case .secureValueTypePhone:
return .phone
case .secureValueTypeEmail:
return .email
}
}
private func parseSecureData(_ value: Api.SecureData) -> (data: Data, hash: Data) {
switch value {
case let .secureData(data, dataHash):
return (data.makeData(), dataHash.makeData())
}
}
struct ParsedSecureValue {
let valueWithContext: SecureIdValueWithContext
let hash: Data
}
func parseSecureValue(context: SecureIdAccessContext, value: Api.SecureValue) -> ParsedSecureValue? {
switch value {
case let .secureValueIdentity(_, data, files, secret, hash, verified):
let (encryptedData, decryptedHash) = parseSecureData(data)
guard let valueContext = decryptedSecureValueAccessContext(context: context, encryptedSecret: secret.makeData(), hash: hash.makeData()) else {
return nil
}
let parsedFileReferences = files.map(SecureIdFileReference.init).flatMap({ $0 })
let parsedFileHashes = parsedFileReferences.map { $0.fileHash }
let parsedFiles = parsedFileReferences.map(SecureIdVerificationDocumentReference.remote)
guard let decryptedData = decryptedSecureValueData(context: valueContext, encryptedData: encryptedData, decryptedDataHash: decryptedHash) else {
return nil
}
guard let value = SecureIdIdentityValue(data: decryptedData, fileReferences: parsedFiles) else {
return nil
}
return ParsedSecureValue(valueWithContext: SecureIdValueWithContext(value: .identity(value), context: valueContext, encryptedMetadata: SecureIdEncryptedValueMetadata(valueDataHash: decryptedHash, fileHashes: parsedFileHashes, valueSecret: valueContext.secret, hash: hash.makeData())), hash: hash.makeData())
case let .secureValueAddress(_, data, files, secret, hash, verified):
let (encryptedData, decryptedHash) = parseSecureData(data)
guard let valueContext = decryptedSecureValueAccessContext(context: context, encryptedSecret: secret.makeData(), hash: hash.makeData()) else {
return nil
}
let parsedFileReferences = files.map(SecureIdFileReference.init).flatMap({ $0 })
let parsedFileHashes = parsedFileReferences.map { $0.fileHash }
let parsedFiles = parsedFileReferences.map(SecureIdVerificationDocumentReference.remote)
guard let decryptedData = decryptedSecureValueData(context: valueContext, encryptedData: encryptedData, decryptedDataHash: decryptedHash) else {
return nil
}
guard let value = SecureIdAddressValue(data: decryptedData, fileReferences: parsedFiles) else {
return nil
}
return ParsedSecureValue(valueWithContext: SecureIdValueWithContext(value: .address(value), context: valueContext, encryptedMetadata: SecureIdEncryptedValueMetadata(valueDataHash: decryptedHash, fileHashes: parsedFileHashes, valueSecret: valueContext.secret, hash: hash.makeData())), hash: hash.makeData())
case let .secureValuePhone(_, phone, hash, verified):
guard let phoneData = phone.data(using: .utf8) else {
return nil
}
if sha256Digest(phoneData) != hash.makeData() {
return nil
}
return ParsedSecureValue(valueWithContext: SecureIdValueWithContext(value: .phone(SecureIdPhoneValue(phone: phone)), context: SecureIdValueAccessContext(secret: Data(), hash: 0), encryptedMetadata: nil), hash: hash.makeData())
case let .secureValueEmail(_, email, hash, verified):
guard let emailData = email.data(using: .utf8) else {
return nil
}
if sha256Digest(emailData) != hash.makeData() {
return nil
}
return ParsedSecureValue(valueWithContext: SecureIdValueWithContext(value: .email(SecureIdEmailValue(email: email)), context: SecureIdValueAccessContext(secret: Data(), hash: 0), encryptedMetadata: nil), hash: hash.makeData())
}
}
private func parseSecureValues(context: SecureIdAccessContext, values: [Api.SecureValue]) -> [SecureIdValueWithContext] {
return values.map({ parseSecureValue(context: context, value: $0) }).flatMap({ $0?.valueWithContext })
}
public struct EncryptedSecureIdForm {
public let peerId: PeerId
public let requestedFields: [SecureIdRequestedFormField]
let encryptedValues: [Api.SecureValue]
}
public func requestSecureIdForm(postbox: Postbox, network: Network, peerId: PeerId, scope: String, publicKey: String) -> Signal<EncryptedSecureIdForm, RequestSecureIdFormError> {
if peerId.namespace != Namespaces.Peer.CloudUser {
return .fail(.generic)
}
return network.request(Api.functions.account.getAuthorizationForm(botId: peerId.id, scope: scope, publicKey: publicKey))
|> mapError { _ -> RequestSecureIdFormError in
return .generic
}
|> mapToSignal { result -> Signal<EncryptedSecureIdForm, RequestSecureIdFormError> in
return postbox.modify { modifier -> EncryptedSecureIdForm in
switch result {
case let .authorizationForm(_, requiredTypes, values, users):
var peers: [Peer] = []
for user in users {
let parsed = TelegramUser(user: user)
peers.append(parsed)
}
updatePeers(modifier: modifier, peers: peers, update: { _, updated in
return updated
})
return EncryptedSecureIdForm(peerId: peerId, requestedFields: requiredTypes.map(parseSecureValueType), encryptedValues: values)
}
} |> mapError { _ in return RequestSecureIdFormError.generic }
}
}
public func decryptedSecureIdForm(context: SecureIdAccessContext, form: EncryptedSecureIdForm) -> SecureIdForm? {
return SecureIdForm(peerId: form.peerId, requestedFields: form.requestedFields, values: parseSecureValues(context: context, values: form.encryptedValues))
}