mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			129 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			129 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Swift
		
	
| // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | |
| 
 | |
| import Foundation
 | |
| 
 | |
| public struct SessionId: Equatable, Hashable, CustomStringConvertible {
 | |
|     public static let byteCount: Int = 33
 | |
|     
 | |
|     public enum Prefix: String, CaseIterable {
 | |
|         case standard = "05"    // Used for identified users, open groups, etc.
 | |
|         case blinded15 = "15"   // Used for authentication and participants in open groups with blinding enabled
 | |
|         case blinded25 = "25"   // Used for authentication and participants in open groups with blinding enabled
 | |
|         case unblinded = "00"   // Used for authentication in open groups with blinding disabled
 | |
|         case group = "03"       // Used for update group conversations
 | |
|         
 | |
|         public init(from stringValue: String?) throws {
 | |
|             guard let stringValue: String = stringValue else { throw SessionIdError.emptyValue }
 | |
|             
 | |
|             switch Prefix(rawValue: String(stringValue.prefix(2))) {
 | |
|                 case _ where stringValue.count < 2: throw SessionIdError.invalidLength
 | |
|                 case .none: throw SessionIdError.invalidPrefix
 | |
|                 case .some(let prefix) where stringValue.count == 2: self = prefix
 | |
|                 case .some(let prefix) where stringValue.count > 2:
 | |
|                     guard KeyPair.isValidHexEncodedPublicKey(candidate: stringValue) else {
 | |
|                         throw SessionIdError.invalidSessionId
 | |
|                     }
 | |
|                     
 | |
|                     self = prefix
 | |
|                     
 | |
|                 case .some: throw SessionIdError.invalidSessionId   // Should be covered by above cases
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         /// In SQLite it's more efficient to check if an indexed column is `{column} > '05' AND {column} < '06'` then it is to use
 | |
|         /// a wildcard like`{column} LIKE '05%'` as the wildcard can't use the index, so this variable is here to streamline this behaviour
 | |
|         ///
 | |
|         /// stringlint:ignore_contents
 | |
|         public var endOfRangeString: String {
 | |
|             switch self {
 | |
|                 case .standard: return "06"
 | |
|                 case .blinded15: return "16"
 | |
|                 case .blinded25: return "26"
 | |
|                 case .unblinded: return "01"
 | |
|                 case .group: return "04"
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     public let prefix: Prefix
 | |
|     public let publicKey: [UInt8]
 | |
|     private let publicKeyString: String
 | |
|     
 | |
|     public var hexString: String {
 | |
|         return prefix.rawValue + publicKeyString
 | |
|     }
 | |
|     
 | |
|     // MARK: - Initialization
 | |
|     
 | |
|     /// Takes a `String` and returns a valid `SessionId` or throws if empty/invalid
 | |
|     public init(from idString: String?) throws {
 | |
|         guard let idString: String = idString, idString.count > 2 else { throw SessionIdError.invalidSessionId }
 | |
|         
 | |
|         self.prefix = try Prefix(from: idString)
 | |
|         self.publicKey = Array(Data(hex: idString.substring(from: 2)))
 | |
|         self.publicKeyString = idString.substring(from: 2)
 | |
|     }
 | |
|     
 | |
|     /// Takes a specified `Prefix` and `publicKey` bytes and assumes the provided values are correct
 | |
|     ///
 | |
|     /// **Note:** This will remove any `Prefix` from the `publicKey` and use the provided one instead
 | |
|     public init(_ type: Prefix, publicKey: [UInt8]) {
 | |
|         self.prefix = type
 | |
|         
 | |
|         // If there was a prefix on the `publicKey` then remove it
 | |
|         let hexString: String = publicKey.toHexString()
 | |
|         
 | |
|         switch (publicKey.count, try? SessionId.Prefix(from: hexString)) {
 | |
|             case (SessionId.byteCount, .some):
 | |
|                 self.publicKey = Array(publicKey.suffix(SessionId.byteCount - 1))
 | |
|                 self.publicKeyString = Array(publicKey.suffix(SessionId.byteCount - 1)).toHexString()
 | |
|                 
 | |
|             default:
 | |
|                 self.publicKey = publicKey
 | |
|                 self.publicKeyString = hexString
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /// Takes a specified `Prefix` and `publicKey` string and assumes the provided values are correct
 | |
|     ///
 | |
|     /// **Note:** This will remove any `Prefix` from the `hex` and use the provided one instead
 | |
|     public init(_ type: Prefix, hex: String) {
 | |
|         self.prefix = type
 | |
|         
 | |
|         // If there was a prefix on the `hex` then remove it
 | |
|         switch (hex.count, try? SessionId.Prefix(from: hex)) {
 | |
|             // Multiply the `byteCount` by 2 because there are 2 characters for each byte in hex
 | |
|             case ((SessionId.byteCount * 2), .some):
 | |
|                 self.publicKey = Array(Data(hex: String(hex.suffix(((SessionId.byteCount - 1) * 2)))))
 | |
|                 self.publicKeyString = String(hex.suffix(((SessionId.byteCount - 1) * 2)))
 | |
|                 
 | |
|             default:
 | |
|                 self.publicKey = Array(Data(hex: hex))
 | |
|                 self.publicKeyString = hex
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     // MARK: - CustomStringConvertible
 | |
|     
 | |
|     public var description: String { hexString }
 | |
| }
 | |
| 
 | |
| // MARK: - SessionIdError
 | |
| 
 | |
| public enum SessionIdError: LocalizedError {
 | |
|     case emptyValue
 | |
|     case invalidLength
 | |
|     case invalidPrefix
 | |
|     case invalidSessionId
 | |
|     
 | |
|     // stringlint:ignore_contents
 | |
|     public var errorDescription: String? {
 | |
|         switch self {
 | |
|             case .emptyValue: return "Empty value."
 | |
|             case .invalidLength: return "Invalid length."
 | |
|             case .invalidPrefix: return "Invalid prefix."
 | |
|             case .invalidSessionId: return "Invalid sessionId."
 | |
|         }
 | |
|     }
 | |
| }
 |