From d67854a097bb58de4745fb7c36bdd87dfc965bf7 Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Fri, 23 Mar 2018 03:11:22 +0400 Subject: [PATCH] no message --- TelegramCore.xcodeproj/project.pbxproj | 68 +++++++ TelegramCore/AccessSecureId.swift | 185 ++++++++++++++++++++ TelegramCore/Account.swift | 23 ++- TelegramCore/RequestSecureIdForm.swift | 101 +++++++++-- TelegramCore/SecureIdDataTypes.swift | 43 +++++ TelegramCore/SecureIdEmailField.swift | 16 ++ TelegramCore/SecureIdForm.swift | 89 +++++++--- TelegramCore/SecureIdIdentityField.swift | 16 ++ TelegramCore/SecureIdPassportIdentity.swift | 41 +++++ TelegramCore/SecureIdPhoneField.swift | 16 ++ TelegramCore/TwoStepVerification.swift | 81 +++++++-- 11 files changed, 624 insertions(+), 55 deletions(-) create mode 100644 TelegramCore/AccessSecureId.swift create mode 100644 TelegramCore/SecureIdDataTypes.swift create mode 100644 TelegramCore/SecureIdEmailField.swift create mode 100644 TelegramCore/SecureIdIdentityField.swift create mode 100644 TelegramCore/SecureIdPassportIdentity.swift create mode 100644 TelegramCore/SecureIdPhoneField.swift diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index 7365db95ab..49c3dcb30e 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -362,6 +362,16 @@ D08F4A671E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08F4A651E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift */; }; D08F4A691E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08F4A681E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift */; }; D08F4A6A1E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08F4A681E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift */; }; + D093D7EB206413C900BC3599 /* SecureIdPassportIdentity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7EA206413C900BC3599 /* SecureIdPassportIdentity.swift */; }; + D093D7EC206413C900BC3599 /* SecureIdPassportIdentity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7EA206413C900BC3599 /* SecureIdPassportIdentity.swift */; }; + D093D7EE206413F600BC3599 /* SecureIdDataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7ED206413F600BC3599 /* SecureIdDataTypes.swift */; }; + D093D7EF206413F600BC3599 /* SecureIdDataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7ED206413F600BC3599 /* SecureIdDataTypes.swift */; }; + D093D7F12064194600BC3599 /* SecureIdIdentityField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7F02064194600BC3599 /* SecureIdIdentityField.swift */; }; + D093D7F22064194600BC3599 /* SecureIdIdentityField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7F02064194600BC3599 /* SecureIdIdentityField.swift */; }; + D093D7F520641A4900BC3599 /* SecureIdPhoneField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7F420641A4900BC3599 /* SecureIdPhoneField.swift */; }; + D093D7F620641A4900BC3599 /* SecureIdPhoneField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7F420641A4900BC3599 /* SecureIdPhoneField.swift */; }; + D093D7F920641AA500BC3599 /* SecureIdEmailField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7F820641AA500BC3599 /* SecureIdEmailField.swift */; }; + D093D7FA20641AA500BC3599 /* SecureIdEmailField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7F820641AA500BC3599 /* SecureIdEmailField.swift */; }; D099D7461EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099D7451EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift */; }; D099D7471EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099D7451EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift */; }; D099D7491EEF418D00A3128C /* HistoryViewChannelStateValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099D7481EEF418D00A3128C /* HistoryViewChannelStateValidation.swift */; }; @@ -499,6 +509,8 @@ D0BE303B20619EE800FBE6D8 /* SecureIdForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE303920619EE800FBE6D8 /* SecureIdForm.swift */; }; D0BE303D2061A29100FBE6D8 /* RequestSecureIdForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE303C2061A29100FBE6D8 /* RequestSecureIdForm.swift */; }; D0BE303E2061A29100FBE6D8 /* RequestSecureIdForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE303C2061A29100FBE6D8 /* RequestSecureIdForm.swift */; }; + D0BE304B20627D9800FBE6D8 /* AccessSecureId.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE304A20627D9800FBE6D8 /* AccessSecureId.swift */; }; + D0BE304C20627D9800FBE6D8 /* AccessSecureId.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE304A20627D9800FBE6D8 /* AccessSecureId.swift */; }; D0BE383E1E7C5995000079AF /* MediaPool.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE383D1E7C5995000079AF /* MediaPool.swift */; }; D0BE383F1E7C5995000079AF /* MediaPool.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE383D1E7C5995000079AF /* MediaPool.swift */; }; D0BEAF5D1E54941B00BD963D /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEAF5C1E54941B00BD963D /* Authorization.swift */; }; @@ -840,6 +852,11 @@ D08CAA8B1ED81EDF0000FDA8 /* Localizations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Localizations.swift; sourceTree = ""; }; D08F4A651E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeInstalledStickerPacksOperations.swift; sourceTree = ""; }; D08F4A681E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeInstalledStickerPacksOperations.swift; sourceTree = ""; }; + D093D7EA206413C900BC3599 /* SecureIdPassportIdentity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdPassportIdentity.swift; sourceTree = ""; }; + D093D7ED206413F600BC3599 /* SecureIdDataTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdDataTypes.swift; sourceTree = ""; }; + D093D7F02064194600BC3599 /* SecureIdIdentityField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdIdentityField.swift; sourceTree = ""; }; + D093D7F420641A4900BC3599 /* SecureIdPhoneField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdPhoneField.swift; sourceTree = ""; }; + D093D7F820641AA500BC3599 /* SecureIdEmailField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdEmailField.swift; sourceTree = ""; }; D099D7451EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelMessageStateVersionAttribute.swift; sourceTree = ""; }; D099D7481EEF418D00A3128C /* HistoryViewChannelStateValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoryViewChannelStateValidation.swift; sourceTree = ""; }; D099EA1B1DE72867001AF5A8 /* PeerCommands.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerCommands.swift; sourceTree = ""; }; @@ -922,6 +939,7 @@ D0BC387A1E40D2880044D6FE /* TogglePeerChatPinned.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TogglePeerChatPinned.swift; sourceTree = ""; }; D0BE303920619EE800FBE6D8 /* SecureIdForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdForm.swift; sourceTree = ""; }; D0BE303C2061A29100FBE6D8 /* RequestSecureIdForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestSecureIdForm.swift; sourceTree = ""; }; + D0BE304A20627D9800FBE6D8 /* AccessSecureId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessSecureId.swift; sourceTree = ""; }; D0BE383D1E7C5995000079AF /* MediaPool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPool.swift; sourceTree = ""; }; D0BEAF5C1E54941B00BD963D /* Authorization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authorization.swift; sourceTree = ""; }; D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManager.swift; sourceTree = ""; }; @@ -1479,6 +1497,42 @@ name = Localization; sourceTree = ""; }; + D093D7E82064135300BC3599 /* Fields */ = { + isa = PBXGroup; + children = ( + D093D7ED206413F600BC3599 /* SecureIdDataTypes.swift */, + D093D7E92064135A00BC3599 /* Identity */, + D093D7F320641A3F00BC3599 /* Phone */, + D093D7F720641A9600BC3599 /* Email */, + ); + name = Fields; + sourceTree = ""; + }; + D093D7E92064135A00BC3599 /* Identity */ = { + isa = PBXGroup; + children = ( + D093D7F02064194600BC3599 /* SecureIdIdentityField.swift */, + D093D7EA206413C900BC3599 /* SecureIdPassportIdentity.swift */, + ); + name = Identity; + sourceTree = ""; + }; + D093D7F320641A3F00BC3599 /* Phone */ = { + isa = PBXGroup; + children = ( + D093D7F420641A4900BC3599 /* SecureIdPhoneField.swift */, + ); + name = Phone; + sourceTree = ""; + }; + D093D7F720641A9600BC3599 /* Email */ = { + isa = PBXGroup; + children = ( + D093D7F820641AA500BC3599 /* SecureIdEmailField.swift */, + ); + name = Email; + sourceTree = ""; + }; D09D8BF71D4FAB1D0081DBEC = { isa = PBXGroup; children = ( @@ -1569,6 +1623,8 @@ children = ( D0BE303920619EE800FBE6D8 /* SecureIdForm.swift */, D0BE303C2061A29100FBE6D8 /* RequestSecureIdForm.swift */, + D0BE304A20627D9800FBE6D8 /* AccessSecureId.swift */, + D093D7E82064135300BC3599 /* Fields */, ); name = "Secure ID"; sourceTree = ""; @@ -1848,6 +1904,7 @@ D021E0DF1DB539FC00C6B04F /* StickerPack.swift in Sources */, D03B0D091D62255C00955575 /* EnqueueMessage.swift in Sources */, D0DFD5DF1FCDBCFD0039B3B1 /* CachedSentMediaReferences.swift in Sources */, + D093D7EE206413F600BC3599 /* SecureIdDataTypes.swift in Sources */, D00D343C1E6EC9770057B307 /* TelegramMediaGame.swift in Sources */, D033FEB01E61EB0200644997 /* PeerReportStatus.swift in Sources */, D050F2511E4A59C200988324 /* JoinLink.swift in Sources */, @@ -1895,6 +1952,7 @@ D0DF0CA81D82BF32008AEB01 /* PeerParticipants.swift in Sources */, D0FA8BA71E1FA6DF001E855B /* TelegramSecretChat.swift in Sources */, D03B0D5F1D631A6900955575 /* Serialization.swift in Sources */, + D093D7F920641AA500BC3599 /* SecureIdEmailField.swift in Sources */, D0C44B611FC616E200227BE0 /* SearchGroupMembers.swift in Sources */, D03B0D441D6319F900955575 /* CloudFileMediaResource.swift in Sources */, D018D3371E648ACF00C5E089 /* CreateChannel.swift in Sources */, @@ -1988,6 +2046,7 @@ D0F7AB2C1DCE889D009AD9A1 /* EditedMessageAttribute.swift in Sources */, D0FA8BAA1E1FB76E001E855B /* ManagedSecretChatOutgoingOperations.swift in Sources */, D00D97C71E32901700E5C2B6 /* PeerInputActivity.swift in Sources */, + D093D7EB206413C900BC3599 /* SecureIdPassportIdentity.swift in Sources */, D0FA8BAD1E1FD6E2001E855B /* MemoryBufferExtensions.swift in Sources */, D03B0CBF1D62234A00955575 /* Log.swift in Sources */, C2FD33E41E687BF1008D13D4 /* PeerPhotoUpdater.swift in Sources */, @@ -2023,6 +2082,7 @@ D033FEB61E61F3F900644997 /* BlockedPeers.swift in Sources */, D00C7CCC1E3620C30080C3D5 /* CachedChannelParticipants.swift in Sources */, D09BB6B41DB02C2B00A905C0 /* PendingMessageManager.swift in Sources */, + D093D7F520641A4900BC3599 /* SecureIdPhoneField.swift in Sources */, D0B167231F9F972E00976B40 /* LoggingSettings.swift in Sources */, D0BC387B1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */, D0BB7C5A1E5C8074001527C3 /* ChannelParticipants.swift in Sources */, @@ -2038,6 +2098,7 @@ D01C7ED31EF5DF83008305F1 /* LimitsConfiguration.swift in Sources */, D0F02CE51E9926C40065DEE2 /* ManagedConfigurationUpdates.swift in Sources */, D0528E6A1E65DD2100E2FEF5 /* WebpagePreview.swift in Sources */, + D0BE304B20627D9800FBE6D8 /* AccessSecureId.swift in Sources */, D0BEAF5D1E54941B00BD963D /* Authorization.swift in Sources */, D0C26D6C1FE286C3004ABF18 /* FetchChatList.swift in Sources */, D0B843831DA6EDB8005F29E1 /* CachedGroupData.swift in Sources */, @@ -2095,6 +2156,7 @@ D021E0E21DB5401A00C6B04F /* StickerManagement.swift in Sources */, D0BC38701E40853E0044D6FE /* UpdatePeers.swift in Sources */, D0F3A8A81E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift in Sources */, + D093D7F12064194600BC3599 /* SecureIdIdentityField.swift in Sources */, D03B0CE21D62249B00955575 /* InlineBotMessageAttribute.swift in Sources */, D0AB0B9A1D666520002C78E7 /* ManagedSynchronizePeerReadStates.swift in Sources */, D03B0D5B1D631A6900955575 /* Buffer.swift in Sources */, @@ -2161,6 +2223,7 @@ C26A37EF1E5E0C41006977AC /* ChannelParticipants.swift in Sources */, D00D343D1E6EC9770057B307 /* TelegramMediaGame.swift in Sources */, D0C26D6A1FE02402004ABF18 /* ManagedSynchronizeGroupedPeersOperations.swift in Sources */, + D093D7F22064194600BC3599 /* SecureIdIdentityField.swift in Sources */, D01C7F051EFC1C49008305F1 /* DeviceContact.swift in Sources */, D050F26A1E4A5B6D00988324 /* ManagedGlobalNotificationSettings.swift in Sources */, D050F26B1E4A5B6D00988324 /* ApplyMaxReadIndexInteractively.swift in Sources */, @@ -2252,6 +2315,7 @@ D0BEAF611E54ACF900BD963D /* AccountManager.swift in Sources */, D0F3CC791DDE2859008148FA /* SearchMessages.swift in Sources */, D0B8442B1DAB91E0005F29E1 /* NBMetadataCore.m in Sources */, + D093D7EF206413F600BC3599 /* SecureIdDataTypes.swift in Sources */, D03DC9141F82F89D001D584C /* RegularChatState.swift in Sources */, D0C44B621FC616E200227BE0 /* SearchGroupMembers.swift in Sources */, D00C7CD01E3628180080C3D5 /* UpdateCachedChannelParticipants.swift in Sources */, @@ -2280,8 +2344,10 @@ D0B8442A1DAB91E0005F29E1 /* NBAsYouTypeFormatter.m in Sources */, D07047B51F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift in Sources */, D0448C8F1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift in Sources */, + D093D7F620641A4900BC3599 /* SecureIdPhoneField.swift in Sources */, D0C26D6D1FE286C3004ABF18 /* FetchChatList.swift in Sources */, D073CE6E1DCBCF17007511FD /* ForwardSourceInfoAttribute.swift in Sources */, + D093D7FA20641AA500BC3599 /* SecureIdEmailField.swift in Sources */, D05A32E21E6F0982002760B4 /* UpdatedAccountPrivacySettings.swift in Sources */, D0613FD01E60520700202CDB /* ChannelMembers.swift in Sources */, D001F3E81E128A1C007A8C60 /* ChannelState.swift in Sources */, @@ -2302,6 +2368,7 @@ D0B418A91D7E0597004562A4 /* Buffer.swift in Sources */, D02ABC821E310E5D00CAE539 /* ManagedCloudChatRemoveMessagesOperations.swift in Sources */, C2FD33E51E687BF1008D13D4 /* PeerPhotoUpdater.swift in Sources */, + D0BE304C20627D9800FBE6D8 /* AccessSecureId.swift in Sources */, D0B8442E1DAB91E0005F29E1 /* NBMetadataCoreTestMapper.m in Sources */, D01A21AD1F38D10E00DDA104 /* SavedStickerItem.swift in Sources */, D03C53731DAD5CA9004C17B3 /* CachedGroupData.swift in Sources */, @@ -2396,6 +2463,7 @@ D099D7471EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift in Sources */, D058E0D21E8AD65C00A442DE /* StandaloneSendMessage.swift in Sources */, D03C536F1DAD5CA9004C17B3 /* BotInfo.swift in Sources */, + D093D7EC206413C900BC3599 /* SecureIdPassportIdentity.swift in Sources */, D0FA8BBA1E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */, D033FEB41E61F3C000644997 /* ReportPeer.swift in Sources */, D0FA8BAE1E1FD6E2001E855B /* MemoryBufferExtensions.swift in Sources */, diff --git a/TelegramCore/AccessSecureId.swift b/TelegramCore/AccessSecureId.swift new file mode 100644 index 0000000000..29af8fe48a --- /dev/null +++ b/TelegramCore/AccessSecureId.swift @@ -0,0 +1,185 @@ +import Foundation +#if os(macOS) + import PostboxMac + import MtProtoKitMac + import SwiftSignalKitMac +#else + import Postbox + import MtProtoKitDynamic + import SwiftSignalKit +#endif + +import TelegramCorePrivateModule + +private enum GenerateSecureSecretError { + case generic +} + +func decryptedSecureSecret(encryptedSecretData: Data, password: String) -> Data? { + guard let passwordData = password.data(using: .utf8) else { + return nil + } + let passwordHash = sha512Digest(passwordData) + let secretKey = passwordHash.subdata(in: 0 ..< 32) + let iv = passwordHash.subdata(in: 32 ..< (32 + 16)) + + var decryptedSecret = Data(count: encryptedSecretData.count) + + guard decryptedSecret.withUnsafeMutableBytes({ (decryptedSecretBytes: UnsafeMutablePointer) -> Bool in + return secretKey.withUnsafeBytes { (secretKeyBytes: UnsafePointer) -> Bool in + return iv.withUnsafeBytes { (ivBytes: UnsafePointer) -> Bool in + return encryptedSecretData.withUnsafeBytes { (encryptedSecretDataBytes: UnsafePointer) -> Bool in + var processedCount: Int = 0 + let result = CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES128), 0, secretKeyBytes, secretKey.count, ivBytes, encryptedSecretDataBytes, encryptedSecretData.count, decryptedSecretBytes, decryptedSecret.count, &processedCount) + if result != kCCSuccess { + return false + } + if processedCount != decryptedSecret.count { + return false + } + return true + } + } + } + }) else { + return nil + } + + guard decryptedSecret.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer) -> Bool in + var checksum: UInt32 = 0 + for i in 0 ..< decryptedSecret.count { + checksum += UInt32(bytes.advanced(by: i).pointee) + checksum = checksum % 255 + } + if checksum == 239 { + return true + } else { + return false + } + }) else { + return nil + } + + return decryptedSecret +} + +func encryptedSecureSecret(secretData: Data, password: String) -> Data? { + guard let passwordData = password.data(using: .utf8) else { + return nil + } + + let passwordHash = sha512Digest(passwordData) + let secretKey = passwordHash.subdata(in: 0 ..< 32) + let iv = passwordHash.subdata(in: 32 ..< (32 + 16)) + + var encryptedSecret = Data(count: secretData.count) + + guard encryptedSecret.withUnsafeMutableBytes({ (encryptedSecretBytes: UnsafeMutablePointer) -> Bool in + return secretKey.withUnsafeBytes { (secretKeyBytes: UnsafePointer) -> Bool in + return iv.withUnsafeBytes { (ivBytes: UnsafePointer) -> Bool in + return secretData.withUnsafeBytes { (secretDataBytes: UnsafePointer) -> Bool in + var processedCount: Int = 0 + let result = CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES128), 0, secretKeyBytes, secretKey.count, ivBytes, secretDataBytes, secretData.count, encryptedSecretBytes, encryptedSecret.count, &processedCount) + if result != kCCSuccess { + return false + } + if processedCount != encryptedSecret.count { + return false + } + return true + } + } + } + }) else { + return nil + } + + if decryptedSecureSecret(encryptedSecretData: encryptedSecret, password: password) != secretData { + return nil + } + + return encryptedSecret +} + +private func generateSecureSecret(network: Network, password: String) -> Signal { + var secretData = Data(count: 32) + guard secretData.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer) -> Bool in + let copyResult = SecRandomCopyBytes(nil, 32, bytes) + return copyResult == errSecSuccess + }) else { + return .fail(.generic) + } + + secretData.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer) in + while true { + var checksum: UInt32 = 0 + for i in 0 ..< secretData.count { + checksum += UInt32(bytes.advanced(by: i).pointee) + checksum = checksum % 255 + } + if checksum == 239 { + break + } else { + var i = secretData.count - 1 + inner: while i >= 0 { + var byte = bytes.advanced(by: i).pointee + if byte != 0xff { + byte += 1 + bytes.advanced(by: i).pointee = byte + break inner + } else { + byte = 0 + bytes.advanced(by: i).pointee = byte + } + i -= 1 + } + } + } + }) + + guard let encryptedSecret = encryptedSecureSecret(secretData: secretData, password: password) else { + return .fail(.generic) + } + + return updateTwoStepVerificationSecureSecret(network: network, password: password, updatedSecret: encryptedSecret) + |> mapError { _ -> GenerateSecureSecretError in + return .generic + } + |> map { _ -> Data in + return secretData + } +} + +public struct SecureIdAccessContext { + let secret: Data +} + +public enum SecureIdAccessError { + case generic + case passwordError(AuthorizationPasswordVerificationError) + case secretPasswordMismatch +} + +public func accessSecureId(network: Network, password: String) -> Signal { + return requestTwoStepVerifiationSettings(network: network, password: password) + |> mapError { error -> SecureIdAccessError in + return .passwordError(error) + } + |> mapToSignal { settings -> Signal in + if let secureSecret = settings.secureSecret { + if let decryptedSecret = decryptedSecureSecret(encryptedSecretData: secureSecret, password: "q") { //password + return .single(SecureIdAccessContext(secret: decryptedSecret)) + } else { + return .fail(.secretPasswordMismatch) + } + } else { + return generateSecureSecret(network: network, password: password) + |> mapError { _ -> SecureIdAccessError in + return SecureIdAccessError.generic + } + |> map { decryptedSecret in + return SecureIdAccessContext(secret: decryptedSecret) + } + } + } +} diff --git a/TelegramCore/Account.swift b/TelegramCore/Account.swift index fbdc72c9e7..1b587a9071 100644 --- a/TelegramCore/Account.swift +++ b/TelegramCore/Account.swift @@ -336,12 +336,12 @@ public func accountWithId(networkArguments: NetworkInitializationArguments, id: } public struct TwoStepAuthData { - let nextSalt: Data - let currentSalt: Data? - let hasRecovery: Bool - let currentHint: String? - let unconfirmedEmailPattern: String? - let secretRandom: Data + public let nextSalt: Data + public let currentSalt: Data? + public let hasRecovery: Bool + public let currentHint: String? + public let unconfirmedEmailPattern: String? + public let secretRandom: Data } public func twoStepAuthData(_ network: Network) -> Signal { @@ -378,6 +378,17 @@ func sha256Digest(_ data : Data) -> Data { return res } +func sha512Digest(_ data : Data) -> Data { + var res = Data() + res.count = Int(CC_SHA512_DIGEST_LENGTH) + res.withUnsafeMutableBytes { mutableBytes -> Void in + data.withUnsafeBytes { bytes -> Void in + CC_SHA512(bytes, CC_LONG(data.count), mutableBytes) + } + } + return res +} + public func verifyPassword(_ account: UnauthorizedAccount, password: String) -> Signal { return twoStepAuthData(account.network) |> mapToSignal { authData -> Signal in diff --git a/TelegramCore/RequestSecureIdForm.swift b/TelegramCore/RequestSecureIdForm.swift index ab3e06e0c7..70385fe5f7 100644 --- a/TelegramCore/RequestSecureIdForm.swift +++ b/TelegramCore/RequestSecureIdForm.swift @@ -13,7 +13,14 @@ public enum RequestSecureIdFormError { case generic } -private func parseFieldType(_ type: Api.AuthFieldType) -> SecureIdFieldType { +public enum RequestedSecureIdField { + case identity + case address + case phone + case email +} + +private func parseRequestedFieldType(_ type: Api.AuthFieldType) -> RequestedSecureIdField { switch type { case .authFieldTypeIdentity: return .identity @@ -35,7 +42,7 @@ private func parseFileReference(_ file: Api.SecureFile) -> SecureIdFileReference } } -private func parseValue(_ value: Api.SecureValue) -> SecureIdFieldValue { +/*private func parseValue(_ value: Api.SecureValue) -> SecureIdFieldValue { switch value { case let .secureValueEmpty(name): return SecureIdFieldValue(name: name, data: .none) @@ -46,16 +53,75 @@ private func parseValue(_ value: Api.SecureValue) -> SecureIdFieldValue { case let .secureValueText(name, text, hash): return SecureIdFieldValue(name: name, data: .text(text: text, hash: hash)) } -} +}*/ -private func parseField(_ field: Api.AuthField) -> SecureIdField { - switch field { - case let .authField(_, type, data, document): - return SecureIdField(type: parseFieldType(type), value: parseValue(data)) +private func parseIdentityField(context: SecureIdAccessContext, value: Api.SecureValue, document: Api.SecureValue?) -> SecureIdIdentityField? { + switch value { + case let .secureValueData(name, data, hash, secret): + return nil + default: + return nil } } -public func requestSecureIdForm(postbox: Postbox, network: Network, peerId: PeerId, scope: [String], origin: String?, packageName: String?, bundleId: String?, publicKey: String?) -> Signal { +private func parsePhoneField(context: SecureIdAccessContext, value: Api.SecureValue) -> SecureIdPhoneField? { + switch value { + case let .secureValueText(name, text, _): + return SecureIdPhoneField(rawValue: text) + default: + return nil + } +} + +private func parseEmailField(context: SecureIdAccessContext, value: Api.SecureValue) -> SecureIdEmailField? { + switch value { + case let .secureValueText(name, text, _): + return SecureIdEmailField(rawValue: text) + default: + return nil + } +} + +private func parseFields(context: SecureIdAccessContext, fields: [Api.AuthField]) -> SecureIdFields { + var result = SecureIdFields(identity: nil, phone: nil, email: nil) + for field in fields { + switch field { + case let .authField(_, type, data, document): + switch type { + case .authFieldTypeIdentity: + if let identity = parseIdentityField(context: context, value: data, document: document) { + result.identity = .value(identity) + } else { + result.identity = .empty + } + case .authFieldTypeAddress: + break + case .authFieldTypePhone: + if let phone = parsePhoneField(context: context, value: data) { + result.phone = .value(phone) + } else { + result.phone = .empty + } + case .authFieldTypeEmail: + if let email = parseEmailField(context: context, value: data) { + result.email = .value(email) + } else { + result.email = .empty + } + } + } + } + return result +} + +public struct EncryptedSecureIdForm { + public let peerId: PeerId + public let requestedFields: [RequestedSecureIdField] + + let encryptedFields: [Api.AuthField] +} + +public func requestSecureIdForm(postbox: Postbox, network: Network, peerId: PeerId, scope: [String], origin: String?, packageName: String?, bundleId: String?, publicKey: String?) -> Signal { if peerId.namespace != Namespaces.Peer.CloudUser { return .fail(.generic) } @@ -76,10 +142,10 @@ public func requestSecureIdForm(postbox: Postbox, network: Network, peerId: Peer |> mapError { _ -> RequestSecureIdFormError in return .generic } - |> mapToSignal { result -> Signal in - return postbox.modify { modifier -> SecureIdForm in + |> mapToSignal { result -> Signal in + return postbox.modify { modifier -> EncryptedSecureIdForm in switch result { - case let .authorizationForm(_, botId, fields, acceptedFields, users): + case let .authorizationForm(_, botId, fields, _, users): var peers: [Peer] = [] for user in users { let parsed = TelegramUser(user: user) @@ -89,10 +155,17 @@ public func requestSecureIdForm(postbox: Postbox, network: Network, peerId: Peer return updated }) - let parsedFields = fields.map(parseField) - - return SecureIdForm(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: botId), fields: parsedFields) + return EncryptedSecureIdForm(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: botId), requestedFields: fields.map { field -> RequestedSecureIdField in + switch field { + case let .authField(_, type, data, document): + return parseRequestedFieldType(type) + } + }, encryptedFields: fields) } } |> mapError { _ in return RequestSecureIdFormError.generic } } } + +public func decryptedSecureIdForm(context: SecureIdAccessContext, form: EncryptedSecureIdForm) -> SecureIdForm? { + return SecureIdForm(peerId: form.peerId, fields: parseFields(context: context, fields: form.encryptedFields)) +} diff --git a/TelegramCore/SecureIdDataTypes.swift b/TelegramCore/SecureIdDataTypes.swift new file mode 100644 index 0000000000..d9ef2f4d90 --- /dev/null +++ b/TelegramCore/SecureIdDataTypes.swift @@ -0,0 +1,43 @@ +import Foundation + +public struct SecureIdDate: Equatable { + private var timestamp: Int32 + + public init(timestamp: Int32) { + self.timestamp = timestamp + } + + public static func ==(lhs: SecureIdDate, rhs: SecureIdDate) -> Bool { + if lhs.timestamp != rhs.timestamp { + return false + } + return true + } +} + +public enum SecureIdGender { + case male + case female +} + +public enum SecureIdFileReference: Equatable { + case none + case file(id: Int64, accessHash: Int64, size: Int32, datacenterId: Int32, fileHash: String) + + public static func ==(lhs: SecureIdFileReference, rhs: SecureIdFileReference) -> Bool { + switch lhs { + case .none: + if case .none = rhs { + return true + } else { + return false + } + case let .file(id, accessHash, size, datacenterId, fileHash): + if case .file(id, accessHash, size, datacenterId, fileHash) = rhs { + return true + } else { + return false + } + } + } +} diff --git a/TelegramCore/SecureIdEmailField.swift b/TelegramCore/SecureIdEmailField.swift new file mode 100644 index 0000000000..763f3be47b --- /dev/null +++ b/TelegramCore/SecureIdEmailField.swift @@ -0,0 +1,16 @@ +import Foundation + +public struct SecureIdEmailField: Equatable { + public let rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + public static func ==(lhs: SecureIdEmailField, rhs: SecureIdEmailField) -> Bool { + if lhs.rawValue != rhs.rawValue { + return false + } + return true + } +} diff --git a/TelegramCore/SecureIdForm.swift b/TelegramCore/SecureIdForm.swift index 08875be58e..292c08e3cd 100644 --- a/TelegramCore/SecureIdForm.swift +++ b/TelegramCore/SecureIdForm.swift @@ -5,36 +5,77 @@ import Foundation import Postbox #endif -public enum SecureIdFieldType { - case identity - case address - case phone - case email +public enum SecureIdFieldValue: Equatable where T: Equatable { + case empty + case value(T) + + public static func ==(lhs: SecureIdFieldValue, rhs: SecureIdFieldValue) -> Bool { + switch lhs { + case .empty: + if case .empty = rhs { + return true + } else { + return false + } + case let .value(value): + if case .value(value) = rhs { + return true + } else { + return false + } + } + } } -public enum SecureIdFileReference { - case none - case file(id: Int64, accessHash: Int64, size: Int32, datacenterId: Int32, fileHash: String) +public struct SecureIdFields: Equatable { + public var identity: SecureIdFieldValue? + public var phone: SecureIdFieldValue? + public var email: SecureIdFieldValue? + + public static func ==(lhs: SecureIdFields, rhs: SecureIdFields) -> Bool { + if lhs.identity != rhs.identity { + return false + } + if lhs.phone != rhs.phone { + return false + } + if lhs.email != rhs.email { + return false + } + return true + } } -public enum SecureIdFieldValueData { - case none - case data(data: Data, hash: String, secret: Data) - case files(files: [SecureIdFileReference], hash: String, secret: Data) - case text(text: String, hash: String) -} - -public struct SecureIdFieldValue { - public let name: String - public let data: SecureIdFieldValueData -} - -public struct SecureIdField { - public let type: SecureIdFieldType - public let value: SecureIdFieldValue +public enum SecureIdField: Equatable { + case identity(SecureIdIdentityField) + case phone(SecureIdPhoneField) + case email(SecureIdEmailField) + + public static func ==(lhs: SecureIdField, rhs: SecureIdField) -> Bool { + switch lhs { + case let .identity(field): + if case .identity(field) = rhs { + return true + } else { + return false + } + case let .phone(field): + if case .phone(field) = rhs { + return true + } else { + return false + } + case let .email(field): + if case .email(field) = rhs { + return true + } else { + return false + } + } + } } public struct SecureIdForm { public let peerId: PeerId - public let fields: [SecureIdField] + public let fields: SecureIdFields } diff --git a/TelegramCore/SecureIdIdentityField.swift b/TelegramCore/SecureIdIdentityField.swift new file mode 100644 index 0000000000..d67802a7a2 --- /dev/null +++ b/TelegramCore/SecureIdIdentityField.swift @@ -0,0 +1,16 @@ +import Foundation + +public struct SecureIdIdentityField: Equatable { + public var passport: SecureIdPassportIdentity? + + public init(passport: SecureIdPassportIdentity?) { + self.passport = passport + } + + public static func ==(lhs: SecureIdIdentityField, rhs: SecureIdIdentityField) -> Bool { + if lhs.passport != rhs.passport { + return false + } + return true + } +} diff --git a/TelegramCore/SecureIdPassportIdentity.swift b/TelegramCore/SecureIdPassportIdentity.swift new file mode 100644 index 0000000000..1d3a4039f1 --- /dev/null +++ b/TelegramCore/SecureIdPassportIdentity.swift @@ -0,0 +1,41 @@ +import Foundation + +public struct SecureIdPassportIdentity: Equatable { + public var id: String + public var firstName: String + public var lastName: String + public var birthdate: SecureIdDate + public var countryCode: String + public var gender: SecureIdGender + + public init(id: String, firstName: String, lastName: String, birthdate: SecureIdDate, countryCode: String, gender: SecureIdGender) { + self.id = id + self.firstName = firstName + self.lastName = lastName + self.birthdate = birthdate + self.countryCode = countryCode + self.gender = gender + } + + public static func ==(lhs: SecureIdPassportIdentity, rhs: SecureIdPassportIdentity) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.firstName != rhs.firstName { + return false + } + if lhs.lastName != rhs.lastName { + return false + } + if lhs.birthdate != rhs.birthdate { + return false + } + if lhs.countryCode != rhs.countryCode { + return false + } + if lhs.gender != rhs.gender { + return false + } + return true + } +} diff --git a/TelegramCore/SecureIdPhoneField.swift b/TelegramCore/SecureIdPhoneField.swift new file mode 100644 index 0000000000..3d7161ada7 --- /dev/null +++ b/TelegramCore/SecureIdPhoneField.swift @@ -0,0 +1,16 @@ +import Foundation + +public struct SecureIdPhoneField: Equatable { + public let rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + public static func ==(lhs: SecureIdPhoneField, rhs: SecureIdPhoneField) -> Bool { + if lhs.rawValue != rhs.rawValue { + return false + } + return true + } +} diff --git a/TelegramCore/TwoStepVerification.swift b/TelegramCore/TwoStepVerification.swift index 139f396026..b430c12baa 100644 --- a/TelegramCore/TwoStepVerification.swift +++ b/TelegramCore/TwoStepVerification.swift @@ -29,10 +29,11 @@ public func twoStepVerificationConfiguration(account: Account) -> Signal Signal { - return twoStepAuthData(account.network) +public func requestTwoStepVerifiationSettings(network: Network, password: String) -> Signal { + return twoStepAuthData(network) |> mapToSignal { authData -> Signal in var data = Data() data.append(authData.currentSalt!) @@ -40,11 +41,11 @@ public func requestTwoStepVerifiationSettings(account: Account, password: String data.append(authData.currentSalt!) let currentPasswordHash = sha256Digest(data) - return account.network.request(Api.functions.account.getPasswordSettings(currentPasswordHash: Buffer(data: currentPasswordHash)), automaticFloodWait: false) + return network.request(Api.functions.account.getPasswordSettings(currentPasswordHash: Buffer(data: currentPasswordHash)), automaticFloodWait: false) |> map { result -> TwoStepVerificationSettings in switch result { - case let .passwordSettings(email, _): - return TwoStepVerificationSettings(email: email) + case let .passwordSettings(email, secureSecret): + return TwoStepVerificationSettings(email: email, secureSecret: secureSecret.size == 0 ? nil : secureSecret.makeData()) } } } @@ -74,12 +75,25 @@ public enum UpdatedTwoStepVerificationPassword { case password(password: String, hint: String, email: String?) } -public func updateTwoStepVerificationPassword(account: Account, currentPassword: String?, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal { - return twoStepAuthData(account.network) +public func updateTwoStepVerificationPassword(network: Network, currentPassword: String?, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal { + return twoStepAuthData(network) |> mapError { _ -> UpdateTwoStepVerificationPasswordError in return .generic } - |> mapToSignal { authData -> Signal in + |> mapToSignal { authData -> Signal<(TwoStepAuthData, Data?), UpdateTwoStepVerificationPasswordError> in + if authData.currentSalt != nil { + return requestTwoStepVerifiationSettings(network: network, password: currentPassword ?? "") + |> mapError { _ -> UpdateTwoStepVerificationPasswordError in + return .generic + } + |> map { settings in + return (authData, settings.secureSecret) + } + } else { + return .single((authData, nil)) + } + } + |> mapToSignal { authData, secureSecret -> Signal in let currentPasswordHash: Buffer if let currentSalt = authData.currentSalt { var data = Data() @@ -100,7 +114,7 @@ public func updateTwoStepVerificationPassword(account: Account, currentPassword: flags |= (1 << 0) } - return account.network.request(Api.functions.account.updatePasswordSettings(currentPasswordHash: currentPasswordHash, newSettings: .passwordInputSettings(flags: flags, newSalt: Buffer(data: Data()), newPasswordHash: Buffer(data: Data()), hint: "", email: "", newSecureSecret: nil)), automaticFloodWait: false) + return network.request(Api.functions.account.updatePasswordSettings(currentPasswordHash: currentPasswordHash, newSettings: .passwordInputSettings(flags: flags, newSalt: Buffer(data: Data()), newPasswordHash: Buffer(data: Data()), hint: "", email: "", newSecureSecret: nil)), automaticFloodWait: false) |> mapError { _ -> UpdateTwoStepVerificationPasswordError in return .generic } @@ -126,14 +140,24 @@ public func updateTwoStepVerificationPassword(account: Account, currentPassword: updatedData.append(password.data(using: .utf8, allowLossyConversion: true)!) updatedData.append(nextSalt) + var updatedSecureSecret: Data? + if let encryptedSecret = secureSecret { + flags |= 1 << 2 + if let decryptedSecret = decryptedSecureSecret(encryptedSecretData: encryptedSecret, password: currentPassword ?? "") { + updatedSecureSecret = encryptedSecureSecret(secretData: decryptedSecret, password: password) + } else { + updatedSecureSecret = Data() + } + } + let updatedPasswordHash = sha256Digest(updatedData) - return account.network.request(Api.functions.account.updatePasswordSettings(currentPasswordHash: currentPasswordHash, newSettings: Api.account.PasswordInputSettings.passwordInputSettings(flags: flags, newSalt: Buffer(data: nextSalt), newPasswordHash: Buffer(data: updatedPasswordHash), hint: hint, email: email, newSecureSecret: nil)), automaticFloodWait: false) + return network.request(Api.functions.account.updatePasswordSettings(currentPasswordHash: currentPasswordHash, newSettings: Api.account.PasswordInputSettings.passwordInputSettings(flags: flags, newSalt: Buffer(data: nextSalt), newPasswordHash: Buffer(data: updatedPasswordHash), hint: hint, email: email, newSecureSecret: updatedSecureSecret.flatMap(Buffer.init))), automaticFloodWait: false) |> map { _ -> UpdateTwoStepVerificationPasswordResult in return .password(password: password, pendingEmailPattern: nil) } |> `catch` { error -> Signal in if error.errorDescription == "EMAIL_UNCONFIRMED" { - return twoStepAuthData(account.network) + return twoStepAuthData(network) |> map { result -> UpdateTwoStepVerificationPasswordResult in return .password(password: password, pendingEmailPattern: result.unconfirmedEmailPattern) } @@ -152,6 +176,41 @@ public func updateTwoStepVerificationPassword(account: Account, currentPassword: } } +enum UpdateTwoStepVerificationSecureSecretResult { + case success +} + +enum UpdateTwoStepVerificationSecureSecretError { + case generic +} + +func updateTwoStepVerificationSecureSecret(network: Network, password: String, updatedSecret: Data) -> Signal { + return twoStepAuthData(network) + |> mapError { _ -> UpdateTwoStepVerificationSecureSecretError in + return .generic + } + |> mapToSignal { authData -> Signal in + guard let currentSalt = authData.currentSalt else { + return .fail(.generic) + } + + var data = Data() + data.append(currentSalt) + data.append(password.data(using: .utf8, allowLossyConversion: true)!) + data.append(currentSalt) + let currentPasswordHash = Buffer(data: sha256Digest(data)) + + let flags: Int32 = (1 << 2) + return network.request(Api.functions.account.updatePasswordSettings(currentPasswordHash: currentPasswordHash, newSettings: .passwordInputSettings(flags: flags, newSalt: nil, newPasswordHash: nil, hint: nil, email: nil, newSecureSecret: Buffer(data: updatedSecret))), automaticFloodWait: false) + |> mapError { _ -> UpdateTwoStepVerificationSecureSecretError in + return .generic + } + |> map { _ -> UpdateTwoStepVerificationSecureSecretResult in + return .success + } + } +} + public func updateTwoStepVerificationEmail(account: Account, currentPassword: String, updatedEmail: String) -> Signal { return twoStepAuthData(account.network) |> mapError { _ -> UpdateTwoStepVerificationPasswordError in