mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
no message
This commit is contained in:
@@ -362,16 +362,15 @@
|
||||
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 */; };
|
||||
D093D7EB206413C900BC3599 /* SecureIdIdentityValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7EA206413C900BC3599 /* SecureIdIdentityValue.swift */; };
|
||||
D093D7EC206413C900BC3599 /* SecureIdIdentityValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7EA206413C900BC3599 /* SecureIdIdentityValue.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 */; };
|
||||
D093D7F520641A4900BC3599 /* SecureIdPhoneValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7F420641A4900BC3599 /* SecureIdPhoneValue.swift */; };
|
||||
D093D7F620641A4900BC3599 /* SecureIdPhoneValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7F420641A4900BC3599 /* SecureIdPhoneValue.swift */; };
|
||||
D093D7F920641AA500BC3599 /* SecureIdEmailValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7F820641AA500BC3599 /* SecureIdEmailValue.swift */; };
|
||||
D093D7FA20641AA500BC3599 /* SecureIdEmailValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D7F820641AA500BC3599 /* SecureIdEmailValue.swift */; };
|
||||
D093D806206539D000BC3599 /* SaveSecureIdValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D093D805206539D000BC3599 /* SaveSecureIdValue.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 */; };
|
||||
@@ -852,11 +851,11 @@
|
||||
D08CAA8B1ED81EDF0000FDA8 /* Localizations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Localizations.swift; sourceTree = "<group>"; };
|
||||
D08F4A651E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeInstalledStickerPacksOperations.swift; sourceTree = "<group>"; };
|
||||
D08F4A681E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeInstalledStickerPacksOperations.swift; sourceTree = "<group>"; };
|
||||
D093D7EA206413C900BC3599 /* SecureIdPassportIdentity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdPassportIdentity.swift; sourceTree = "<group>"; };
|
||||
D093D7EA206413C900BC3599 /* SecureIdIdentityValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdIdentityValue.swift; sourceTree = "<group>"; };
|
||||
D093D7ED206413F600BC3599 /* SecureIdDataTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdDataTypes.swift; sourceTree = "<group>"; };
|
||||
D093D7F02064194600BC3599 /* SecureIdIdentityField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdIdentityField.swift; sourceTree = "<group>"; };
|
||||
D093D7F420641A4900BC3599 /* SecureIdPhoneField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdPhoneField.swift; sourceTree = "<group>"; };
|
||||
D093D7F820641AA500BC3599 /* SecureIdEmailField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdEmailField.swift; sourceTree = "<group>"; };
|
||||
D093D7F420641A4900BC3599 /* SecureIdPhoneValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdPhoneValue.swift; sourceTree = "<group>"; };
|
||||
D093D7F820641AA500BC3599 /* SecureIdEmailValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdEmailValue.swift; sourceTree = "<group>"; };
|
||||
D093D805206539D000BC3599 /* SaveSecureIdValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveSecureIdValue.swift; sourceTree = "<group>"; };
|
||||
D099D7451EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelMessageStateVersionAttribute.swift; sourceTree = "<group>"; };
|
||||
D099D7481EEF418D00A3128C /* HistoryViewChannelStateValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoryViewChannelStateValidation.swift; sourceTree = "<group>"; };
|
||||
D099EA1B1DE72867001AF5A8 /* PeerCommands.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerCommands.swift; sourceTree = "<group>"; };
|
||||
@@ -1497,40 +1496,31 @@
|
||||
name = Localization;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D093D7E82064135300BC3599 /* Fields */ = {
|
||||
D093D7E82064135300BC3599 /* Values */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D093D7ED206413F600BC3599 /* SecureIdDataTypes.swift */,
|
||||
D093D7E92064135A00BC3599 /* Identity */,
|
||||
D093D7F320641A3F00BC3599 /* Phone */,
|
||||
D093D7F720641A9600BC3599 /* Email */,
|
||||
D093D7F320641A3F00BC3599 /* Other */,
|
||||
);
|
||||
name = Fields;
|
||||
name = Values;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D093D7E92064135A00BC3599 /* Identity */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D093D7F02064194600BC3599 /* SecureIdIdentityField.swift */,
|
||||
D093D7EA206413C900BC3599 /* SecureIdPassportIdentity.swift */,
|
||||
D093D7EA206413C900BC3599 /* SecureIdIdentityValue.swift */,
|
||||
);
|
||||
name = Identity;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D093D7F320641A3F00BC3599 /* Phone */ = {
|
||||
D093D7F320641A3F00BC3599 /* Other */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D093D7F420641A4900BC3599 /* SecureIdPhoneField.swift */,
|
||||
D093D7F420641A4900BC3599 /* SecureIdPhoneValue.swift */,
|
||||
D093D7F820641AA500BC3599 /* SecureIdEmailValue.swift */,
|
||||
);
|
||||
name = Phone;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D093D7F720641A9600BC3599 /* Email */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D093D7F820641AA500BC3599 /* SecureIdEmailField.swift */,
|
||||
);
|
||||
name = Email;
|
||||
name = Other;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D09D8BF71D4FAB1D0081DBEC = {
|
||||
@@ -1621,10 +1611,11 @@
|
||||
D0BE303820619E9E00FBE6D8 /* Secure ID */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D0BE303920619EE800FBE6D8 /* SecureIdForm.swift */,
|
||||
D0BE303C2061A29100FBE6D8 /* RequestSecureIdForm.swift */,
|
||||
D0BE304A20627D9800FBE6D8 /* AccessSecureId.swift */,
|
||||
D093D7E82064135300BC3599 /* Fields */,
|
||||
D0BE303920619EE800FBE6D8 /* SecureIdForm.swift */,
|
||||
D093D805206539D000BC3599 /* SaveSecureIdValue.swift */,
|
||||
D093D7E82064135300BC3599 /* Values */,
|
||||
);
|
||||
name = "Secure ID";
|
||||
sourceTree = "<group>";
|
||||
@@ -1952,7 +1943,7 @@
|
||||
D0DF0CA81D82BF32008AEB01 /* PeerParticipants.swift in Sources */,
|
||||
D0FA8BA71E1FA6DF001E855B /* TelegramSecretChat.swift in Sources */,
|
||||
D03B0D5F1D631A6900955575 /* Serialization.swift in Sources */,
|
||||
D093D7F920641AA500BC3599 /* SecureIdEmailField.swift in Sources */,
|
||||
D093D7F920641AA500BC3599 /* SecureIdEmailValue.swift in Sources */,
|
||||
D0C44B611FC616E200227BE0 /* SearchGroupMembers.swift in Sources */,
|
||||
D03B0D441D6319F900955575 /* CloudFileMediaResource.swift in Sources */,
|
||||
D018D3371E648ACF00C5E089 /* CreateChannel.swift in Sources */,
|
||||
@@ -2046,7 +2037,7 @@
|
||||
D0F7AB2C1DCE889D009AD9A1 /* EditedMessageAttribute.swift in Sources */,
|
||||
D0FA8BAA1E1FB76E001E855B /* ManagedSecretChatOutgoingOperations.swift in Sources */,
|
||||
D00D97C71E32901700E5C2B6 /* PeerInputActivity.swift in Sources */,
|
||||
D093D7EB206413C900BC3599 /* SecureIdPassportIdentity.swift in Sources */,
|
||||
D093D7EB206413C900BC3599 /* SecureIdIdentityValue.swift in Sources */,
|
||||
D0FA8BAD1E1FD6E2001E855B /* MemoryBufferExtensions.swift in Sources */,
|
||||
D03B0CBF1D62234A00955575 /* Log.swift in Sources */,
|
||||
C2FD33E41E687BF1008D13D4 /* PeerPhotoUpdater.swift in Sources */,
|
||||
@@ -2082,7 +2073,7 @@
|
||||
D033FEB61E61F3F900644997 /* BlockedPeers.swift in Sources */,
|
||||
D00C7CCC1E3620C30080C3D5 /* CachedChannelParticipants.swift in Sources */,
|
||||
D09BB6B41DB02C2B00A905C0 /* PendingMessageManager.swift in Sources */,
|
||||
D093D7F520641A4900BC3599 /* SecureIdPhoneField.swift in Sources */,
|
||||
D093D7F520641A4900BC3599 /* SecureIdPhoneValue.swift in Sources */,
|
||||
D0B167231F9F972E00976B40 /* LoggingSettings.swift in Sources */,
|
||||
D0BC387B1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */,
|
||||
D0BB7C5A1E5C8074001527C3 /* ChannelParticipants.swift in Sources */,
|
||||
@@ -2103,6 +2094,7 @@
|
||||
D0C26D6C1FE286C3004ABF18 /* FetchChatList.swift in Sources */,
|
||||
D0B843831DA6EDB8005F29E1 /* CachedGroupData.swift in Sources */,
|
||||
D0E35A121DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */,
|
||||
D093D806206539D000BC3599 /* SaveSecureIdValue.swift in Sources */,
|
||||
C239BE9C1E630CA700C2C453 /* UpdatePinnedMessage.swift in Sources */,
|
||||
D08CAA7D1ED77EE90000FDA8 /* LocalizationSettings.swift in Sources */,
|
||||
D0B844531DAC0773005F29E1 /* TelegramUserPresence.swift in Sources */,
|
||||
@@ -2156,7 +2148,6 @@
|
||||
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 */,
|
||||
@@ -2223,7 +2214,6 @@
|
||||
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 */,
|
||||
@@ -2344,10 +2334,10 @@
|
||||
D0B8442A1DAB91E0005F29E1 /* NBAsYouTypeFormatter.m in Sources */,
|
||||
D07047B51F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift in Sources */,
|
||||
D0448C8F1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift in Sources */,
|
||||
D093D7F620641A4900BC3599 /* SecureIdPhoneField.swift in Sources */,
|
||||
D093D7F620641A4900BC3599 /* SecureIdPhoneValue.swift in Sources */,
|
||||
D0C26D6D1FE286C3004ABF18 /* FetchChatList.swift in Sources */,
|
||||
D073CE6E1DCBCF17007511FD /* ForwardSourceInfoAttribute.swift in Sources */,
|
||||
D093D7FA20641AA500BC3599 /* SecureIdEmailField.swift in Sources */,
|
||||
D093D7FA20641AA500BC3599 /* SecureIdEmailValue.swift in Sources */,
|
||||
D05A32E21E6F0982002760B4 /* UpdatedAccountPrivacySettings.swift in Sources */,
|
||||
D0613FD01E60520700202CDB /* ChannelMembers.swift in Sources */,
|
||||
D001F3E81E128A1C007A8C60 /* ChannelState.swift in Sources */,
|
||||
@@ -2463,7 +2453,7 @@
|
||||
D099D7471EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift in Sources */,
|
||||
D058E0D21E8AD65C00A442DE /* StandaloneSendMessage.swift in Sources */,
|
||||
D03C536F1DAD5CA9004C17B3 /* BotInfo.swift in Sources */,
|
||||
D093D7EC206413C900BC3599 /* SecureIdPassportIdentity.swift in Sources */,
|
||||
D093D7EC206413C900BC3599 /* SecureIdIdentityValue.swift in Sources */,
|
||||
D0FA8BBA1E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */,
|
||||
D033FEB41E61F3C000644997 /* ReportPeer.swift in Sources */,
|
||||
D0FA8BAE1E1FD6E2001E855B /* MemoryBufferExtensions.swift in Sources */,
|
||||
|
||||
@@ -15,11 +15,58 @@ private enum GenerateSecureSecretError {
|
||||
case generic
|
||||
}
|
||||
|
||||
func decryptedSecureSecret(encryptedSecretData: Data, password: String) -> Data? {
|
||||
func encryptSecureData(key: Data, iv: Data, data: Data, decrypt: Bool) -> Data? {
|
||||
if data.count % 16 != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var processedData = Data(count: data.count)
|
||||
guard processedData.withUnsafeMutableBytes({ (processedDataBytes: UnsafeMutablePointer<Int8>) -> Bool in
|
||||
return key.withUnsafeBytes { (keyBytes: UnsafePointer<Int8>) -> Bool in
|
||||
return iv.withUnsafeBytes { (ivBytes: UnsafePointer<Int8>) -> Bool in
|
||||
return data.withUnsafeBytes { (dataBytes: UnsafePointer<Int8>) -> Bool in
|
||||
var processedCount: Int = 0
|
||||
let result = CCCrypt(CCOperation(decrypt ? kCCDecrypt : kCCEncrypt), CCAlgorithm(kCCAlgorithmAES128), 0, keyBytes, key.count, ivBytes, dataBytes, data.count, processedDataBytes, processedData.count, &processedCount)
|
||||
if result != kCCSuccess {
|
||||
return false
|
||||
}
|
||||
if processedCount != processedData.count {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return processedData
|
||||
}
|
||||
|
||||
func verifySecureSecret(_ data: Data) -> Bool {
|
||||
guard data.withUnsafeBytes({ (bytes: UnsafePointer<UInt8>) -> Bool in
|
||||
var checksum: UInt32 = 0
|
||||
for i in 0 ..< data.count {
|
||||
checksum += UInt32(bytes.advanced(by: i).pointee)
|
||||
checksum = checksum % 255
|
||||
}
|
||||
if checksum == 239 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}) else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func decryptedSecureSecret(encryptedSecretData: Data, password: String, salt: Data, hash: Int64) -> Data? {
|
||||
guard let passwordData = password.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
let passwordHash = sha512Digest(passwordData)
|
||||
let passwordHash = sha512Digest(salt + passwordData + salt)
|
||||
let secretKey = passwordHash.subdata(in: 0 ..< 32)
|
||||
let iv = passwordHash.subdata(in: 32 ..< (32 + 16))
|
||||
|
||||
@@ -45,30 +92,45 @@ func decryptedSecureSecret(encryptedSecretData: Data, password: String) -> Data?
|
||||
return nil
|
||||
}
|
||||
|
||||
guard decryptedSecret.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<UInt8>) -> Bool in
|
||||
var checksum: UInt32 = 0
|
||||
for i in 0 ..< decryptedSecret.count {
|
||||
checksum += UInt32(bytes.advanced(by: i).pointee)
|
||||
checksum = checksum % 255
|
||||
if !verifySecureSecret(decryptedSecret) {
|
||||
return nil
|
||||
}
|
||||
if checksum == 239 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
|
||||
let secretHashData = sha256Digest(decryptedSecret)
|
||||
var secretHash: Int64 = 0
|
||||
secretHashData.withUnsafeBytes { (bytes: UnsafePointer<Int8>) -> Void in
|
||||
memcpy(&secretHash, bytes.advanced(by: secretHashData.count - 8), 8)
|
||||
}
|
||||
}) else {
|
||||
|
||||
if secretHash != hash {
|
||||
return nil
|
||||
}
|
||||
|
||||
return decryptedSecret
|
||||
}
|
||||
|
||||
func encryptedSecureSecret(secretData: Data, password: String) -> Data? {
|
||||
func encryptedSecureSecret(secretData: Data, password: String, inputSalt: Data) -> (data: Data, salt: Data, hash: Int64)? {
|
||||
let secretHashData = sha256Digest(secretData)
|
||||
var secretHash: Int64 = 0
|
||||
secretHashData.withUnsafeBytes { (bytes: UnsafePointer<Int8>) -> Void in
|
||||
memcpy(&secretHash, bytes.advanced(by: secretHashData.count - 8), 8)
|
||||
}
|
||||
|
||||
guard let passwordData = password.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let passwordHash = sha512Digest(passwordData)
|
||||
var randomSalt = Data(count: 8)
|
||||
guard randomSalt.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<Int8>) -> Bool in
|
||||
let result = SecRandomCopyBytes(nil, randomSalt.count, bytes)
|
||||
return result == errSecSuccess
|
||||
}) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let secretSalt = inputSalt + randomSalt
|
||||
|
||||
let passwordHash = sha512Digest(secretSalt + passwordData + secretSalt)
|
||||
let secretKey = passwordHash.subdata(in: 0 ..< 32)
|
||||
let iv = passwordHash.subdata(in: 32 ..< (32 + 16))
|
||||
|
||||
@@ -94,20 +156,20 @@ func encryptedSecureSecret(secretData: Data, password: String) -> Data? {
|
||||
return nil
|
||||
}
|
||||
|
||||
if decryptedSecureSecret(encryptedSecretData: encryptedSecret, password: password) != secretData {
|
||||
if decryptedSecureSecret(encryptedSecretData: encryptedSecret, password: password, salt: secretSalt, hash: secretHash) != secretData {
|
||||
return nil
|
||||
}
|
||||
|
||||
return encryptedSecret
|
||||
return (encryptedSecret, secretSalt, secretHash)
|
||||
}
|
||||
|
||||
private func generateSecureSecret(network: Network, password: String) -> Signal<Data, GenerateSecureSecretError> {
|
||||
func generateSecureSecretData() -> Data? {
|
||||
var secretData = Data(count: 32)
|
||||
guard secretData.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<Int8>) -> Bool in
|
||||
let copyResult = SecRandomCopyBytes(nil, 32, bytes)
|
||||
return copyResult == errSecSuccess
|
||||
}) else {
|
||||
return .fail(.generic)
|
||||
return nil
|
||||
}
|
||||
|
||||
secretData.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<UInt8>) in
|
||||
@@ -136,12 +198,15 @@ private func generateSecureSecret(network: Network, password: String) -> Signal<
|
||||
}
|
||||
}
|
||||
})
|
||||
return secretData
|
||||
}
|
||||
|
||||
guard let encryptedSecret = encryptedSecureSecret(secretData: secretData, password: password) else {
|
||||
private func generateSecureSecret(network: Network, password: String) -> Signal<Data, GenerateSecureSecretError> {
|
||||
guard let secretData = generateSecureSecretData() else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
return updateTwoStepVerificationSecureSecret(network: network, password: password, updatedSecret: encryptedSecret)
|
||||
return updateTwoStepVerificationSecureSecret(network: network, password: password, secret: secretData)
|
||||
|> mapError { _ -> GenerateSecureSecretError in
|
||||
return .generic
|
||||
}
|
||||
@@ -152,6 +217,7 @@ private func generateSecureSecret(network: Network, password: String) -> Signal<
|
||||
|
||||
public struct SecureIdAccessContext {
|
||||
let secret: Data
|
||||
let hash: Int64
|
||||
}
|
||||
|
||||
public enum SecureIdAccessError {
|
||||
@@ -167,8 +233,8 @@ public func accessSecureId(network: Network, password: String) -> Signal<SecureI
|
||||
}
|
||||
|> mapToSignal { settings -> Signal<SecureIdAccessContext, SecureIdAccessError> in
|
||||
if let secureSecret = settings.secureSecret {
|
||||
if let decryptedSecret = decryptedSecureSecret(encryptedSecretData: secureSecret, password: "q") { //password
|
||||
return .single(SecureIdAccessContext(secret: decryptedSecret))
|
||||
if let decryptedSecret = decryptedSecureSecret(encryptedSecretData: secureSecret.data, password: password, salt: secureSecret.salt, hash: secureSecret.hash) {
|
||||
return .single(SecureIdAccessContext(secret: decryptedSecret, hash: secureSecret.hash))
|
||||
} else {
|
||||
return .fail(.secretPasswordMismatch)
|
||||
}
|
||||
@@ -178,7 +244,12 @@ public func accessSecureId(network: Network, password: String) -> Signal<SecureI
|
||||
return SecureIdAccessError.generic
|
||||
}
|
||||
|> map { decryptedSecret in
|
||||
return SecureIdAccessContext(secret: decryptedSecret)
|
||||
let secretHashData = sha256Digest(decryptedSecret)
|
||||
var secretHash: Int64 = 0
|
||||
secretHashData.withUnsafeBytes { (bytes: UnsafePointer<Int8>) -> Void in
|
||||
memcpy(&secretHash, bytes.advanced(by: secretHashData.count - 8), 8)
|
||||
}
|
||||
return SecureIdAccessContext(secret: decryptedSecret, hash: secretHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,20 +342,47 @@ public struct TwoStepAuthData {
|
||||
public let currentHint: String?
|
||||
public let unconfirmedEmailPattern: String?
|
||||
public let secretRandom: Data
|
||||
public let nextSecureSalt: Data
|
||||
}
|
||||
|
||||
public func twoStepAuthData(_ network: Network) -> Signal<TwoStepAuthData, MTRpcError> {
|
||||
return network.request(Api.functions.account.getPassword())
|
||||
|> map { config -> TwoStepAuthData in
|
||||
switch config {
|
||||
case let .noPassword(newSalt, secretRandom, emailUnconfirmedPattern):
|
||||
return TwoStepAuthData(nextSalt: newSalt.makeData(), currentSalt: nil, hasRecovery: false, currentHint: nil, unconfirmedEmailPattern: emailUnconfirmedPattern, secretRandom: secretRandom.makeData())
|
||||
case let .password(currentSalt, newSalt, secretRandom, hint, hasRecovery, emailUnconfirmedPattern):
|
||||
return TwoStepAuthData(nextSalt: newSalt.makeData(), currentSalt: currentSalt.makeData(), hasRecovery: hasRecovery == .boolTrue, currentHint: hint, unconfirmedEmailPattern: emailUnconfirmedPattern, secretRandom: secretRandom.makeData())
|
||||
case let .noPassword(newSalt, newSecureSalt, secretRandom, emailUnconfirmedPattern):
|
||||
return TwoStepAuthData(nextSalt: newSalt.makeData(), currentSalt: nil, hasRecovery: false, currentHint: nil, unconfirmedEmailPattern: emailUnconfirmedPattern, secretRandom: secretRandom.makeData(), nextSecureSalt: newSecureSalt.makeData())
|
||||
case let .password(currentSalt, newSalt, newSecureSalt, secretRandom, hint, hasRecovery, emailUnconfirmedPattern):
|
||||
return TwoStepAuthData(nextSalt: newSalt.makeData(), currentSalt: currentSalt.makeData(), hasRecovery: hasRecovery == .boolTrue, currentHint: hint, unconfirmedEmailPattern: emailUnconfirmedPattern, secretRandom: secretRandom.makeData(), nextSecureSalt: newSecureSalt.makeData())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func hexString(_ data: Data) -> String {
|
||||
let hexString = NSMutableString()
|
||||
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||
for i in 0 ..< data.count {
|
||||
hexString.appendFormat("%02x", UInt(bytes.advanced(by: i).pointee))
|
||||
}
|
||||
}
|
||||
|
||||
return hexString as String
|
||||
}
|
||||
|
||||
func dataWithHexString(_ string: String) -> Data {
|
||||
var hex = string
|
||||
var data = Data()
|
||||
while hex.count > 0 {
|
||||
let subIndex = hex.index(hex.startIndex, offsetBy: 2)
|
||||
let c = String(hex[..<subIndex])
|
||||
hex = String(hex[subIndex...])
|
||||
var ch: UInt32 = 0
|
||||
Scanner(string: c).scanHexInt32(&ch)
|
||||
var char = UInt8(ch)
|
||||
data.append(&char, count: 1)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func sha1Digest(_ data : Data) -> Data {
|
||||
var res = Data()
|
||||
res.count = Int(CC_SHA1_DIGEST_LENGTH)
|
||||
@@ -467,6 +494,81 @@ public struct AccountRunningImportantTasks: OptionSet {
|
||||
public static let pendingMessages = AccountRunningImportantTasks(rawValue: 1 << 1)
|
||||
}
|
||||
|
||||
private struct MasterNotificationKey {
|
||||
let id: Data
|
||||
let data: Data
|
||||
}
|
||||
|
||||
private func masterNotificationsKey(account: Account, ignoreDisabled: Bool) -> Signal<MasterNotificationKey, NoError> {
|
||||
if let key = account.masterNotificationKey.with({ $0 }) {
|
||||
//return .single(key)
|
||||
}
|
||||
|
||||
return account.postbox.modify(ignoreDisabled: ignoreDisabled, { modifier -> MasterNotificationKey in
|
||||
if let value = modifier.keychainEntryForKey("master-notification-secret"), !value.isEmpty {
|
||||
let authKeyHash = sha1Digest(value)
|
||||
let authKeyId = authKeyHash.subdata(in: authKeyHash.count - 8 ..< authKeyHash.count)
|
||||
let keyData = MasterNotificationKey(id: authKeyId, data: value)
|
||||
let _ = account.masterNotificationKey.swap(keyData)
|
||||
return keyData
|
||||
} else {
|
||||
var secretData = Data(count: 256)
|
||||
if !secretData.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<Int8>) -> Bool in
|
||||
let copyResult = SecRandomCopyBytes(nil, secretData.count, bytes)
|
||||
return copyResult == errSecSuccess
|
||||
}) {
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
modifier.setKeychainEntry(secretData, forKey: "master-notification-secret")
|
||||
let authKeyHash = sha1Digest(secretData)
|
||||
let authKeyId = authKeyHash.subdata(in: authKeyHash.count - 8 ..< authKeyHash.count)
|
||||
let keyData = MasterNotificationKey(id: authKeyId, data: secretData)
|
||||
let _ = account.masterNotificationKey.swap(keyData)
|
||||
return keyData
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func decryptedNotificationPayload(account: Account, data: Data) -> Signal<Data?, NoError> {
|
||||
return masterNotificationsKey(account: account, ignoreDisabled: true)
|
||||
|> map { secret -> Data? in
|
||||
if data.subdata(in: 0 ..< 8) != secret.id {
|
||||
return nil
|
||||
}
|
||||
|
||||
let x = 8
|
||||
let msgKey = data.subdata(in: 8 ..< (8 + 16))
|
||||
let rawData = data.subdata(in: (8 + 16) ..< data.count)
|
||||
let sha256_a = sha256Digest(msgKey + secret.data.subdata(in: x ..< (x + 36)))
|
||||
let sha256_b = sha256Digest(secret.data.subdata(in: (40 + x) ..< (40 + x + 36)) + msgKey)
|
||||
let aesKey = sha256_a.subdata(in: 0 ..< 8) + sha256_b.subdata(in: 8 ..< (8 + 16)) + sha256_a.subdata(in: 24 ..< (24 + 8))
|
||||
let aesIv = sha256_b.subdata(in: 0 ..< 8) + sha256_a.subdata(in: 8 ..< (8 + 16)) + sha256_b.subdata(in: 24 ..< (24 + 8))
|
||||
|
||||
guard let data = MTAesDecrypt(rawData, aesKey, aesIv), data.count > 4 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var dataLength: Int32 = 0
|
||||
data.withUnsafeBytes { (bytes: UnsafePointer<Int8>) -> Void in
|
||||
memcpy(&dataLength, bytes, 4)
|
||||
}
|
||||
|
||||
if dataLength < 0 || dataLength > data.count - 4 {
|
||||
return nil
|
||||
}
|
||||
|
||||
let checkMsgKeyLarge = sha256Digest(secret.data.subdata(in: (88 + x) ..< (88 + x + 32)) + data)
|
||||
let checkMsgKey = checkMsgKeyLarge.subdata(in: 8 ..< (8 + 16))
|
||||
|
||||
if checkMsgKey != msgKey {
|
||||
return nil
|
||||
}
|
||||
|
||||
return data.subdata(in: 4 ..< (4 + Int(dataLength)))
|
||||
}
|
||||
}
|
||||
|
||||
public class Account {
|
||||
public let id: AccountRecordId
|
||||
public let basePath: String
|
||||
@@ -524,6 +626,8 @@ public class Account {
|
||||
return self._importantTasksRunning.get()
|
||||
}
|
||||
|
||||
fileprivate let masterNotificationKey = Atomic<MasterNotificationKey?>(value: nil)
|
||||
|
||||
var transformOutgoingMessageMedia: TransformOutgoingMessageMedia?
|
||||
|
||||
public init(id: AccountRecordId, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, peerId: PeerId, auxiliaryMethods: AccountAuxiliaryMethods) {
|
||||
@@ -588,12 +692,16 @@ public class Account {
|
||||
#if DEBUG
|
||||
appSandbox = .boolTrue
|
||||
#endif
|
||||
return network.request(Api.functions.account.registerDevice(tokenType: 1, token: tokenString, appSandbox: appSandbox, secret: Buffer(), otherUids: []))
|
||||
|
||||
return masterNotificationsKey(account: self, ignoreDisabled: false)
|
||||
|> mapToSignal { secret -> Signal<Void, NoError> in
|
||||
return network.request(Api.functions.account.registerDevice(tokenType: 1, token: tokenString, appSandbox: appSandbox, secret: Buffer(data: secret.data), otherUids: []))
|
||||
|> retryRequest
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
self.notificationTokenDisposable.set(appliedNotificationToken.start())
|
||||
|
||||
let appliedVoipToken = self.voipToken.get()
|
||||
@@ -612,12 +720,15 @@ public class Account {
|
||||
appSandbox = .boolTrue
|
||||
#endif
|
||||
|
||||
return network.request(Api.functions.account.registerDevice(tokenType: 9, token: tokenString, appSandbox: appSandbox, secret: Buffer(), otherUids: []))
|
||||
return masterNotificationsKey(account: self, ignoreDisabled: false)
|
||||
|> mapToSignal { secret -> Signal<Void, NoError> in
|
||||
return network.request(Api.functions.account.registerDevice(tokenType: 9, token: tokenString, appSandbox: appSandbox, secret: Buffer(data: secret.data), otherUids: []))
|
||||
|> retryRequest
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
self.voipTokenDisposable.set(appliedVoipToken.start())
|
||||
|
||||
let serviceTasksMasterBecomeMaster = shouldBeServiceTaskMaster.get()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -156,7 +156,7 @@ public func authorizeWithCode(account: UnauthorizedAccount, code: String) -> Sig
|
||||
switch result {
|
||||
case .noPassword:
|
||||
return .fail(.generic)
|
||||
case let .password(_, _, _, hint, _, _):
|
||||
case let .password(_, _, _, _, hint, _, _):
|
||||
return .single(.password(hint: hint))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,139 +13,87 @@ public enum RequestSecureIdFormError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public enum RequestedSecureIdField {
|
||||
case identity
|
||||
case address
|
||||
case phone
|
||||
case email
|
||||
}
|
||||
|
||||
private func parseRequestedFieldType(_ type: Api.AuthFieldType) -> RequestedSecureIdField {
|
||||
private func parseSecureValueType(_ type: Api.SecureValueType) -> SecureIdRequestedFormField {
|
||||
switch type {
|
||||
case .authFieldTypeIdentity:
|
||||
case .secureValueTypeIdentity:
|
||||
return .identity
|
||||
case .authFieldTypeAddress:
|
||||
case .secureValueTypeAddress:
|
||||
return .address
|
||||
case .authFieldTypePhone:
|
||||
case .secureValueTypePhone:
|
||||
return .phone
|
||||
case .authFieldTypeEmail:
|
||||
case .secureValueTypeEmail:
|
||||
return .email
|
||||
}
|
||||
}
|
||||
|
||||
private func parseFileReference(_ file: Api.SecureFile) -> SecureIdFileReference {
|
||||
switch file {
|
||||
case .secureFileEmpty:
|
||||
return .none
|
||||
case let .secureFile(id, accessHash, size, dcId, fileHash):
|
||||
return .file(id: id, accessHash: accessHash, size: size, datacenterId: dcId, fileHash: fileHash)
|
||||
//secureData data:bytes data_hash:bytes = SecureData;
|
||||
|
||||
private func parseSecureData(_ value: Api.SecureData) -> (data: Data, hash: Data) {
|
||||
switch value {
|
||||
case let .secureData(data, dataHash):
|
||||
return (data.makeData(), dataHash.makeData())
|
||||
}
|
||||
}
|
||||
|
||||
/*private func parseValue(_ value: Api.SecureValue) -> SecureIdFieldValue {
|
||||
private func parseSecureValue(context: SecureIdAccessContext, value: Api.SecureValue) -> SecureIdValue? {
|
||||
switch value {
|
||||
case let .secureValueEmpty(name):
|
||||
return SecureIdFieldValue(name: name, data: .none)
|
||||
case let .secureValueData(name, data, hash, secret):
|
||||
return SecureIdFieldValue(name: name, data: .data(data: data.makeData(), hash: hash, secret: secret.makeData()))
|
||||
case let .secureValueFile(name, file, hash, secret):
|
||||
return SecureIdFieldValue(name: name, data: .files(files: file.map(parseFileReference), hash: hash, secret: secret.makeData()))
|
||||
case let .secureValueText(name, text, hash):
|
||||
return SecureIdFieldValue(name: name, data: .text(text: text, hash: hash))
|
||||
}
|
||||
}*/
|
||||
|
||||
private func parseIdentityField(context: SecureIdAccessContext, value: Api.SecureValue, document: Api.SecureValue?) -> SecureIdIdentityField? {
|
||||
switch value {
|
||||
case let .secureValueData(name, data, hash, secret):
|
||||
return nil
|
||||
default:
|
||||
case let .secureValueIdentity(_, data, files, secret, hash, verified):
|
||||
let (encryptedData, encryptedHash) = parseSecureData(data)
|
||||
guard let decryptedData = decryptedSecureData(context: context, data: encryptedData, dataHash: encryptedHash, encryptedSecret: secret.makeData()) else {
|
||||
return nil
|
||||
}
|
||||
var fileReferences: [Int64: SecureIdFileReference] = [:]
|
||||
for file in files.map(SecureIdFileReference.init).flatMap({ $0 }) {
|
||||
fileReferences[file.id] = file
|
||||
}
|
||||
|
||||
private func parsePhoneField(context: SecureIdAccessContext, value: Api.SecureValue) -> SecureIdPhoneField? {
|
||||
switch value {
|
||||
case let .secureValueText(name, text, _):
|
||||
return SecureIdPhoneField(rawValue: text)
|
||||
default:
|
||||
guard let value = SecureIdIdentityValue(data: decryptedData, fileReferences: fileReferences) else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func parseEmailField(context: SecureIdAccessContext, value: Api.SecureValue) -> SecureIdEmailField? {
|
||||
switch value {
|
||||
case let .secureValueText(name, text, _):
|
||||
return SecureIdEmailField(rawValue: text)
|
||||
default:
|
||||
return .identity(value)
|
||||
case let .secureValueAddress(_, data, files, secret, hash, verified):
|
||||
return nil
|
||||
case let .secureValuePhone(_, phone, hash, verified):
|
||||
guard let phoneData = phone.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
if sha256Digest(phoneData) != hash.makeData() {
|
||||
return nil
|
||||
}
|
||||
return .phone(SecureIdPhoneValue(phone: phone))
|
||||
case let .secureValueEmail(_, email, hash, verified):
|
||||
guard let emailData = email.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
if sha256Digest(emailData) != hash.makeData() {
|
||||
return nil
|
||||
}
|
||||
return .email(SecureIdEmailValue(email: email))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
private func parseSecureValues(context: SecureIdAccessContext, values: [Api.SecureValue]) -> [SecureIdValue] {
|
||||
return values.map({ parseSecureValue(context: context, value: $0) }).flatMap({ $0 })
|
||||
}
|
||||
|
||||
public struct EncryptedSecureIdForm {
|
||||
public let peerId: PeerId
|
||||
public let requestedFields: [RequestedSecureIdField]
|
||||
public let requestedFields: [SecureIdRequestedFormField]
|
||||
|
||||
let encryptedFields: [Api.AuthField]
|
||||
let encryptedValues: [Api.SecureValue]
|
||||
}
|
||||
|
||||
public func requestSecureIdForm(postbox: Postbox, network: Network, peerId: PeerId, scope: [String], origin: String?, packageName: String?, bundleId: String?, publicKey: String?) -> Signal<EncryptedSecureIdForm, RequestSecureIdFormError> {
|
||||
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)
|
||||
}
|
||||
var flags: Int32 = 0
|
||||
if let _ = origin {
|
||||
flags |= 1 << 0
|
||||
}
|
||||
if let _ = packageName {
|
||||
flags |= 1 << 1
|
||||
}
|
||||
if let _ = bundleId {
|
||||
flags |= 1 << 2
|
||||
}
|
||||
if let _ = publicKey {
|
||||
flags |= 1 << 3
|
||||
}
|
||||
return network.request(Api.functions.account.getAuthorizationForm(flags: flags, botId: peerId.id, scope: scope, origin: origin, packageName: packageName, bundleId: bundleId, publicKey: publicKey))
|
||||
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(_, botId, fields, _, users):
|
||||
case let .authorizationForm(_, requiredTypes, values, users):
|
||||
var peers: [Peer] = []
|
||||
for user in users {
|
||||
let parsed = TelegramUser(user: user)
|
||||
@@ -155,17 +103,12 @@ public func requestSecureIdForm(postbox: Postbox, network: Network, peerId: Peer
|
||||
return updated
|
||||
})
|
||||
|
||||
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)
|
||||
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, fields: parseFields(context: context, fields: form.encryptedFields))
|
||||
return SecureIdForm(peerId: form.peerId, requestedFields: form.requestedFields, values: parseSecureValues(context: context, values: form.encryptedValues))
|
||||
}
|
||||
|
||||
150
TelegramCore/SaveSecureIdValue.swift
Normal file
150
TelegramCore/SaveSecureIdValue.swift
Normal file
@@ -0,0 +1,150 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import MtProtoKitMac
|
||||
import SwiftSignalKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import MtProtoKitDynamic
|
||||
import SwiftSignalKit
|
||||
#endif
|
||||
|
||||
public enum SaveSecureIdValueError {
|
||||
case generic
|
||||
}
|
||||
|
||||
private func paddedData(_ data: Data) -> Data {
|
||||
var paddingCount = Int(47 + arc4random_uniform(255 - 47))
|
||||
paddingCount -= ((data.count + paddingCount) % 16)
|
||||
var result = Data(count: paddingCount + data.count)
|
||||
result.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
||||
bytes.advanced(by: 0).pointee = UInt8(paddingCount)
|
||||
arc4random_buf(bytes.advanced(by: 1), paddingCount - 1)
|
||||
data.withUnsafeBytes { (source: UnsafePointer<UInt8>) -> Void in
|
||||
memcpy(bytes.advanced(by: paddingCount), source, data.count)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func unpaddedData(_ data: Data) -> Data? {
|
||||
var paddingCount: UInt8 = 0
|
||||
data.copyBytes(to: &paddingCount, count: 1)
|
||||
|
||||
if paddingCount < 0 || paddingCount > data.count {
|
||||
return nil
|
||||
}
|
||||
|
||||
return data.subdata(in: Int(paddingCount) ..< data.count)
|
||||
}
|
||||
|
||||
struct EncryptedSecureData {
|
||||
let data: Data
|
||||
let hash: Data
|
||||
let encryptedSecret: Data
|
||||
let secretHash: Data
|
||||
}
|
||||
|
||||
func encryptedSecureData(context: SecureIdAccessContext, data: Data) -> EncryptedSecureData? {
|
||||
let fileData = paddedData(data)
|
||||
let fileHash = sha256Digest(fileData)
|
||||
|
||||
guard let fileSecret = generateSecureSecretData() 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 encryptedFileData = encryptSecureData(key: fileKey, iv: fileIv, data: fileData, decrypt: false) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let secretHash = sha512Digest(context.secret)
|
||||
let secretKey = secretHash.subdata(in: 0 ..< 32)
|
||||
let secretIv = secretHash.subdata(in: 32 ..< (32 + 16))
|
||||
|
||||
guard let encryptedFileSecret = encryptSecureData(key: secretKey, iv: secretIv, data: fileSecret, decrypt: false) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return EncryptedSecureData(data: encryptedFileData, hash: fileHash, encryptedSecret: encryptedFileSecret, secretHash: secretHash)
|
||||
}
|
||||
|
||||
func decryptedSecureData(context: SecureIdAccessContext, data: Data, dataHash: Data, encryptedSecret: Data) -> Data? {
|
||||
let secretHash = sha512Digest(context.secret)
|
||||
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
|
||||
}
|
||||
|
||||
if !verifySecureSecret(fileSecret) {
|
||||
return nil
|
||||
}
|
||||
|
||||
let fileSecretHash = sha512Digest(fileSecret + dataHash)
|
||||
let fileKey = fileSecretHash.subdata(in: 0 ..< 32)
|
||||
let fileIv = fileSecretHash.subdata(in: 32 ..< (32 + 16))
|
||||
|
||||
guard let decryptedFileData = encryptSecureData(key: fileKey, iv: fileIv, data: data, decrypt: true) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let checkDataHash = sha256Digest(decryptedFileData)
|
||||
if checkDataHash != dataHash {
|
||||
return nil
|
||||
}
|
||||
|
||||
return unpaddedData(decryptedFileData)
|
||||
}
|
||||
|
||||
private func makeInputSecureValue(context: SecureIdAccessContext, value: SecureIdValue) -> Api.InputSecureValue? {
|
||||
switch value {
|
||||
case .identity:
|
||||
guard let (decryptedData, fileReferences) = value.serialize() else {
|
||||
return nil
|
||||
}
|
||||
guard let encryptedData = encryptedSecureData(context: context, data: decryptedData) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let checkData = decryptedSecureData(context: context, data: encryptedData.data, dataHash: encryptedData.hash, encryptedSecret: encryptedData.encryptedSecret) {
|
||||
if checkData != decryptedData {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let files = fileReferences.map { file in
|
||||
return Api.InputSecureFile.inputSecureFile(id: file.id, accessHash: file.accessHash)
|
||||
}
|
||||
|
||||
return Api.InputSecureValue.inputSecureValueIdentity(data: Api.SecureData.secureData(data: Buffer(data: encryptedData.data), dataHash: Buffer(data: encryptedData.hash)), files: files, secret: Buffer(data: encryptedData.encryptedSecret), hash: Buffer(data: encryptedData.secretHash))
|
||||
case let .phone(value):
|
||||
guard let phoneData = value.phone.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
return Api.InputSecureValue.inputSecureValuePhone(phone: value.phone, hash: Buffer(data: sha256Digest(phoneData)))
|
||||
case let .email(value):
|
||||
guard let emailData = value.email.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
return Api.InputSecureValue.inputSecureValueEmail(email: value.email, hash: Buffer(data: sha256Digest(emailData)))
|
||||
}
|
||||
}
|
||||
|
||||
public func saveSecureIdValue(network: Network, context: SecureIdAccessContext, value: SecureIdValue) -> Signal<Void, SaveSecureIdValueError> {
|
||||
guard let inputValue = makeInputSecureValue(context: context, value: value) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return network.request(Api.functions.account.saveSecureValue(value: inputValue, secureSecretHash: context.hash))
|
||||
|> mapError { _ -> SaveSecureIdValueError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, SaveSecureIdValueError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
public struct SecureIdDate: Equatable {
|
||||
private var timestamp: Int32
|
||||
public let timestamp: Int32
|
||||
|
||||
public init(timestamp: Int32) {
|
||||
self.timestamp = timestamp
|
||||
@@ -20,24 +20,40 @@ public enum SecureIdGender {
|
||||
case female
|
||||
}
|
||||
|
||||
public enum SecureIdFileReference: Equatable {
|
||||
case none
|
||||
case file(id: Int64, accessHash: Int64, size: Int32, datacenterId: Int32, fileHash: String)
|
||||
public struct SecureIdFileReference: Equatable {
|
||||
let id: Int64
|
||||
let accessHash: Int64
|
||||
let size: Int32
|
||||
let datacenterId: Int32
|
||||
let fileHash: Data
|
||||
|
||||
public static func ==(lhs: SecureIdFileReference, rhs: SecureIdFileReference) -> Bool {
|
||||
switch lhs {
|
||||
case .none:
|
||||
if case .none = rhs {
|
||||
return true
|
||||
} else {
|
||||
if lhs.id != rhs.id {
|
||||
return false
|
||||
}
|
||||
case let .file(id, accessHash, size, datacenterId, fileHash):
|
||||
if case .file(id, accessHash, size, datacenterId, fileHash) = rhs {
|
||||
return true
|
||||
} else {
|
||||
if lhs.accessHash != rhs.accessHash {
|
||||
return false
|
||||
}
|
||||
if lhs.size != rhs.size {
|
||||
return false
|
||||
}
|
||||
if lhs.datacenterId != rhs.datacenterId {
|
||||
return false
|
||||
}
|
||||
if lhs.fileHash != rhs.fileHash {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension SecureIdFileReference {
|
||||
init?(apiFile: Api.SecureFile) {
|
||||
switch apiFile {
|
||||
case let .secureFile(id, accessHash, size, dcId, fileHash):
|
||||
self.init(id: id, accessHash: accessHash, size: size, datacenterId: dcId, fileHash: fileHash.makeData())
|
||||
case .secureFileEmpty:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
16
TelegramCore/SecureIdEmailValue.swift
Normal file
16
TelegramCore/SecureIdEmailValue.swift
Normal file
@@ -0,0 +1,16 @@
|
||||
import Foundation
|
||||
|
||||
public struct SecureIdEmailValue: Equatable {
|
||||
public let email: String
|
||||
|
||||
public init(email: String) {
|
||||
self.email = email
|
||||
}
|
||||
|
||||
public static func ==(lhs: SecureIdEmailValue, rhs: SecureIdEmailValue) -> Bool {
|
||||
if lhs.email != rhs.email {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -5,77 +5,72 @@ import Foundation
|
||||
import Postbox
|
||||
#endif
|
||||
|
||||
public enum SecureIdFieldValue<T>: Equatable where T: Equatable {
|
||||
case empty
|
||||
case value(T)
|
||||
public enum SecureIdValue: Equatable {
|
||||
case identity(SecureIdIdentityValue)
|
||||
case phone(SecureIdPhoneValue)
|
||||
case email(SecureIdEmailValue)
|
||||
|
||||
public static func ==(lhs: SecureIdFieldValue<T>, rhs: SecureIdFieldValue<T>) -> Bool {
|
||||
public static func ==(lhs: SecureIdValue, rhs: SecureIdValue) -> Bool {
|
||||
switch lhs {
|
||||
case .empty:
|
||||
if case .empty = rhs {
|
||||
case let .identity(value):
|
||||
if case .identity(value) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .value(value):
|
||||
if case .value(value) = rhs {
|
||||
case let .phone(value):
|
||||
if case .phone(value) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .email(value):
|
||||
if case .email(value) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct SecureIdFields: Equatable {
|
||||
public var identity: SecureIdFieldValue<SecureIdIdentityField>?
|
||||
public var phone: SecureIdFieldValue<SecureIdPhoneField>?
|
||||
public var email: SecureIdFieldValue<SecureIdEmailField>?
|
||||
|
||||
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 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
|
||||
}
|
||||
func serialize() -> (Data, [SecureIdFileReference])? {
|
||||
switch self {
|
||||
case let .identity(value):
|
||||
return value.serialize()
|
||||
case .phone, .email:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct SecureIdForm {
|
||||
public enum SecureIdRequestedFormField {
|
||||
case identity
|
||||
case address
|
||||
case phone
|
||||
case email
|
||||
}
|
||||
|
||||
public struct SecureIdForm: Equatable {
|
||||
public let peerId: PeerId
|
||||
public let fields: SecureIdFields
|
||||
public let requestedFields: [SecureIdRequestedFormField]
|
||||
public let values: [SecureIdValue]
|
||||
|
||||
public init(peerId: PeerId, requestedFields: [SecureIdRequestedFormField], values: [SecureIdValue]) {
|
||||
self.peerId = peerId
|
||||
self.requestedFields = requestedFields
|
||||
self.values = values
|
||||
}
|
||||
|
||||
public static func ==(lhs: SecureIdForm, rhs: SecureIdForm) -> Bool {
|
||||
if lhs.peerId != rhs.peerId {
|
||||
return false
|
||||
}
|
||||
if lhs.requestedFields != rhs.requestedFields {
|
||||
return false
|
||||
}
|
||||
if lhs.values != rhs.values {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
227
TelegramCore/SecureIdIdentityValue.swift
Normal file
227
TelegramCore/SecureIdIdentityValue.swift
Normal file
@@ -0,0 +1,227 @@
|
||||
import Foundation
|
||||
|
||||
public enum SecureIdIdentityValue: Equatable {
|
||||
case passport(SecureIdIdentityPassportValue)
|
||||
|
||||
public static func ==(lhs: SecureIdIdentityValue, rhs: SecureIdIdentityValue) -> Bool {
|
||||
switch lhs {
|
||||
case let .passport(value):
|
||||
if case .passport(value) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct SecureIdIdentityPassportValue: Equatable {
|
||||
public var identifier: String
|
||||
public var firstName: String
|
||||
public var lastName: String
|
||||
public var birthdate: SecureIdDate
|
||||
public var countryCode: String
|
||||
public var gender: SecureIdGender
|
||||
public var issueDate: SecureIdDate
|
||||
public var expiryDate: SecureIdDate?
|
||||
public var verificationDocuments: [SecureIdFileReference]
|
||||
|
||||
public init(identifier: String, firstName: String, lastName: String, birthdate: SecureIdDate, countryCode: String, gender: SecureIdGender, issueDate: SecureIdDate, expiryDate: SecureIdDate?, verificationDocuments: [SecureIdFileReference]) {
|
||||
self.identifier = identifier
|
||||
self.firstName = firstName
|
||||
self.lastName = lastName
|
||||
self.birthdate = birthdate
|
||||
self.countryCode = countryCode
|
||||
self.gender = gender
|
||||
self.issueDate = issueDate
|
||||
self.expiryDate = expiryDate
|
||||
self.verificationDocuments = verificationDocuments
|
||||
}
|
||||
|
||||
public static func ==(lhs: SecureIdIdentityPassportValue, rhs: SecureIdIdentityPassportValue) -> Bool {
|
||||
if lhs.identifier != rhs.identifier {
|
||||
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
|
||||
}
|
||||
if lhs.issueDate != rhs.issueDate {
|
||||
return false
|
||||
}
|
||||
if lhs.expiryDate != rhs.expiryDate {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private func parseGender(_ string: String) -> SecureIdGender? {
|
||||
switch string {
|
||||
case "male":
|
||||
return .male
|
||||
case "female":
|
||||
return .female
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func serializeGender(_ gender: SecureIdGender) -> String {
|
||||
switch gender {
|
||||
case .male:
|
||||
return "male"
|
||||
case .female:
|
||||
return "female"
|
||||
}
|
||||
}
|
||||
|
||||
private let dateFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "dd.MM.yyyy"
|
||||
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
return formatter
|
||||
}()
|
||||
|
||||
private func parseDate(_ string: String) -> SecureIdDate? {
|
||||
guard let date = dateFormatter.date(from: string) else {
|
||||
return nil
|
||||
}
|
||||
return SecureIdDate(timestamp: Int32(date.timeIntervalSince1970))
|
||||
}
|
||||
|
||||
private func serializeDate(_ date: SecureIdDate) -> String {
|
||||
return dateFormatter.string(from: Date(timeIntervalSince1970: Double(date.timestamp)))
|
||||
}
|
||||
|
||||
private func parseFileReferenceId(_ string: String) -> Int64? {
|
||||
let data = dataWithHexString(string)
|
||||
if data.count != 8 {
|
||||
return nil
|
||||
}
|
||||
var value: Int64 = 0
|
||||
data.withUnsafeBytes { (bytes: UnsafePointer<Int8>) -> Void in
|
||||
memcpy(&value, bytes, 8)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
private func serializeFileReferenceId(_ id: Int64) -> String {
|
||||
var data = Data(count: 8)
|
||||
data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<Int8>) -> Void in
|
||||
var id = id
|
||||
memcpy(bytes, &id, 8)
|
||||
}
|
||||
return hexString(data)
|
||||
}
|
||||
|
||||
extension SecureIdIdentityValue {
|
||||
init?(data: Data, fileReferences: [Int64: SecureIdFileReference]) {
|
||||
guard let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else {
|
||||
return nil
|
||||
}
|
||||
guard let documentType = dict["document_type"] as? String else {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch documentType {
|
||||
case "passport":
|
||||
if let passport = SecureIdIdentityPassportValue(dict: dict, fileReferences: fileReferences) {
|
||||
self = .passport(passport)
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func serialize() -> (Data, [SecureIdFileReference])? {
|
||||
var dict: [String: Any] = [:]
|
||||
let fileReferences: [SecureIdFileReference]
|
||||
switch self {
|
||||
case let .passport(value):
|
||||
dict["document_type"] = "passport"
|
||||
let (valueDict, references) = value.serialize()
|
||||
dict.merge(valueDict, uniquingKeysWith: { lhs, _ in return lhs })
|
||||
fileReferences = references
|
||||
}
|
||||
guard let data = try? JSONSerialization.data(withJSONObject: dict, options: []) else {
|
||||
return nil
|
||||
}
|
||||
return (data, fileReferences)
|
||||
}
|
||||
}
|
||||
|
||||
private extension SecureIdIdentityPassportValue {
|
||||
init?(dict: [String: Any], fileReferences: [Int64: SecureIdFileReference]) {
|
||||
guard let identifier = dict["document_no"] as? String else {
|
||||
return nil
|
||||
}
|
||||
guard let firstName = dict["first_name"] as? String else {
|
||||
return nil
|
||||
}
|
||||
guard let lastName = dict["last_name"] as? String else {
|
||||
return nil
|
||||
}
|
||||
guard let birthdate = (dict["date_of_birth"] as? String).flatMap(parseDate) else {
|
||||
return nil
|
||||
}
|
||||
guard let gender = (dict["gender"] as? String).flatMap(parseGender) else {
|
||||
return nil
|
||||
}
|
||||
guard let countryCode = dict["country_code"] as? String else {
|
||||
return nil
|
||||
}
|
||||
guard let issueDate = (dict["issue_date"] as? String).flatMap(parseDate) else {
|
||||
return nil
|
||||
}
|
||||
let expiryDate = (dict["expiry_date"] as? String).flatMap(parseDate)
|
||||
|
||||
var verificationDocuments: [SecureIdFileReference] = []
|
||||
if let files = dict["files"] as? [String] {
|
||||
for fileId in files {
|
||||
guard let fileId = parseFileReferenceId(fileId) else {
|
||||
continue
|
||||
}
|
||||
guard let file = fileReferences[fileId] else {
|
||||
continue
|
||||
}
|
||||
verificationDocuments.append(file)
|
||||
}
|
||||
}
|
||||
|
||||
self.init(identifier: identifier, firstName: firstName, lastName: lastName, birthdate: birthdate, countryCode: countryCode, gender: gender, issueDate: issueDate, expiryDate: expiryDate, verificationDocuments: verificationDocuments)
|
||||
}
|
||||
|
||||
func serialize() -> ([String: Any], [SecureIdFileReference]) {
|
||||
var dict: [String: Any] = [:]
|
||||
dict["document_no"] = self.identifier
|
||||
dict["first_name"] = self.firstName
|
||||
dict["last_name"] = self.lastName
|
||||
dict["date_of_birth"] = serializeDate(self.birthdate)
|
||||
dict["gender"] = serializeGender(self.gender)
|
||||
dict["country_code"] = self.countryCode
|
||||
dict["issue_date"] = serializeDate(self.issueDate)
|
||||
if let expiryDate = self.expiryDate {
|
||||
dict["expiry_date"] = serializeDate(expiryDate)
|
||||
}
|
||||
if !self.verificationDocuments.isEmpty {
|
||||
dict["files"] = self.verificationDocuments.map { serializeFileReferenceId($0.id) }
|
||||
}
|
||||
|
||||
return (dict, self.verificationDocuments)
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
16
TelegramCore/SecureIdPhoneValue.swift
Normal file
16
TelegramCore/SecureIdPhoneValue.swift
Normal file
@@ -0,0 +1,16 @@
|
||||
import Foundation
|
||||
|
||||
public struct SecureIdPhoneValue: Equatable {
|
||||
public let phone: String
|
||||
|
||||
public init(phone: String) {
|
||||
self.phone = phone
|
||||
}
|
||||
|
||||
public static func ==(lhs: SecureIdPhoneValue, rhs: SecureIdPhoneValue) -> Bool {
|
||||
if lhs.phone != rhs.phone {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -19,22 +19,37 @@ public func twoStepVerificationConfiguration(account: Account) -> Signal<TwoStep
|
||||
|> retryRequest
|
||||
|> map { result -> TwoStepVerificationConfiguration in
|
||||
switch result {
|
||||
case let .noPassword(_, _, emailUnconfirmedPattern):
|
||||
case let .noPassword(_, _, _, emailUnconfirmedPattern):
|
||||
return .notSet(pendingEmailPattern: emailUnconfirmedPattern)
|
||||
case let .password(_, _, _, hint, hasRecovery, emailUnconfirmedPattern):
|
||||
case let .password(_, _, _, _, hint, hasRecovery, emailUnconfirmedPattern):
|
||||
return .set(hint: hint, hasRecoveryEmail: hasRecovery == .boolTrue, pendingEmailPattern: emailUnconfirmedPattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct TwoStepVerificationSecureSecret {
|
||||
public let data: Data
|
||||
public let salt: Data
|
||||
public let hash: Int64
|
||||
}
|
||||
|
||||
public struct TwoStepVerificationSettings {
|
||||
public let email: String
|
||||
public let secureSecret: Data?
|
||||
public let secureSecret: TwoStepVerificationSecureSecret?
|
||||
}
|
||||
|
||||
public func requestTwoStepVerifiationSettings(network: Network, password: String) -> Signal<TwoStepVerificationSettings, AuthorizationPasswordVerificationError> {
|
||||
return twoStepAuthData(network)
|
||||
|> mapToSignal { authData -> Signal<TwoStepVerificationSettings, MTRpcError> in
|
||||
|> mapError { error -> AuthorizationPasswordVerificationError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .limitExceeded
|
||||
} else if error.errorDescription == "PASSWORD_HASH_INVALID" {
|
||||
return .invalidPassword
|
||||
} else {
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
|> mapToSignal { authData -> Signal<TwoStepVerificationSettings, AuthorizationPasswordVerificationError> in
|
||||
var data = Data()
|
||||
data.append(authData.currentSalt!)
|
||||
data.append(password.data(using: .utf8, allowLossyConversion: true)!)
|
||||
@@ -42,21 +57,22 @@ public func requestTwoStepVerifiationSettings(network: Network, password: String
|
||||
let currentPasswordHash = sha256Digest(data)
|
||||
|
||||
return network.request(Api.functions.account.getPasswordSettings(currentPasswordHash: Buffer(data: currentPasswordHash)), automaticFloodWait: false)
|
||||
|> map { result -> TwoStepVerificationSettings in
|
||||
|> mapError { _ -> AuthorizationPasswordVerificationError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<TwoStepVerificationSettings, AuthorizationPasswordVerificationError> in
|
||||
switch result {
|
||||
case let .passwordSettings(email, secureSecret):
|
||||
return TwoStepVerificationSettings(email: email, secureSecret: secureSecret.size == 0 ? nil : secureSecret.makeData())
|
||||
}
|
||||
}
|
||||
}
|
||||
|> `catch` { error -> Signal<TwoStepVerificationSettings, AuthorizationPasswordVerificationError> in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .fail(.limitExceeded)
|
||||
} else if error.errorDescription == "PASSWORD_HASH_INVALID" {
|
||||
return .fail(.invalidPassword)
|
||||
} else {
|
||||
case let .passwordSettings(email, secureSalt, secureSecret, secureSecretHash):
|
||||
var parsedSecureSecret: TwoStepVerificationSecureSecret?
|
||||
if secureSalt.size != 0 && secureSecret.size != 0 {
|
||||
if secureSecret.size != 32 {
|
||||
return .fail(.generic)
|
||||
}
|
||||
parsedSecureSecret = TwoStepVerificationSecureSecret(data: secureSecret.makeData(), salt: secureSalt.makeData(), hash: secureSecretHash)
|
||||
}
|
||||
return .single(TwoStepVerificationSettings(email: email, secureSecret: parsedSecureSecret))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +96,7 @@ public func updateTwoStepVerificationPassword(network: Network, currentPassword:
|
||||
|> mapError { _ -> UpdateTwoStepVerificationPasswordError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { authData -> Signal<(TwoStepAuthData, Data?), UpdateTwoStepVerificationPasswordError> in
|
||||
|> mapToSignal { authData -> Signal<(TwoStepAuthData, TwoStepVerificationSecureSecret?), UpdateTwoStepVerificationPasswordError> in
|
||||
if authData.currentSalt != nil {
|
||||
return requestTwoStepVerifiationSettings(network: network, password: currentPassword ?? "")
|
||||
|> mapError { _ -> UpdateTwoStepVerificationPasswordError in
|
||||
@@ -114,7 +130,7 @@ public func updateTwoStepVerificationPassword(network: Network, currentPassword:
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
|
||||
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)
|
||||
return network.request(Api.functions.account.updatePasswordSettings(currentPasswordHash: currentPasswordHash, newSettings: .passwordInputSettings(flags: flags, newSalt: Buffer(data: Data()), newPasswordHash: Buffer(data: Data()), hint: "", email: "", newSecureSalt: nil, newSecureSecret: nil, newSecureSecretHash: nil)), automaticFloodWait: false)
|
||||
|> mapError { _ -> UpdateTwoStepVerificationPasswordError in
|
||||
return .generic
|
||||
}
|
||||
@@ -140,18 +156,22 @@ public func updateTwoStepVerificationPassword(network: Network, currentPassword:
|
||||
updatedData.append(password.data(using: .utf8, allowLossyConversion: true)!)
|
||||
updatedData.append(nextSalt)
|
||||
|
||||
var updatedSecureSecret: Data?
|
||||
var updatedSecureSecret: TwoStepVerificationSecureSecret?
|
||||
if let encryptedSecret = secureSecret {
|
||||
flags |= 1 << 2
|
||||
if let decryptedSecret = decryptedSecureSecret(encryptedSecretData: encryptedSecret, password: currentPassword ?? "") {
|
||||
updatedSecureSecret = encryptedSecureSecret(secretData: decryptedSecret, password: password)
|
||||
if let decryptedSecret = decryptedSecureSecret(encryptedSecretData: encryptedSecret.data, password: currentPassword ?? "", salt: encryptedSecret.salt, hash: encryptedSecret.hash) {
|
||||
if let (data, salt, hash) = encryptedSecureSecret(secretData: decryptedSecret, password: password, inputSalt: authData.nextSecureSalt) {
|
||||
updatedSecureSecret = TwoStepVerificationSecureSecret(data: data, salt: salt, hash: hash)
|
||||
} else {
|
||||
updatedSecureSecret = Data()
|
||||
return .fail(.generic)
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
|
||||
let updatedPasswordHash = sha256Digest(updatedData)
|
||||
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)
|
||||
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, newSecureSalt: (updatedSecureSecret?.salt).flatMap(Buffer.init), newSecureSecret: (updatedSecureSecret?.data).flatMap(Buffer.init), newSecureSecretHash: updatedSecureSecret?.hash)), automaticFloodWait: false)
|
||||
|> map { _ -> UpdateTwoStepVerificationPasswordResult in
|
||||
return .password(password: password, pendingEmailPattern: nil)
|
||||
}
|
||||
@@ -184,7 +204,7 @@ enum UpdateTwoStepVerificationSecureSecretError {
|
||||
case generic
|
||||
}
|
||||
|
||||
func updateTwoStepVerificationSecureSecret(network: Network, password: String, updatedSecret: Data) -> Signal<UpdateTwoStepVerificationSecureSecretResult, UpdateTwoStepVerificationSecureSecretError> {
|
||||
func updateTwoStepVerificationSecureSecret(network: Network, password: String, secret: Data) -> Signal<UpdateTwoStepVerificationSecureSecretResult, UpdateTwoStepVerificationSecureSecretError> {
|
||||
return twoStepAuthData(network)
|
||||
|> mapError { _ -> UpdateTwoStepVerificationSecureSecretError in
|
||||
return .generic
|
||||
@@ -194,14 +214,22 @@ func updateTwoStepVerificationSecureSecret(network: Network, password: String, u
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
guard let passwordData = password.data(using: .utf8) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
var data = Data()
|
||||
data.append(currentSalt)
|
||||
data.append(password.data(using: .utf8, allowLossyConversion: true)!)
|
||||
data.append(passwordData)
|
||||
data.append(currentSalt)
|
||||
let currentPasswordHash = Buffer(data: sha256Digest(data))
|
||||
|
||||
guard let (encryptedSecret, secretSalt, secretHash) = encryptedSecureSecret(secretData: secret, password: password, inputSalt: authData.nextSecureSalt) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
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)
|
||||
return network.request(Api.functions.account.updatePasswordSettings(currentPasswordHash: currentPasswordHash, newSettings: .passwordInputSettings(flags: flags, newSalt: nil, newPasswordHash: nil, hint: nil, email: nil, newSecureSalt: Buffer(data: secretSalt), newSecureSecret: Buffer(data: encryptedSecret), newSecureSecretHash: secretHash)), automaticFloodWait: false)
|
||||
|> mapError { _ -> UpdateTwoStepVerificationSecureSecretError in
|
||||
return .generic
|
||||
}
|
||||
@@ -229,7 +257,7 @@ public func updateTwoStepVerificationEmail(account: Account, currentPassword: St
|
||||
}
|
||||
|
||||
let flags: Int32 = 1 << 1
|
||||
return account.network.request(Api.functions.account.updatePasswordSettings(currentPasswordHash: currentPasswordHash, newSettings: Api.account.PasswordInputSettings.passwordInputSettings(flags: flags, newSalt: nil, newPasswordHash: nil, hint: nil, email: updatedEmail, newSecureSecret: nil)), automaticFloodWait: false)
|
||||
return account.network.request(Api.functions.account.updatePasswordSettings(currentPasswordHash: currentPasswordHash, newSettings: Api.account.PasswordInputSettings.passwordInputSettings(flags: flags, newSalt: nil, newPasswordHash: nil, hint: nil, email: updatedEmail, newSecureSalt: nil, newSecureSecret: nil, newSecureSecretHash: nil)), automaticFloodWait: false)
|
||||
|> map { _ -> UpdateTwoStepVerificationPasswordResult in
|
||||
return .password(password: currentPassword, pendingEmailPattern: nil)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user