杨锴
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
//
//  CryptoSwift
//
//  Copyright (C) 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.
//
//  ASN1 Code inspired by Asn1Parser.swift from SwiftyRSA
 
import Foundation
 
/// A Partial ASN.1 (Abstract Syntax Notation 1) Encoder & Decoder Implementation.
///
/// - Note: This implementation is limited to a few core types and is not an exhaustive / complete ASN1 implementation
/// - Warning: This implementation has been developed for encoding and decoding DER & PEM files specifically. If you're using this Encoder/Decoder on other ASN1 structures, make sure you test the expected behavior appropriately.
enum ASN1 {
  internal enum IDENTIFIERS: UInt8, Equatable {
    case SEQUENCE = 0x30
    case INTERGER = 0x02
    case OBJECTID = 0x06
    case NULL = 0x05
    case BITSTRING = 0x03
    case OCTETSTRING = 0x04
 
    static func == (lhs: UInt8, rhs: IDENTIFIERS) -> Bool {
      lhs == rhs.rawValue
    }
 
    var bytes: [UInt8] {
      switch self {
        case .NULL:
          return [self.rawValue, 0x00]
        default:
          return [self.rawValue]
      }
    }
  }
 
  /// An ASN1 node
  internal enum Node: CustomStringConvertible {
    /// An array of more `ASN1.Node`s
    case sequence(nodes: [Node])
    /// An integer
    /// - Note: This ASN1 Encoder makes no assumptions about the sign and bit order of the integers passed in. The conversion from Integer to Data is your responsiblity.
    case integer(data: Data)
    /// An objectIdentifier
    case objectIdentifier(data: Data)
    /// A null object
    case null
    /// A bitString
    case bitString(data: Data)
    /// An octetString
    case octetString(data: Data)
 
    var description: String {
      ASN1.printNode(self, level: 0)
    }
  }
 
  internal static func printNode(_ node: ASN1.Node, level: Int) -> String {
    var str: [String] = []
    let prefix = String(repeating: "\t", count: level)
    switch node {
      case .integer(let int):
        str.append("\(prefix)Integer: \(int.toHexString())")
      case .bitString(let bs):
        str.append("\(prefix)BitString: \(bs.toHexString())")
      case .null:
        str.append("\(prefix)NULL")
      case .objectIdentifier(let oid):
        str.append("\(prefix)ObjectID: \(oid.toHexString())")
      case .octetString(let os):
        str.append("\(prefix)OctetString: \(os.toHexString())")
      case .sequence(let nodes):
        str.append("\(prefix)Sequence:")
        nodes.forEach { str.append(printNode($0, level: level + 1)) }
    }
    return str.joined(separator: "\n")
  }
}