Introduce TokenBuilder to build up OTP tokens conveniently

This commit is contained in:
Danny Moesch 2018-12-01 15:53:48 +01:00 committed by Bob Sun
parent 6817f61e3b
commit 2e744a760f
6 changed files with 363 additions and 79 deletions

View file

@ -15,7 +15,8 @@ class PasswordTest: XCTestCase {
private let PASSWORD_PATH = "/path/to/password"
private let PASSWORD_URL = URL(fileURLWithPath: "/path/to/password")
private let PASSWORD_STRING = "abcd1234"
private let OTP_TOKEN = "otpauth://totp/email@email.com?secret=abcd1234"
private let TOTP_URL = "otpauth://totp/email@email.com?secret=abcd1234"
private let HOTP_URL = "otpauth://hotp/email@email.com?secret=abcd1234"
private let SECURE_URL_FIELD = "url" => "https://secure.com"
private let INSECURE_URL_FIELD = "url" => "http://insecure.com"
@ -23,6 +24,7 @@ class PasswordTest: XCTestCase {
private let USERNAME_FIELD = "username" => "some username"
private let NOTE_FIELD = "note" => "A NOTE"
private let HINT_FIELD = "some hints" => "äöüß // €³ %% −° && @²` | [{\\}],.<>"
private let TOTP_URL_FIELD = "otpauth" => "//totp/email@email.com?secret=abcd1234"
func testUrl() {
let password = getPasswordObjectWith(content: "")
@ -158,7 +160,7 @@ class PasswordTest: XCTestCase {
}
func testPasswordFileWithOtpToken() {
let additions = NOTE_FIELD | OTP_TOKEN
let additions = NOTE_FIELD | TOTP_URL
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
@ -166,22 +168,52 @@ class PasswordTest: XCTestCase {
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertEqual(password.otpType, OtpType.totp)
XCTAssertEqual(password.otpType, .totp)
XCTAssertNotNil(password.currentOtp)
}
func testFirstLineIsOtpToken() {
let password = getPasswordObjectWith(content: OTP_TOKEN)
let password = getPasswordObjectWith(content: TOTP_URL)
XCTAssertEqual(password.password, OTP_TOKEN)
XCTAssertEqual(password.plainData, OTP_TOKEN.data(using: .utf8))
XCTAssertEqual(password.password, TOTP_URL)
XCTAssertEqual(password.plainData, TOTP_URL.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, "")
XCTAssertNil(password.username)
XCTAssertNil(password.urlString)
XCTAssertNil(password.login)
XCTAssertEqual(password.otpType, OtpType.totp)
XCTAssertEqual(password.otpType, .totp)
XCTAssertNotNil(password.currentOtp)
}
func testOtpTokenAsField() {
let additions = TOTP_URL_FIELD.asString
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PASSWORD_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertEqual(password.otpType, .totp)
XCTAssertNotNil(password.currentOtp)
}
func testOtpTokenFromFields() {
let additions =
Constants.OTP_SECRET => "secret" |
Constants.OTP_TYPE => "hotp" |
Constants.OTP_COUNTER => "12" |
Constants.OTP_DIGITS => "7"
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PASSWORD_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertEqual(password.otpType, .hotp)
XCTAssertNotNil(password.currentOtp)
}

View file

@ -0,0 +1,214 @@
//
// TokenBuilderTest.swift
// passKitTests
//
// Created by Danny Moesch on 01.12.18.
// Copyright © 2018 Bob Sun. All rights reserved.
//
import Base32
import OneTimePassword
import XCTest
@testable import passKit
class TokenBuilderTest: XCTestCase {
private let SECRET = "secret"
private let DIGITS = Constants.DEFAULT_DIGITS
private let TIMER = Generator.Factor.timer(period: Constants.DEFAULT_PERIOD)
func testNoSecret() {
XCTAssertNil(TokenBuilder().build())
XCTAssertNil(TokenBuilder().usingSecret(nil).build())
XCTAssertNil(TokenBuilder().usingSecret("").build())
}
func testDefault() {
let token = TokenBuilder()
.usingSecret(SECRET)
.build()
XCTAssertEqual(token?.generator.secret, MF_Base32Codec.data(fromBase32String: SECRET))
XCTAssertEqual(token?.generator.factor, TIMER)
XCTAssertEqual(token?.generator.algorithm, .sha1)
XCTAssertEqual(token?.generator.digits, DIGITS)
}
func testName() {
[
"some name",
"a",
"totp",
].forEach { name in
let token = TokenBuilder()
.usingName(name)
.usingSecret(SECRET)
.build()
XCTAssertEqual(token?.name, name)
}
}
func testTypeNone() {
let token = TokenBuilder()
.usingSecret(SECRET)
.usingType("something")
.build()
XCTAssertNil(token)
}
func testTypeTotp() {
let token = TokenBuilder()
.usingSecret(SECRET)
.usingType("toTp")
.build()
XCTAssertEqual(token?.generator.factor, TIMER)
}
func testTypeHotp() {
let token = TokenBuilder()
.usingSecret(SECRET)
.usingType("HotP")
.usingCounter("4")
.build()
XCTAssertEqual(token?.generator.factor, Generator.Factor.counter(4))
}
func testAlgorithm() {
[
("sha1", .sha1),
("something", .sha1),
(nil, .sha1),
("sha256", .sha256),
("Sha256", .sha256),
("sha512", .sha512),
("sHA512", .sha512),
].forEach { (inputAlgorithm: String?, algorithm: Generator.Algorithm) in
let token = TokenBuilder()
.usingSecret(SECRET)
.usingAlgorithm(inputAlgorithm)
.build()
XCTAssertEqual(token?.generator.algorithm, algorithm)
}
}
func testDigits() {
[
(nil, nil),
(5, nil),
(6, 6),
(7, 7),
(8, 8),
(9, nil),
].forEach { inputDigits, digits in
let token = TokenBuilder()
.usingSecret(SECRET)
.usingDigits(inputDigits == nil ? nil : String(inputDigits!))
.build()
XCTAssertEqual(token?.generator.digits, digits)
}
}
func testUnparsableDigits() {
let token = TokenBuilder()
.usingSecret(SECRET)
.usingDigits("unparsable digits")
.build()
XCTAssertNil(token)
}
func testPeriod() {
[
(nil, nil),
(1.2, 1.2),
(-12.0, nil),
(27.5, 27.5),
(35.0, 35.0),
(120.7, 120.7),
].forEach { inputPeriod, period in
let token = TokenBuilder()
.usingSecret(SECRET)
.usingPeriod(inputPeriod == nil ? nil : String(inputPeriod!))
.build()
let timer = period == nil ? nil : Generator.Factor.timer(period: period!)
XCTAssertEqual(token?.generator.factor, timer)
}
}
func testUnparsablePeriod() {
let token = TokenBuilder()
.usingSecret(SECRET)
.usingPeriod("unparsable period")
.build()
XCTAssertNil(token)
}
func testCounter() {
[
(nil, nil),
(1, 1),
(0, 0),
(27, 27),
(120, 120),
(4321, 4321),
].forEach { inputCounter, counter in
let token = TokenBuilder()
.usingSecret(SECRET)
.usingType("hotp")
.usingCounter(inputCounter == nil ? nil : String(inputCounter!))
.build()
let counter = counter == nil ? nil : Generator.Factor.counter(UInt64(counter!))
XCTAssertEqual(token?.generator.factor, counter)
}
}
func testUnparsableCounter() {
let token = TokenBuilder()
.usingSecret(SECRET)
.usingType("hotp")
.usingCounter("unparsable counter")
.build()
XCTAssertNil(token)
}
func testAllMixed() {
let builder = TokenBuilder()
.usingName("name")
.usingSecret(SECRET)
.usingAlgorithm("sha512")
.usingDigits("7")
.usingPeriod("42")
.usingCounter("12")
let totpToken = builder.usingType("totp").build()
XCTAssertNotNil(totpToken)
XCTAssertEqual(totpToken?.name, "name")
XCTAssertEqual(totpToken?.currentPassword?.count, 7)
XCTAssertEqual(totpToken?.generator.algorithm, .sha512)
XCTAssertEqual(totpToken?.generator.digits, 7)
XCTAssertEqual(totpToken?.generator.factor, .timer(period: 42))
XCTAssertEqual(totpToken?.generator.secret, MF_Base32Codec.data(fromBase32String: SECRET))
let hotpToken = builder.usingType("hotp").build()
XCTAssertNotNil(hotpToken)
XCTAssertEqual(hotpToken?.name, "name")
XCTAssertEqual(hotpToken?.currentPassword?.count, 7)
XCTAssertEqual(hotpToken?.generator.algorithm, .sha512)
XCTAssertEqual(hotpToken?.generator.digits, 7)
XCTAssertEqual(hotpToken?.generator.factor, .counter(12))
XCTAssertEqual(hotpToken?.generator.secret, MF_Base32Codec.data(fromBase32String: SECRET))
}
}