Support Steam OTP (#505)

This commit is contained in:
Mingshen Sun 2021-09-06 10:47:04 -07:00 committed by GitHub
parent 06d2ef1d09
commit f2ab400f4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 85 additions and 34 deletions

View file

@ -72,7 +72,6 @@
30A3001826DA6974002A734E /* SwiftyUserDefaults in Frameworks */ = {isa = PBXBuildFile; productRef = 30A3001726DA6974002A734E /* SwiftyUserDefaults */; };
30A3001A26DA697C002A734E /* SwiftyUserDefaults in Frameworks */ = {isa = PBXBuildFile; productRef = 30A3001926DA697C002A734E /* SwiftyUserDefaults */; };
30A3001C26DA91BF002A734E /* SwiftyUserDefaults in Frameworks */ = {isa = PBXBuildFile; productRef = 30A3001B26DA91BF002A734E /* SwiftyUserDefaults */; };
30A3001E26DA91C4002A734E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 30A3001D26DA91C4002A734E /* OneTimePassword */; };
30A3002026DA91D7002A734E /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 30A3001F26DA91D7002A734E /* Base32 */; };
30A69948240EED5E00B7D967 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A69947240EED5E00B7D967 /* IntentHandler.swift */; };
30A86F95230F237000F821A4 /* CryptoFrameworkTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A86F94230F237000F821A4 /* CryptoFrameworkTest.swift */; };
@ -108,7 +107,9 @@
556EC3D922335D2800934F9C /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 30BF5ED521ED2434000E4154 /* Localizable.stringsdict */; };
556EC3DA22335D3400934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; };
556EC3DB22335D3D00934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; };
9A17C06726DDAAE400C23FAB /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A17C06626DDAAE400C23FAB /* OneTimePassword */; };
9A1D1CE526E5D1CE0052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE426E5D1CE0052028E /* OneTimePassword */; };
9A1D1CE726E5D2230052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE626E5D2230052028E /* OneTimePassword */; };
9A1F47FA26E5CF4B000C0E01 /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1F47F926E5CF4B000C0E01 /* OneTimePassword */; };
9A55C158259E785600FA8FD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC917BDD1E2E8231000FDF54 /* Assets.xcassets */; };
9A55C15F259E785700FA8FD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC917BDD1E2E8231000FDF54 /* Assets.xcassets */; };
9A55C185259E8C5600FA8FD9 /* PasswordsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A55C184259E8C5600FA8FD9 /* PasswordsViewController.swift */; };
@ -127,7 +128,6 @@
9A8F9ECC259ECB410027CE15 /* PasswordSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9ECB259ECB410027CE15 /* PasswordSelectionDelegate.swift */; };
9A8F9F4025A1A91F0027CE15 /* CredentialProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9F3F25A1A91F0027CE15 /* CredentialProvider.swift */; };
9A996C5326DDF61F00A4485D /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 9A996C5226DDF61F00A4485D /* Base32 */; };
9A996C5526DDF62300A4485D /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A996C5426DDF62300A4485D /* OneTimePassword */; };
9A996C5726DDF65900A4485D /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 9A996C5626DDF65900A4485D /* Base32 */; };
9A996C5826DEB0D100A4485D /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; };
9A996C5926DEB0D200A4485D /* passKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -511,7 +511,7 @@
9ADAB21D26DDA52400900F10 /* Gopenpgp.xcframework in Frameworks */,
30A3001426DA6692002A734E /* KeychainAccess in Frameworks */,
9A996C5726DDF65900A4485D /* Base32 in Frameworks */,
9A17C06726DDAAE400C23FAB /* OneTimePassword in Frameworks */,
9A1D1CE526E5D1CE0052028E /* OneTimePassword in Frameworks */,
30A3001626DA6697002A734E /* SwiftyUserDefaults in Frameworks */,
3032DA5626DAF4E500A7728C /* ObjectivePGP in Frameworks */,
);
@ -521,10 +521,10 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
30A3001E26DA91C4002A734E /* OneTimePassword in Frameworks */,
A26075811EEC6F34005DB03E /* passKit.framework in Frameworks */,
30A3002026DA91D7002A734E /* Base32 in Frameworks */,
30A3001C26DA91BF002A734E /* SwiftyUserDefaults in Frameworks */,
9A1D1CE726E5D2230052028E /* OneTimePassword in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -549,7 +549,7 @@
files = (
3010CB6026DA4F87008964D2 /* SwiftyUserDefaults in Frameworks */,
3010CB6326DA4FE9008964D2 /* FavIcon in Frameworks */,
9A996C5526DDF62300A4485D /* OneTimePassword in Frameworks */,
9A1F47FA26E5CF4B000C0E01 /* OneTimePassword in Frameworks */,
9A996C5326DDF61F00A4485D /* Base32 in Frameworks */,
3032DA5426DAF4C200A7728C /* ObjectivePGP in Frameworks */,
3010CB6626DA500F008964D2 /* KeychainAccess in Frameworks */,
@ -1087,8 +1087,8 @@
30A3001326DA6692002A734E /* KeychainAccess */,
30A3001526DA6697002A734E /* SwiftyUserDefaults */,
3032DA5526DAF4E500A7728C /* ObjectivePGP */,
9A17C06626DDAAE400C23FAB /* OneTimePassword */,
9A996C5626DDF65900A4485D /* Base32 */,
9A1D1CE426E5D1CE0052028E /* OneTimePassword */,
);
productName = passKit;
productReference = A26075781EEC6F34005DB03E /* passKit.framework */;
@ -1111,8 +1111,8 @@
name = passKitTests;
packageProductDependencies = (
30A3001B26DA91BF002A734E /* SwiftyUserDefaults */,
30A3001D26DA91C4002A734E /* OneTimePassword */,
30A3001F26DA91D7002A734E /* Base32 */,
9A1D1CE626E5D2230052028E /* OneTimePassword */,
);
productName = passKitTests;
productReference = A26075801EEC6F34005DB03E /* passKitTests.xctest */;
@ -1185,7 +1185,7 @@
3010CB6526DA500F008964D2 /* KeychainAccess */,
3032DA5326DAF4C200A7728C /* ObjectivePGP */,
9A996C5226DDF61F00A4485D /* Base32 */,
9A996C5426DDF62300A4485D /* OneTimePassword */,
9A1F47F926E5CF4B000C0E01 /* OneTimePassword */,
);
productName = pass;
productReference = DC917BD31E2E8231000FDF54 /* Pass.app */;
@ -1285,9 +1285,9 @@
3010CB5E26DA4F87008964D2 /* XCRemoteSwiftPackageReference "SwiftyUserDefaults" */,
3010CB6126DA4FE9008964D2 /* XCRemoteSwiftPackageReference "FavIcon" */,
3010CB6426DA500F008964D2 /* XCRemoteSwiftPackageReference "KeychainAccess" */,
3010CB6726DA50B3008964D2 /* XCRemoteSwiftPackageReference "OneTimePassword" */,
30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */,
3032DA5226DAF4C200A7728C /* XCRemoteSwiftPackageReference "ObjectivePGP" */,
9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */,
);
productRefGroup = DC917BD41E2E8231000FDF54 /* Products */;
projectDirPath = "";
@ -2789,14 +2789,6 @@
minimumVersion = 4.2.2;
};
};
3010CB6726DA50B3008964D2 /* XCRemoteSwiftPackageReference "OneTimePassword" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mattrubin/OneTimePassword";
requirement = {
branch = develop;
kind = branch;
};
};
3032DA5226DAF4C200A7728C /* XCRemoteSwiftPackageReference "ObjectivePGP" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/krzyzanowskim/ObjectivePGP";
@ -2813,6 +2805,14 @@
kind = branch;
};
};
9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mssun/OneTimePassword";
requirement = {
branch = passforios;
kind = branch;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
@ -2866,19 +2866,24 @@
package = 3010CB5E26DA4F87008964D2 /* XCRemoteSwiftPackageReference "SwiftyUserDefaults" */;
productName = SwiftyUserDefaults;
};
30A3001D26DA91C4002A734E /* OneTimePassword */ = {
isa = XCSwiftPackageProductDependency;
package = 3010CB6726DA50B3008964D2 /* XCRemoteSwiftPackageReference "OneTimePassword" */;
productName = OneTimePassword;
};
30A3001F26DA91D7002A734E /* Base32 */ = {
isa = XCSwiftPackageProductDependency;
package = 30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */;
productName = Base32;
};
9A17C06626DDAAE400C23FAB /* OneTimePassword */ = {
9A1D1CE426E5D1CE0052028E /* OneTimePassword */ = {
isa = XCSwiftPackageProductDependency;
package = 3010CB6726DA50B3008964D2 /* XCRemoteSwiftPackageReference "OneTimePassword" */;
package = 9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */;
productName = OneTimePassword;
};
9A1D1CE626E5D2230052028E /* OneTimePassword */ = {
isa = XCSwiftPackageProductDependency;
package = 9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */;
productName = OneTimePassword;
};
9A1F47F926E5CF4B000C0E01 /* OneTimePassword */ = {
isa = XCSwiftPackageProductDependency;
package = 9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */;
productName = OneTimePassword;
};
9A996C5226DDF61F00A4485D /* Base32 */ = {
@ -2886,11 +2891,6 @@
package = 30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */;
productName = Base32;
};
9A996C5426DDF62300A4485D /* OneTimePassword */ = {
isa = XCSwiftPackageProductDependency;
package = 3010CB6726DA50B3008964D2 /* XCRemoteSwiftPackageReference "OneTimePassword" */;
productName = OneTimePassword;
};
9A996C5626DDF65900A4485D /* Base32 */ = {
isa = XCSwiftPackageProductDependency;
package = 30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */;

View file

@ -39,10 +39,10 @@
},
{
"package": "OneTimePassword",
"repositoryURL": "https://github.com/mattrubin/OneTimePassword",
"repositoryURL": "https://github.com/mssun/OneTimePassword",
"state": {
"branch": "develop",
"revision": "bd2a8fa24057916e4e543ae323e34f75ae744db8",
"branch": "passforios",
"revision": "cad8872ed9f506bfba6f2ad51673f267602f0879",
"version": null
}
},

View file

@ -165,6 +165,7 @@ public class Password {
.usingDigits(getAdditionValue(withKey: Constants.OTP_DIGITS))
.usingPeriod(getAdditionValue(withKey: Constants.OTP_PERIOD))
.usingCounter(getAdditionValue(withKey: Constants.OTP_COUNTER))
.usingRepresentation(getAdditionValue(withKey: Constants.OTP_REPRESENTATION))
.build()
}

View file

@ -6,6 +6,8 @@
// Copyright © 2018 Bob Sun. All rights reserved.
//
import OneTimePassword
public enum Constants {
static let OTP_SECRET = "otp_secret"
static let OTP_TYPE = "otp_type"
@ -13,6 +15,7 @@ public enum Constants {
static let OTP_PERIOD = "otp_period"
static let OTP_DIGITS = "otp_digits"
static let OTP_COUNTER = "otp_counter"
static let OTP_REPRESENTATION = "otp_representation"
static let OTPAUTH = "otpauth"
public static let OTP_KEYWORDS = [
@ -22,6 +25,7 @@ public enum Constants {
OTP_PERIOD,
OTP_DIGITS,
OTP_COUNTER,
OTP_REPRESENTATION,
OTPAUTH,
]
@ -32,6 +36,7 @@ public enum Constants {
static let DEFAULT_DIGITS = 6
static let DEFAULT_PERIOD = 30.0
static let DEFAULT_COUNTER: UInt64? = nil
static let DEFAULT_REPRESENTATION: OneTimePassword.Generator.Representation = .numeric
static let BLANK = " "
static let MULTILINE_WITH_LINE_BREAK_INDICATOR = "|"

View file

@ -34,6 +34,7 @@ class TokenBuilder {
private var digits: Int? = Constants.DEFAULT_DIGITS
private var period: Double? = Constants.DEFAULT_PERIOD
private var counter: UInt64? = Constants.DEFAULT_COUNTER
private var representation: OneTimePassword.Generator.Representation = Constants.DEFAULT_REPRESENTATION
func usingName(_ name: String) -> TokenBuilder {
self.name = name
@ -79,6 +80,18 @@ class TokenBuilder {
return self
}
func usingRepresentation(_ representation: String?) -> TokenBuilder {
switch representation {
case "numeric":
self.representation = .numeric
case "steamguard":
self.representation = .steamguard
default:
self.representation = .numeric
}
return self
}
func build() -> Token? {
guard secret != nil, digits != nil else {
return nil
@ -95,7 +108,7 @@ class TokenBuilder {
}
private func createToken(factor: Generator.Factor) -> Token? {
guard let generator = Generator(factor: factor, secret: secret!, algorithm: algorithm, digits: digits!) else {
guard let generator = Generator(factor: factor, secret: secret!, algorithm: algorithm, digits: digits!, representation: representation) else {
return nil
}
return Token(name: name, issuer: "", generator: generator)

View file

@ -317,4 +317,14 @@ class PasswordTest: XCTestCase {
XCTAssertNotNil(otpStrings)
XCTAssertEqual(otpStrings!.description, "HmacBased".localize())
}
func testSteamOtpStringsToken() {
let password = getPasswordObjectWith(content: STEAM_TOTP_URL)
let otpStrings = password.getOtpStrings()
let otpDescription = otpStrings!.description
XCTAssertNotNil(otpStrings)
XCTAssert(otpDescription.hasPrefix("TimeBased".localize() + " ("))
XCTAssert(otpDescription.hasSuffix(")"))
}
}

View file

@ -171,6 +171,27 @@ class TokenBuilderTest: XCTestCase {
}
}
func testRepresentation() {
[
(nil, .numeric),
("steamguard", .steamguard),
("numeric", .numeric),
("wrong representation", .numeric),
].forEach { (inputRepresentation: String?, represenetation: OneTimePassword.Generator.Representation) in
var builder = TokenBuilder()
.usingSecret(SECRET)
.usingType("totp")
.usingRepresentation(inputRepresentation)
if represenetation == .steamguard {
builder = builder
.usingDigits("5")
.usingAlgorithm("sha1")
}
let token = builder.build()
XCTAssertEqual(token?.generator.representation, represenetation)
}
}
func testUnparsableCounter() {
let token = TokenBuilder()
.usingSecret(SECRET)

View file

@ -14,6 +14,7 @@ let PASSWORD_PATH = "/path/to/password"
let PASSWORD_URL = URL(fileURLWithPath: "/path/to/password")
let PASSWORD_STRING = "abcd1234"
let TOTP_URL = "otpauth://totp/email@email.com?secret=abcd1234"
let STEAM_TOTP_URL = "otpauth://totp/username?secret=12345678901234567890&issuer=Steam&algorithm=SHA1&digits=5&period=30&representation=steamguard"
let HOTP_URL = "otpauth://hotp/email@email.com?secret=abcd1234"
let FIELD = "key" => "value"