11import Crypto
22import Foundation
33
4- extension UInt8 : ExpressibleByUnicodeScalarLiteral {
4+ extension UInt8 {
55 fileprivate static var NUL : UInt8 { return 0x00 /* yeah, just U+0000 man */ }
66 fileprivate static var comma : UInt8 { return 0x2c /* .init(ascii: ",") */ }
77 fileprivate static var equals : UInt8 { return 0x3d /* .init(ascii: "=") */ }
8- public init ( unicodeScalarLiteral value: Unicode . Scalar ) {
9- self . init ( ascii: value)
10- }
118}
129
1310fileprivate extension String {
@@ -87,7 +84,7 @@ fileprivate extension Array where Element == UInt8 {
8784 */
8885 var isValidScramValue : Bool {
8986 // TODO: FInd a better way than doing a whole construction of String...
90- return self . count > 0 && !( String ( bytes : self , encoding : . utf8 ) ? . contains ( " , " ) ?? true )
87+ return self . count > 0 && !( String ( decoding : self , as : Unicode . UTF8 . self ) . contains ( " , " ) )
9188 }
9289
9390}
@@ -171,40 +168,40 @@ fileprivate struct SCRAMMessageParser {
171168 static func parseAttributePair( name: [ UInt8 ] , value: [ UInt8 ] , isGS2Header: Bool = false ) -> SCRAMAttribute ? {
172169 guard name. count == 1 || isGS2Header else { return nil }
173170 switch name. first {
174- case " m " where !isGS2Header: return . m( value)
175- case " r " where !isGS2Header: return String ( printableAscii: value) . map { . r( $0) }
176- case " c " where !isGS2Header:
177- guard let parsedAttrs = value. decodingBase64 ( ) . flatMap ( { parse ( raw: $0, isGS2Header: true ) } ) else { return nil }
178- guard ( 1 ... 3 ) . contains ( parsedAttrs. count) else { return nil }
179- switch ( parsedAttrs. first, parsedAttrs. dropFirst ( 1 ) . first, parsedAttrs. dropFirst ( 2 ) . first) {
180- case let ( . gp( . bind( name, . none) ) , . a( ident) , . gm( data) ) : return . c( binding: . bind( name, data) , authIdentity: ident)
181- case let ( . gp( . bind( name, . none) ) , . gm( data) , . none) : return . c( binding: . bind( name, data) )
182- case let ( . gp( bind) , . a( ident) , . none) : return . c( binding: bind, authIdentity: ident)
183- case let ( . gp( bind) , . none, . none) : return . c( binding: bind)
184- default : return nil
185- }
186- case " n " where !isGS2Header: return String ( bytes: value, encoding: . utf8) ? . decodedAsSaslName. map { . n( $0) }
187- case " s " where !isGS2Header: return value. decodingBase64 ( ) . map { . s( $0) }
188- case " i " where !isGS2Header: return String ( printableAscii: value) . flatMap { UInt32 . init ( $0) } . map { . i( $0) }
189- case " p " where !isGS2Header: return value. decodingBase64 ( ) . map { . p( $0) }
190- case " v " where !isGS2Header: return value. decodingBase64 ( ) . map { . v( $0) }
191- case " e " where !isGS2Header: // TODO: actually map the specific enum string values
192- guard value. isValidScramValue else { return nil }
193- return String ( bytes: value, encoding: . utf8) . flatMap { SCRAMServerError ( rawValue: $0) } . map { . e( $0) }
194-
195- case " y " where isGS2Header && value. count == 0 : return . gp( . unused)
196- case " n " where isGS2Header && value. count == 0 : return . gp( . unsupported)
197- case " p " where isGS2Header: return String ( asciiAlphanumericMorse: value) . map { . gp( . bind( $0, nil ) ) }
198- case " a " where isGS2Header: return String ( bytes: value, encoding: . utf8) ? . decodedAsSaslName. map { . a( $0) }
199- case . none where isGS2Header: return . a( nil )
171+ case UInt8 ( ascii: " m " ) where !isGS2Header: return . m( value)
172+ case UInt8 ( ascii: " r " ) where !isGS2Header: return String ( printableAscii: value) . map { . r( $0) }
173+ case UInt8 ( ascii: " c " ) where !isGS2Header:
174+ guard let parsedAttrs = value. decodingBase64 ( ) . flatMap ( { parse ( raw: $0, isGS2Header: true ) } ) else { return nil }
175+ guard ( 1 ... 3 ) . contains ( parsedAttrs. count) else { return nil }
176+ switch ( parsedAttrs. first, parsedAttrs. dropFirst ( 1 ) . first, parsedAttrs. dropFirst ( 2 ) . first) {
177+ case let ( . gp( . bind( name, . none) ) , . a( ident) , . gm( data) ) : return . c( binding: . bind( name, data) , authIdentity: ident)
178+ case let ( . gp( . bind( name, . none) ) , . gm( data) , . none) : return . c( binding: . bind( name, data) )
179+ case let ( . gp( bind) , . a( ident) , . none) : return . c( binding: bind, authIdentity: ident)
180+ case let ( . gp( bind) , . none, . none) : return . c( binding: bind)
181+ default : return nil
182+ }
183+ case UInt8 ( ascii: " n " ) where !isGS2Header: return String ( decoding: value, as: Unicode . UTF8. self) . decodedAsSaslName. map { . n( $0) }
184+ case UInt8 ( ascii: " s " ) where !isGS2Header: return value. decodingBase64 ( ) . map { . s( $0) }
185+ case UInt8 ( ascii: " i " ) where !isGS2Header: return String ( printableAscii: value) . flatMap { UInt32 . init ( $0) } . map { . i( $0) }
186+ case UInt8 ( ascii: " p " ) where !isGS2Header: return value. decodingBase64 ( ) . map { . p( $0) }
187+ case UInt8 ( ascii: " v " ) where !isGS2Header: return value. decodingBase64 ( ) . map { . v( $0) }
188+ case UInt8 ( ascii: " e " ) where !isGS2Header: // TODO: actually map the specific enum string values
189+ guard value. isValidScramValue else { return nil }
190+ return SCRAMServerError ( rawValue: String ( decoding: value, as: Unicode . UTF8. self) ) . flatMap { . e( $0) }
200191
201- default :
202- if isGS2Header {
203- return . gm( name + value)
204- } else {
205- guard value. count > 0 , value. isValidScramValue else { return nil }
206- return . optional( name: CChar ( name [ 0 ] ) , value: value)
207- }
192+ case UInt8 ( ascii: " y " ) where isGS2Header && value. count == 0 : return . gp( . unused)
193+ case UInt8 ( ascii: " n " ) where isGS2Header && value. count == 0 : return . gp( . unsupported)
194+ case UInt8 ( ascii: " p " ) where isGS2Header: return String ( asciiAlphanumericMorse: value) . map { . gp( . bind( $0, nil ) ) }
195+ case UInt8 ( ascii: " a " ) where isGS2Header: return String ( decoding: value, as: Unicode . UTF8. self) . decodedAsSaslName. map { . a( $0) }
196+ case . none where isGS2Header: return . a( nil )
197+
198+ default :
199+ if isGS2Header {
200+ return . gm( name + value)
201+ } else {
202+ guard value. count > 0 , value. isValidScramValue else { return nil }
203+ return . optional( name: CChar ( name [ 0 ] ) , value: value)
204+ }
208205 }
209206 }
210207
@@ -230,45 +227,45 @@ fileprivate struct SCRAMMessageParser {
230227 for attribute in attributes {
231228 switch attribute {
232229 case . m( let value) :
233- result. append ( " m " ) ; result. append ( " = " ) ; result. append ( contentsOf: value)
230+ result. append ( UInt8 ( ascii : " m " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: value)
234231 case . r( let nonce) :
235- result. append ( " r " ) ; result. append ( " = " ) ; result. append ( contentsOf: nonce. utf8. map { UInt8 ( $0) } )
232+ result. append ( UInt8 ( ascii : " r " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: nonce. utf8. map { UInt8 ( $0) } )
236233 case . n( let name) :
237- result. append ( " n " ) ; result. append ( " = " ) ; result. append ( contentsOf: name. encodedAsSaslName. utf8. map { UInt8 ( $0) } )
234+ result. append ( UInt8 ( ascii : " n " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: name. encodedAsSaslName. utf8. map { UInt8 ( $0) } )
238235 case . s( let salt) :
239- result. append ( " s " ) ; result. append ( " = " ) ; result. append ( contentsOf: salt. encodingBase64 ( ) )
236+ result. append ( UInt8 ( ascii : " s " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: salt. encodingBase64 ( ) )
240237 case . i( let count) :
241- result. append ( " i " ) ; result. append ( " = " ) ; result. append ( contentsOf: " \( count) " . utf8. map { UInt8 ( $0) } )
238+ result. append ( UInt8 ( ascii : " i " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: " \( count) " . utf8. map { UInt8 ( $0) } )
242239 case . p( let proof) :
243- result. append ( " p " ) ; result. append ( " = " ) ; result. append ( contentsOf: proof. encodingBase64 ( ) )
240+ result. append ( UInt8 ( ascii : " p " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: proof. encodingBase64 ( ) )
244241 case . v( let signature) :
245- result. append ( " v " ) ; result. append ( " = " ) ; result. append ( contentsOf: signature. encodingBase64 ( ) )
242+ result. append ( UInt8 ( ascii : " v " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: signature. encodingBase64 ( ) )
246243 case . e( let error) :
247- result. append ( " e " ) ; result. append ( " = " ) ; result. append ( contentsOf: error. rawValue. utf8. map { UInt8 ( $0) } )
244+ result. append ( UInt8 ( ascii : " e " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: error. rawValue. utf8. map { UInt8 ( $0) } )
248245 case . c( let binding, let identity) :
249246 if isInitialGS2Header {
250247 switch binding {
251- case . unsupported: result. append ( " n " )
252- case . unused: result. append ( " y " )
253- case . bind( let name, _) : result. append ( " p " ) ; result. append ( " = " ) ; result. append ( contentsOf: name. utf8. map { UInt8 ( $0) } )
248+ case . unsupported: result. append ( UInt8 ( ascii : " n " ) )
249+ case . unused: result. append ( UInt8 ( ascii : " y " ) )
250+ case . bind( let name, _) : result. append ( UInt8 ( ascii : " p " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: name. utf8. map { UInt8 ( $0) } )
254251 }
255- result. append ( " , " )
252+ result. append ( . comma )
256253 if let identity = identity {
257- result. append ( " a " ) ; result. append ( " = " ) ; result. append ( contentsOf: identity. encodedAsSaslName. utf8. map { UInt8 ( $0) } )
254+ result. append ( UInt8 ( ascii : " a " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: identity. encodedAsSaslName. utf8. map { UInt8 ( $0) } )
258255 }
259- result. append ( " , " )
256+ result. append ( . comma )
260257 } else {
261258 guard var partial = serialize ( [ attribute] , isInitialGS2Header: true ) else { return nil }
262259 if case let . bind( _, data) = binding {
263260 guard let data = data else { return nil }
264261 partial. append ( contentsOf: data)
265262 }
266- result. append ( " c " ) ; result. append ( " = " ) ; result. append ( contentsOf: partial. encodingBase64 ( ) )
263+ result. append ( UInt8 ( ascii : " c " ) ) ; result. append ( . equals ) ; result. append ( contentsOf: partial. encodingBase64 ( ) )
267264 }
268265 default :
269266 return nil
270267 }
271- result. append ( " , " )
268+ result. append ( . comma )
272269 }
273270 return result. dropLast ( )
274271 }
@@ -472,7 +469,7 @@ fileprivate final class SASLMechanism_SCRAM_SHA256_Common {
472469 let saltedPassword = Hi ( string: password, salt: serverSalt, iterations: serverIterations)
473470 let clientKey = HMAC< SHA256> . authenticationCode( for: " Client Key " . data ( using: . utf8) !, using: . init( data: saltedPassword) )
474471 let storedKey = SHA256 . hash ( data: Data ( clientKey) )
475- var authMessage = firstMessageBare; authMessage. append ( " , " ) ; authMessage. append ( contentsOf: message) ; authMessage. append ( " , " ) ; authMessage. append ( contentsOf: clientFinalNoProof)
472+ var authMessage = firstMessageBare; authMessage. append ( . comma ) ; authMessage. append ( contentsOf: message) ; authMessage. append ( . comma ) ; authMessage. append ( contentsOf: clientFinalNoProof)
476473 let clientSignature = HMAC< SHA256> . authenticationCode( for: authMessage, using: . init( data: storedKey) )
477474 var clientProof = Array ( clientKey)
478475
@@ -485,7 +482,7 @@ fileprivate final class SASLMechanism_SCRAM_SHA256_Common {
485482 }
486483
487484 // Generate a `client-final-message`
488- var clientFinalMessage = clientFinalNoProof; clientFinalMessage. append ( " , " )
485+ var clientFinalMessage = clientFinalNoProof; clientFinalMessage. append ( . comma )
489486 guard let proofPart = SCRAMMessageParser . serialize ( [ . p( Array ( clientProof) ) ] ) else { throw SASLAuthenticationError . genericAuthenticationFailure }
490487 clientFinalMessage. append ( contentsOf: proofPart)
491488
@@ -590,7 +587,7 @@ fileprivate final class SASLMechanism_SCRAM_SHA256_Common {
590587 // Compute client signature
591588 let clientKey = HMAC< SHA256> . authenticationCode( for: " Client Key " . data ( using: . utf8) !, using: . init( data: saltedPassword) )
592589 let storedKey = SHA256 . hash ( data: Data ( clientKey) )
593- var authMessage = clientBareFirstMessage; authMessage. append ( " , " ) ; authMessage. append ( contentsOf: serverFirstMessage) ; authMessage. append ( " , " ) ; authMessage. append ( contentsOf: message. dropLast ( proof. count + 3 ) )
590+ var authMessage = clientBareFirstMessage; authMessage. append ( . comma ) ; authMessage. append ( contentsOf: serverFirstMessage) ; authMessage. append ( . comma ) ; authMessage. append ( contentsOf: message. dropLast ( proof. count + 3 ) )
594591 let clientSignature = HMAC< SHA256> . authenticationCode( for: authMessage, using: . init( data: storedKey) )
595592
596593 // Recompute client key from signature and proof, verify match
0 commit comments