Skip to content

Commit

Permalink
Merge pull request #71 from auth0/add-otp-generators
Browse files Browse the repository at this point in the history
Add OTP Generators
  • Loading branch information
hzalaz authored Jul 24, 2018
2 parents 5b0fe6f + 1deefe6 commit 5210284
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 109 deletions.
32 changes: 24 additions & 8 deletions Guardian.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
2331C01A1DD5FC6F0047F1D4 /* Data+Base64URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2331C0191DD5FC6F0047F1D4 /* Data+Base64URL.swift */; };
233F75271D92F27000B8C15C /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233F75261D92F27000B8C15C /* Notification.swift */; };
233F75291D93076600B8C15C /* NotificationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233F75281D93076600B8C15C /* NotificationSpec.swift */; };
234004DF1D8CA944009AB77C /* OneTimePassword.swift in Sources */ = {isa = PBXBuildFile; fileRef = 234004DE1D8CA944009AB77C /* OneTimePassword.swift */; };
234004E91D9084A0009AB77C /* OneTimePasswordSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 234004E81D9084A0009AB77C /* OneTimePasswordSpec.swift */; };
234004EF1D909C91009AB77C /* Base32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 234004EE1D909C91009AB77C /* Base32.swift */; };
234B25B21D9DA37D00078FA0 /* NotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 234B25B11D9DA37D00078FA0 /* NotificationController.swift */; };
2356C82E1D88998A00B6C84A /* EnrollRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2356C82D1D88998A00B6C84A /* EnrollRequest.swift */; };
Expand Down Expand Up @@ -58,6 +56,8 @@
5F1604F02106831C00B0F25B /* AsymmetricPublicKeySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F1604EF2106831C00B0F25B /* AsymmetricPublicKeySpec.swift */; };
5F1604F22106855F00B0F25B /* KeychainRSAPrivateKeySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F1604F12106855F00B0F25B /* KeychainRSAPrivateKeySpec.swift */; };
5F1604F421068FCC00B0F25B /* AuthenticationDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F1604F321068FCC00B0F25B /* AuthenticationDevice.swift */; };
5F1604F72107A93D00B0F25B /* OneTimePasswordGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F1604F62107A93D00B0F25B /* OneTimePasswordGenerator.swift */; };
5F1604FB2107AF2800B0F25B /* OneTimePasswordGeneratorSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F1604FA2107AF2800B0F25B /* OneTimePasswordGeneratorSpec.swift */; };
5F1DB45B1DA4649600264437 /* Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F1DB45A1DA4649600264437 /* Authentication.swift */; };
5F1DB45F1DA4757800264437 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F1DB45E1DA4757800264437 /* Constants.swift */; };
5F2037D11D5E1E3E0005D2E2 /* Guardian.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F2037D01D5E1E3E0005D2E2 /* Guardian.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -146,8 +146,6 @@
2331C0191DD5FC6F0047F1D4 /* Data+Base64URL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Base64URL.swift"; sourceTree = "<group>"; };
233F75261D92F27000B8C15C /* Notification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Notification.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
233F75281D93076600B8C15C /* NotificationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationSpec.swift; sourceTree = "<group>"; };
234004DE1D8CA944009AB77C /* OneTimePassword.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OneTimePassword.swift; sourceTree = "<group>"; };
234004E81D9084A0009AB77C /* OneTimePasswordSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OneTimePasswordSpec.swift; sourceTree = "<group>"; };
234004EE1D909C91009AB77C /* Base32.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base32.swift; sourceTree = "<group>"; };
234B25AD1D9D554800078FA0 /* GuardianApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GuardianApp.entitlements; sourceTree = "<group>"; };
234B25B11D9DA37D00078FA0 /* NotificationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -189,6 +187,8 @@
5F1604EF2106831C00B0F25B /* AsymmetricPublicKeySpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsymmetricPublicKeySpec.swift; sourceTree = "<group>"; };
5F1604F12106855F00B0F25B /* KeychainRSAPrivateKeySpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainRSAPrivateKeySpec.swift; sourceTree = "<group>"; };
5F1604F321068FCC00B0F25B /* AuthenticationDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationDevice.swift; sourceTree = "<group>"; };
5F1604F62107A93D00B0F25B /* OneTimePasswordGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneTimePasswordGenerator.swift; sourceTree = "<group>"; };
5F1604FA2107AF2800B0F25B /* OneTimePasswordGeneratorSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneTimePasswordGeneratorSpec.swift; sourceTree = "<group>"; };
5F1DB45A1DA4649600264437 /* Authentication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authentication.swift; sourceTree = "<group>"; };
5F1DB45C1DA4750F00264437 /* AuthenticationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationSpec.swift; sourceTree = "<group>"; };
5F1DB45E1DA4757800264437 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -293,6 +293,22 @@
path = Data;
sourceTree = "<group>";
};
5F1604F52107A8E700B0F25B /* Generators */ = {
isa = PBXGroup;
children = (
5F1604F62107A93D00B0F25B /* OneTimePasswordGenerator.swift */,
);
path = Generators;
sourceTree = "<group>";
};
5F1604F92107AEE100B0F25B /* Generators */ = {
isa = PBXGroup;
children = (
5F1604FA2107AF2800B0F25B /* OneTimePasswordGeneratorSpec.swift */,
);
path = Generators;
sourceTree = "<group>";
};
5F1DB4561DA41BBF00264437 /* Networking */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -326,7 +342,6 @@
23B7A0461DD4E7820038124A /* A0RSA.m */,
231687161DDA071A00B09526 /* A0SHA.h */,
231687181DDA07FD00B09526 /* A0SHA.m */,
234004DE1D8CA944009AB77C /* OneTimePassword.swift */,
234004EE1D909C91009AB77C /* Base32.swift */,
2331BFEA1DD5260D0047F1D4 /* JWT.swift */,
2331C0191DD5FC6F0047F1D4 /* Data+Base64URL.swift */,
Expand Down Expand Up @@ -378,6 +393,7 @@
5F2037CF1D5E1E3E0005D2E2 /* Guardian */ = {
isa = PBXGroup;
children = (
5F1604F52107A8E700B0F25B /* Generators */,
5FAE671F21024CBA00F149A3 /* Keys */,
5F1DB4591DA4648300264437 /* Authentication */,
5F1DB47A1DA57AB300264437 /* Enrollment */,
Expand All @@ -394,14 +410,14 @@
5F2037DB1D5E1E3E0005D2E2 /* GuardianTests */ = {
isa = PBXGroup;
children = (
5F1604F92107AEE100B0F25B /* Generators */,
5F1604DE21066A4F00B0F25B /* Data */,
5F1604D1210661A300B0F25B /* Keys */,
2374A9F11D672E4200737F2E /* Utils */,
5F2037DE1D5E1E3E0005D2E2 /* Info.plist */,
2374A9EA1D67175200737F2E /* APIClientSpec.swift */,
239CE2631D80CCE5008DCAE4 /* RequestSpec.swift */,
2356C82F1D88A10600B6C84A /* GuardianSpec.swift */,
234004E81D9084A0009AB77C /* OneTimePasswordSpec.swift */,
23D3E43F1D91BEA200F3FDE2 /* Base32Spec.swift */,
233F75281D93076600B8C15C /* NotificationSpec.swift */,
5F1DB45C1DA4750F00264437 /* AuthenticationSpec.swift */,
Expand Down Expand Up @@ -658,8 +674,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
234004DF1D8CA944009AB77C /* OneTimePassword.swift in Sources */,
2374A9E41D670F5900737F2E /* APIClient.swift in Sources */,
5F1604F72107A93D00B0F25B /* OneTimePasswordGenerator.swift in Sources */,
234004EF1D909C91009AB77C /* Base32.swift in Sources */,
2374A9EF1D672ABD00737F2E /* Result.swift in Sources */,
23AD869F1DEE10B500051F41 /* FailedRequest.swift in Sources */,
Expand Down Expand Up @@ -708,6 +724,7 @@
2356C8301D88A10600B6C84A /* GuardianSpec.swift in Sources */,
2374A9F51D67339E00737F2E /* Matchers.swift in Sources */,
23C67AEC1D81D6A400A38A2E /* MockNSURLSession.swift in Sources */,
5F1604FB2107AF2800B0F25B /* OneTimePasswordGeneratorSpec.swift in Sources */,
5F1604C92106493600B0F25B /* SigningKeyStorageSpec.swift in Sources */,
5F7969341DDABDDE006AC7BA /* AuthenticationSpec.swift in Sources */,
2374A9F31D672E4F00737F2E /* Responses.swift in Sources */,
Expand All @@ -716,7 +733,6 @@
2374A9EB1D67175200737F2E /* APIClientSpec.swift in Sources */,
23D3E4401D91BEA200F3FDE2 /* Base32Spec.swift in Sources */,
5F1604F02106831C00B0F25B /* AsymmetricPublicKeySpec.swift in Sources */,
234004E91D9084A0009AB77C /* OneTimePasswordSpec.swift in Sources */,
5F1604F22106855F00B0F25B /* KeychainRSAPrivateKeySpec.swift in Sources */,
5F1604EC2106709B00B0F25B /* DataRSAPrivateKeySpec.swift in Sources */,
);
Expand Down
93 changes: 93 additions & 0 deletions Guardian/Generators/OneTimePasswordGenerator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// OneTimePasswordGenerator.swift
//
// Copyright (c) 2018 Auth0 (http://auth0.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Foundation

public protocol TOTP {
func new(time: TimeInterval, period: Int) -> String
}

extension TOTP {
public func new(time: TimeInterval = Date().timeIntervalSince1970, period: Int = 30) -> String {
return self.new(time: time, period: period)
}
}

public protocol HOTP {
func new(counter: Int) -> String
}

public func totp(base32Secret: String, algorithm: HMACAlgorithm, digits: Int = 6) throws -> TOTP {
guard let secret = Base32.decode(string: base32Secret) else { throw GuardianError.invalidBase32Secret }
return try totp(secret: secret, algorithm: algorithm, digits: digits)
}

public func totp(secret: Data, algorithm: HMACAlgorithm, digits: Int = 6) throws -> TOTP {
return try OneTimePasswordGenerator(secret: secret, algorithm: algorithm, digits: digits)
}

public func hotp(secret: Data, algorithm: HMACAlgorithm, digits: Int = 6) throws -> HOTP {
return try OneTimePasswordGenerator(secret: secret, algorithm: algorithm, digits: digits)
}

public enum HMACAlgorithm: String {
case sha1
case sha256
case sha512

func hmac(secret: Data) -> A0HMAC {
return A0HMAC(algorithm: self.rawValue, key: secret)!
}
}

struct OneTimePasswordGenerator: TOTP, HOTP {
let digits: Int
let hmac: A0HMAC

init(secret: Data, algorithm: HMACAlgorithm, digits: Int) throws {
self.hmac = algorithm.hmac(secret: secret)
self.digits = digits
}

func new(counter: Int) -> String {
var c = UInt64(counter).bigEndian
let buffer = Data(bytes: &c, count: MemoryLayout<UInt64>.size);
let digestData = hmac.sign(buffer)
let hash = digestData.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> UInt32 in
let last = bytes.advanced(by: hmac.digestLength - 1)
let offset = last.pointee & 0x0f
let start = bytes.advanced(by: Int(offset))
let value = start.withMemoryRebound(to: UInt32.self, capacity: 1) { $0 }
var hash = UInt32(bigEndian: value.pointee)
hash &= 0x7fffffff
hash = hash % UInt32(pow(10, Float(digits)))
return hash
}

return String(format: "%0\(digits)d", Int(hash))
}

func new(time: TimeInterval, period: Int) -> String {
let steps = time / Double(period)
return self.new(counter: Int(steps))
}
}
56 changes: 0 additions & 56 deletions Guardian/OneTimePassword.swift

This file was deleted.

Loading

0 comments on commit 5210284

Please sign in to comment.