杨锴
2024-08-14 909e20941e45f8712c012db602034b47da0bfdb0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//
//  CryptoSwift
//
//  Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
//  This software is provided 'as-is', without any express or implied warranty.
//
//  In no event will the authors be held liable for any damages arising from the use of this software.
//
//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
//
//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
//  - This notice may not be removed or altered from any source or binary distribution.
//
 
public extension PKCS5 {
  /// A key derivation function.
  ///
  /// PBKDF1 is recommended only for compatibility with existing
  /// applications since the keys it produces may not be large enough for
  /// some applications.
  struct PBKDF1 {
    public enum Error: Swift.Error {
      case invalidInput
      case derivedKeyTooLong
    }
 
    public enum Variant {
      case md5, sha1
 
      @usableFromInline
      var size: Int {
        switch self {
          case .md5:
            return MD5.digestLength
          case .sha1:
            return SHA1.digestLength
        }
      }
 
      @usableFromInline
      func calculateHash(_ bytes: Array<UInt8>) -> Array<UInt8> {
        switch self {
          case .sha1:
            return Digest.sha1(bytes)
          case .md5:
            return Digest.md5(bytes)
        }
      }
    }
 
    @usableFromInline
    let iterations: Int // c
 
    @usableFromInline
    let variant: Variant
 
    @usableFromInline
    let keyLength: Int
 
    @usableFromInline
    let t1: Array<UInt8>
 
    /// - parameters:
    ///   - salt: salt, an eight-bytes
    ///   - variant: hash variant
    ///   - iterations: iteration count, a positive integer
    ///   - keyLength: intended length of derived key
    public init(password: Array<UInt8>, salt: Array<UInt8>, variant: Variant = .sha1, iterations: Int = 4096 /* c */, keyLength: Int? = nil /* dkLen */ ) throws {
      precondition(iterations > 0)
      precondition(salt.count == 8)
 
      let keyLength = keyLength ?? variant.size
 
      if keyLength > variant.size {
        throw Error.derivedKeyTooLong
      }
 
      let t1 = variant.calculateHash(password + salt)
 
      self.iterations = iterations
      self.variant = variant
      self.keyLength = keyLength
      self.t1 = t1
    }
 
    /// Apply the underlying hash function Hash for c iterations
    @inlinable
    public func calculate() -> Array<UInt8> {
      var t = self.t1
      for _ in 2...self.iterations {
        t = self.variant.calculateHash(t)
      }
      return Array(t[0..<self.keyLength])
    }
 
    public func callAsFunction() -> Array<UInt8> {
      calculate()
    }
  }
}