Introduce TokenBuilder to build up OTP tokens conveniently
This commit is contained in:
parent
6817f61e3b
commit
2e744a760f
6 changed files with 363 additions and 79 deletions
|
|
@ -143,84 +143,26 @@ public class Password {
|
|||
|
||||
*/
|
||||
private func updateOtpToken() {
|
||||
self.otpToken = nil
|
||||
|
||||
// get otpauth, if we are able to generate a token, return
|
||||
if var otpauthString = getAdditionValue(withKey: Constants.OTPAUTH, caseSensitive: true) {
|
||||
if !otpauthString.hasPrefix("\(Constants.OTPAUTH):") {
|
||||
otpauthString = "\(Constants.OTPAUTH):\(otpauthString)"
|
||||
}
|
||||
if let otpauthUrl = URL(string: otpauthString),
|
||||
let token = Token(url: otpauthUrl) {
|
||||
self.otpToken = token
|
||||
if let otpauthUrl = URL(string: otpauthString), let token = Token(url: otpauthUrl) {
|
||||
otpToken = token
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// get secret data
|
||||
guard let secretString = getAdditionValue(withKey: Constants.OTP_SECRET),
|
||||
let secretData = MF_Base32Codec.data(fromBase32String: secretString),
|
||||
!secretData.isEmpty else {
|
||||
// Missing / Invalid otp secret
|
||||
return
|
||||
}
|
||||
|
||||
// get type
|
||||
guard let type = getAdditionValue(withKey: Constants.OTP_TYPE)?.lowercased(),
|
||||
(type == Constants.TOTP || type == Constants.HOTP) else {
|
||||
// Missing/Invalid OTP type
|
||||
return
|
||||
}
|
||||
|
||||
// get algorithm (optional)
|
||||
var algorithm = Generator.Algorithm.sha1
|
||||
if let algoString = getAdditionValue(withKey: Constants.OTP_ALGORITHM) {
|
||||
switch algoString.lowercased() {
|
||||
case Constants.SHA256:
|
||||
algorithm = .sha256
|
||||
case Constants.SHA512:
|
||||
algorithm = .sha512
|
||||
default:
|
||||
algorithm = .sha1
|
||||
}
|
||||
}
|
||||
|
||||
// construct the token
|
||||
if type == Constants.TOTP {
|
||||
// HOTP
|
||||
// default: 6 digits, 30 seconds
|
||||
guard let digits = Int(getAdditionValue(withKey: Constants.OTP_DIGITS) ?? Constants.DEFAULT_DIGITS),
|
||||
let period = Double(getAdditionValue(withKey: Constants.OTP_PERIOD) ?? Constants.DEFAULT_PERIOD) else {
|
||||
// Invalid OTP digits or OTP period.
|
||||
return
|
||||
}
|
||||
guard let generator = Generator(
|
||||
factor: .timer(period: period),
|
||||
secret: secretData,
|
||||
algorithm: algorithm,
|
||||
digits: digits) else {
|
||||
// Invalid OTP generator parameters.
|
||||
return
|
||||
}
|
||||
self.otpToken = Token(name: self.name, issuer: "", generator: generator)
|
||||
} else {
|
||||
// HOTP
|
||||
// default: 6 digits
|
||||
guard let digits = Int(getAdditionValue(withKey: Constants.OTP_DIGITS) ?? Constants.DEFAULT_DIGITS),
|
||||
let counter = UInt64(getAdditionValue(withKey: Constants.OTP_COUNTER) ?? Constants.DEFAULT_COUNTER) else {
|
||||
// Invalid OTP digits or OTP counter.
|
||||
return
|
||||
}
|
||||
guard let generator = Generator(
|
||||
factor: .counter(counter),
|
||||
secret: secretData,
|
||||
algorithm: algorithm,
|
||||
digits: digits) else {
|
||||
// Invalid OTP generator parameters.
|
||||
return
|
||||
}
|
||||
self.otpToken = Token(name: self.name, issuer: "", generator: generator)
|
||||
}
|
||||
otpToken = TokenBuilder()
|
||||
.usingName(name)
|
||||
.usingSecret(getAdditionValue(withKey: Constants.OTP_SECRET))
|
||||
.usingType(getAdditionValue(withKey: Constants.OTP_TYPE))
|
||||
.usingAlgorithm(getAdditionValue(withKey: Constants.OTP_ALGORITHM))
|
||||
.usingDigits(getAdditionValue(withKey: Constants.OTP_DIGITS))
|
||||
.usingPeriod(getAdditionValue(withKey: Constants.OTP_PERIOD))
|
||||
.usingCounter(getAdditionValue(withKey: Constants.OTP_COUNTER))
|
||||
.build()
|
||||
}
|
||||
|
||||
// return the description and the password strings
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ public struct Constants {
|
|||
static let HOTP = "hotp"
|
||||
static let SHA256 = "sha256"
|
||||
static let SHA512 = "sha512"
|
||||
static let DEFAULT_DIGITS = "6"
|
||||
static let DEFAULT_PERIOD = "30.0"
|
||||
static let DEFAULT_COUNTER = ""
|
||||
static let DEFAULT_DIGITS = 6
|
||||
static let DEFAULT_PERIOD = 30.0
|
||||
static let DEFAULT_COUNTER: UInt64? = nil
|
||||
|
||||
static let BLANK = " "
|
||||
static let MULTILINE_WITH_LINE_BREAK_INDICATOR = "|"
|
||||
|
|
|
|||
88
passKit/Parser/TokenBuilder.swift
Normal file
88
passKit/Parser/TokenBuilder.swift
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// TokenBuilder.swift
|
||||
// passKit
|
||||
//
|
||||
// Created by Danny Moesch on 01.12.18.
|
||||
// Copyright © 2018 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
import Base32
|
||||
import OneTimePassword
|
||||
|
||||
class TokenBuilder {
|
||||
|
||||
private var name: String = ""
|
||||
private var secret: Data?
|
||||
private var type: OtpType = .totp
|
||||
private var algorithm: Generator.Algorithm = .sha1
|
||||
private var digits: Int? = Constants.DEFAULT_DIGITS
|
||||
private var period: Double? = Constants.DEFAULT_PERIOD
|
||||
private var counter: UInt64? = Constants.DEFAULT_COUNTER
|
||||
|
||||
func usingName(_ name: String) -> TokenBuilder {
|
||||
self.name = name
|
||||
return self
|
||||
}
|
||||
|
||||
func usingSecret(_ secret: String?) -> TokenBuilder {
|
||||
if secret != nil, let secretData = MF_Base32Codec.data(fromBase32String: secret!), !secretData.isEmpty {
|
||||
self.secret = secretData
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func usingType(_ type: String?) -> TokenBuilder {
|
||||
self.type = OtpType(name: type)
|
||||
return self
|
||||
}
|
||||
|
||||
func usingAlgorithm(_ algorithm: String?) -> TokenBuilder {
|
||||
switch algorithm?.lowercased() {
|
||||
case Constants.SHA256:
|
||||
self.algorithm = .sha256
|
||||
case Constants.SHA512:
|
||||
self.algorithm = .sha512
|
||||
default:
|
||||
self.algorithm = .sha1
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func usingDigits(_ digits: String?) -> TokenBuilder {
|
||||
self.digits = digits == nil ? nil : Int(digits!)
|
||||
return self
|
||||
}
|
||||
|
||||
func usingPeriod(_ period: String?) -> TokenBuilder {
|
||||
self.period = period == nil ? nil : Double(period!)
|
||||
return self
|
||||
}
|
||||
|
||||
func usingCounter(_ counter: String?) -> TokenBuilder {
|
||||
self.counter = counter == nil ? nil : UInt64(counter!)
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
func build() -> Token? {
|
||||
guard secret != nil, digits != nil else {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch type {
|
||||
case .totp:
|
||||
return period == nil ? nil : createToken(factor: Generator.Factor.timer(period: period!))
|
||||
case .hotp:
|
||||
return counter == nil ? nil : createToken(factor: Generator.Factor.counter(counter!))
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func createToken(factor: Generator.Factor) -> Token? {
|
||||
guard let generator = Generator(factor: factor, secret: secret!, algorithm: algorithm, digits: digits!) else {
|
||||
return nil
|
||||
}
|
||||
return Token(name: name, issuer: "", generator: generator)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue