Skip to content

Commit

Permalink
Merge pull request #23 from gaetanomatonti/release/0.2.0
Browse files Browse the repository at this point in the history
Release 0.2.0
  • Loading branch information
gaetanomatonti authored Sep 16, 2021
2 parents 1670508 + 17d3581 commit e419310
Show file tree
Hide file tree
Showing 19 changed files with 756 additions and 224 deletions.
51 changes: 0 additions & 51 deletions Sources/Uno/Algorithm.swift

This file was deleted.

35 changes: 0 additions & 35 deletions Sources/Uno/AuthenticationCodeGenerator.swift

This file was deleted.

9 changes: 8 additions & 1 deletion Sources/Uno/Extensions/Data+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,15 @@ extension Data {

extension Data {
/// The possible errors regarding the `Data` type.
enum Error: Swift.Error {
enum Error: Swift.Error, LocalizedError {
/// The count of the converted bytes doesn't match the expected byte count.
case bytesCountMismatch

var errorDescription: String? {
switch self {
case .bytesCountMismatch:
return "The count of the converted bytes doesn't match the expected byte count."
}
}
}
}
25 changes: 25 additions & 0 deletions Sources/Uno/Extensions/URLQueryItem+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Uno
//
// Created by Gaetano Matonti on 06/09/21.
//

import Foundation

extension Array where Element == URLQueryItem {
/// Accesses the value of the `URLQueryItem` for the specified `URIParser.ItemKey`.
/// - Parameter key: The `URIParser.ItemKey` representing the name of the `URLQueryItem`.
/// - Returns: An optional `String` representing the value of the query item. `nil` if a value couldn't be found for the specified key.
subscript(_ key: URIParser.ItemKey) -> String? {
value(for: key)
}

/// Gets the value of a `URLQueryItem` from its key.
/// - Parameter key: The `ItemKey` representing the name of the query item.
/// - Returns: An optional `String` representing the value of the query item. `nil` if a value couldn't be found for the specified key.
func value(for key: URIParser.ItemKey) -> String? {
first {
$0.name == key.rawValue
}?.value
}
}
19 changes: 19 additions & 0 deletions Sources/Uno/Generator/AuthenticationCodeGenerator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Uno
//
// Created by Gaetano Matonti on 28/08/21.
//

import Foundation

/// A protocol the represents the requirements for a one-time password generator.
public protocol AuthenticationCodeGenerator {
/// The secret to seed into the generator.
var secret: OneTimePassword.Secret { get }

/// The amount of digits composing the authentication code.
var codeLength: OneTimePassword.Length { get }

/// The hash function used to generate the authentication code's hash.
var algorithm: OneTimePassword.Algorithm { get }
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,22 @@ import Foundation

/// A type that represents an HMAC-based one-time password.
public struct CounterBasedGenerator: AuthenticationCodeGenerator {
/// The possible errors thrown in `CounterBasedGenerator`.
public enum Error: Swift.Error {
/// The length of the authentication code is not supported.
case codeLengthNotSupported
}

// MARK: - Stored Properties

public let secret: Secret
public let secret: OneTimePassword.Secret

public let codeLength: Int
public let codeLength: OneTimePassword.Length

public let algorithm: Algorithm
public let algorithm: OneTimePassword.Algorithm

// MARK: - Init

public init(secret: Secret, codeLength: Int = 6, algorithm: Algorithm = .sha1) {
public init(
secret: OneTimePassword.Secret,
codeLength: OneTimePassword.Length = .six,
algorithm: OneTimePassword.Algorithm = .sha1
) {
self.secret = secret
self.codeLength = codeLength
self.algorithm = algorithm
Expand All @@ -46,7 +45,7 @@ extension CounterBasedGenerator {
/// - Returns: A `String` representing the generated one-time password.
public func generate(from counter: UInt64) throws -> String {
let hmac = try generateHMAC(from: counter)
let code = hmac.dynamicallyTrimmed(numberOfDigits: codeLength)
let code = hmac.dynamicallyTrimmed(numberOfDigits: codeLength.rawValue)
return code
}

Expand All @@ -55,12 +54,8 @@ extension CounterBasedGenerator {
/// - counter: A variable number that acts as a seed for the generator.
/// - Returns: A `Data` payload representing a HMAC.
func generateHMAC(from counter: UInt64) throws -> Data {
guard isCodeLengthValid else {
throw Error.codeLengthNotSupported
}

guard secret.isValid(for: algorithm) else {
throw Algorithm.Error.invalidMinimumKeySize
throw OneTimePassword.Algorithm.Error.invalidMinimumKeySize
}

var counter = counter.bigEndian
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,14 @@ import Foundation

/// A type that represents a Time-based one-time password.
public struct TimeBasedGenerator: AuthenticationCodeGenerator {
/// The possible errors thrown in the `TimeBasedGenerator`.
public enum Error: Swift.Error {
/// The provided timestep is not valid.
case timestepInvalid

/// The time for counter computation is invalid.
case timeInvalid
}

// MARK: - Stored Properties

public let secret: Secret
public let secret: OneTimePassword.Secret

public let codeLength: Int
public let codeLength: OneTimePassword.Length

public let algorithm: Algorithm
public let algorithm: OneTimePassword.Algorithm

/// The period of validity of the authentication code expressed in seconds.
public let timestep: TimeInterval
Expand All @@ -39,7 +31,12 @@ public struct TimeBasedGenerator: AuthenticationCodeGenerator {

// MARK: - Init

public init(secret: Secret, codeLength: Int = 6, algorithm: Algorithm = .sha1, timestep: TimeInterval) {
public init(
secret: OneTimePassword.Secret,
codeLength: OneTimePassword.Length = .six,
algorithm: OneTimePassword.Algorithm = .sha1,
timestep: TimeInterval
) {
self.secret = secret
self.codeLength = codeLength
self.algorithm = algorithm
Expand All @@ -63,7 +60,7 @@ extension TimeBasedGenerator {
/// - Returns: A `String` representing the generated one-time password.
public func generate(from secondsSince1970: TimeInterval) throws -> String {
guard secondsSince1970 >= 0 else {
throw Error.timeInvalid
throw Error.invalidTime
}

let counterFromSeconds = try counter(from: secondsSince1970)
Expand All @@ -75,7 +72,7 @@ extension TimeBasedGenerator {
/// - Returns: A `UInt64` representing the counter factor for HOTP generation.
func counter(from secondsSince1970: TimeInterval) throws -> UInt64 {
guard timestep >= 0 else {
throw Error.timestepInvalid
throw Error.invalidTimestep
}

// Round down and remove the fractional part.
Expand All @@ -84,3 +81,26 @@ extension TimeBasedGenerator {
return UInt64(counter)
}
}

// MARK: - Errors

public extension TimeBasedGenerator {
/// The possible errors thrown in the `TimeBasedGenerator`.
enum Error: Swift.Error, LocalizedError {
/// The provided timestep for OTP generation is not valid.
case invalidTimestep

/// The time for counter computation is invalid.
case invalidTime

public var errorDescription: String? {
switch self {
case .invalidTimestep:
return "The provided timestep for OTP generation is not valid."

case .invalidTime:
return "The time for counter computation is invalid."
}
}
}
}
73 changes: 73 additions & 0 deletions Sources/Uno/OneTimePassword/Algorithm.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// Uno
//
// Created by Gaetano Matonti on 29/08/21.
//

#if canImport(CryptoKit)
import CryptoKit
#else
import Crypto
#endif

import Foundation

public extension OneTimePassword {
/// The hash function used to generate the HMAC.
enum Algorithm: String {
/// The SHA1 hash function. This is the most frequently used albeit insecure.
case sha1 = "SHA1"

/// The SHA256 hash function.
case sha256 = "SHA256"

/// The SHA512 hash function.
case sha512 = "SHA512"

// MARK: - Computed Properties

/// The minimum size of the symmetric key in bytes.
var minimumKeySize: Int {
switch self {
case .sha1:
return 20

case .sha256:
return 32

case .sha512:
return 64
}
}
}
}

// MARK: - Helpers

extension OneTimePassword.Algorithm {
/// Gets the `Algorithm` from its name.
///
/// - Note: If no algorithm could be found for the specified `String`, the default SHA1 algorithm will be used.
/// - Parameter value: The `String` value of the algorithm's name.
/// - Returns: A `Algorithm` used to generate the OTP.
static func from(_ value: String) -> OneTimePassword.Algorithm {
OneTimePassword.Algorithm(rawValue: value) ?? .sha1
}
}

// MARK: - Errors

public extension OneTimePassword.Algorithm {
/// The possible errors regarding the hash functions.
enum Error: Swift.Error, LocalizedError {
/// The minimum size of the symmetric key does not match the requirement.
case invalidMinimumKeySize

public var errorDescription: String? {
switch self {
case .invalidMinimumKeySize:
return "The minimum size of the symmetric key does not match the requirement."
}
}
}
}
Loading

0 comments on commit e419310

Please sign in to comment.