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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftCertificates open source project
//
// Copyright (c) 2022 Apple Inc. and the SwiftCertificates project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftCertificates project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import SwiftASN1
/// A distinguished name is a name that uniquely identifies a specific entity.
///
/// Distinguished names are a remnant of the X.500 directory system. In that system,
/// distinguished names were the primary key, enabling the identification of a specific entity
/// within the directory.
///
/// These use-cases are largely obsolete, but distinguished names continue to be used to identify
/// both the subject of and issuer of a given X.509 certificate. In this context, the distinguished
/// name is a largely opaque identifier that just happens to have a human-readable string representation.
///
/// The structure of a distinguished name reflects its X.500 roots. A distinguished name is conceptually
/// an ordered sequence of ``RelativeDistinguishedName``s. This sequence is conceptually ordered by hierarchy, from least
/// to most specific. Each ``RelativeDistinguishedName`` contains a collection of ``RelativeDistinguishedName/Attribute``s
/// that are intended to be equivalent representations of the same idea. In common usage, each ``RelativeDistinguishedName``
/// contains a single ``RelativeDistinguishedName/Attribute``.
///
/// As an example, the ``DistinguishedName`` that represents the Apple-operated intermediate certificate authority
/// "Apple Public EV Server RSA CA 2 - G1" is:
///
/// ```swift
/// try DistinguishedName([
/// RelativeDistinguishedName([
/// RelativeDistinguishedName.Attribute(type: .RDNAttributeType.countryName, printableString: "US"),
/// ]),
/// RelativeDistinguishedName([
/// RelativeDistinguishedName.Attribute(type: .RDNAttributeType.organizationName, printableString: "Apple Inc."),
/// ]),
/// RelativeDistinguishedName([
/// RelativeDistinguishedName.Attribute(type: .RDNAttributeType.commonName, printableString: "Apple Public EV Server ECC CA 1 - G1"),
/// ]),
/// ])
/// ```
///
/// The ``DistinguishedName`` type models this in its full complexity.
///
/// ``DistinguishedName`` is a collection of ``RelativeDistinguishedName``, making it easy to perform generic computations
/// across the ``RelativeDistinguishedName``s that make up a full ``DistinguishedName``.
///
/// To make working with ``DistinguishedName`` easier, users have a number of convenience APIs for creating the most common
/// kinds of ``DistinguishedName``. In addition to the example above (using ``init(_:)-3no37``), users can also create distinguished
/// names that follow the "one ``RelativeDistinguishedName/Attribute`` per ``RelativeDistinguishedName``" pattern by passing
/// a sequence of ``RelativeDistinguishedName/Attribute`` directly, which will be wrapped into ``RelativeDistinguishedName``
/// objects. For example, the above ``DistinguishedName`` can also be represented by:
///
/// ```swift
/// try DistinguishedName([
/// RelativeDistinguishedName.Attribute(type: .RDNAttributeType.countryName, printableString: "US"),
/// RelativeDistinguishedName.Attribute(type: .RDNAttributeType.organizationName, printableString: "Apple Inc."),
/// RelativeDistinguishedName.Attribute(type: .RDNAttributeType.commonName, printableString: "Apple Public EV Server ECC CA 1 - G1"),
/// ])
/// ```
///
/// This produces an identical ``DistinguishedName`` to the prior example.
///
/// Additionally, users can take advantage of ``DistinguishedNameBuilder`` to use a result builder DSL to construct ``DistinguishedName`` objects.
/// The above distinguished name can further be represented as:
///
/// ```swift
/// let name = try DistinguishedName {
/// CountryName("US")
/// OrganizationName("Apple Inc.")
/// CommonName("Apple Public EV Server ECC CA 1 - G1")
/// }
/// ```
///
/// This convenient shorthand is particularly valuable in testing, as well as in code that needs to generate certificates or CSRs.
public struct DistinguishedName {
@usableFromInline
var rdns: [RelativeDistinguishedName]
/// Construct a ``DistinguishedName`` from a sequence of ``RelativeDistinguishedName``.
///
/// - Parameter rdns: The elements of this ``DistinguishedName``.
@inlinable
public init<RDNSequence: Sequence>(_ rdns: RDNSequence) where RDNSequence.Element == RelativeDistinguishedName {
self.rdns = Array(rdns)
}
/// Construct a ``DistinguishedName`` from a sequence of ``RelativeDistinguishedName/Attribute``.
///
/// This helper initializer will wrap each ``RelativeDistinguishedName/Attribute`` in a ``RelativeDistinguishedName``
/// transparently.
///
/// - Parameter attributes: The sequence of ``RelativeDistinguishedName/Attribute``s that make up the ``DistinguishedName``.
@inlinable
public init<AttributeSequence: Sequence>(_ attributes: AttributeSequence) throws
where AttributeSequence.Element == RelativeDistinguishedName.Attribute {
self.rdns = attributes.map { RelativeDistinguishedName($0) }
}
/// Construct a new empty ``DistinguishedName``.
@inlinable
public init() {
self.rdns = []
}
/// Construct a ``DistinguishedName`` using a DSL.
///
/// This API uses a result builder DSL to make it easier to construct complex
/// ``DistinguishedName``s. As an example, a ``DistinguishedName`` can be constructed
/// like this:
///
/// ```swift
/// let name = try DistinguishedName {
/// CountryName("US")
/// OrganizationName("Apple Inc.")
/// CommonName("Apple Public EV Server ECC CA 1 - G1")
/// }
/// ```
///
/// - Parameter builder: The ``DistinguishedNameBuilder`` block.
@inlinable
public init(@DistinguishedNameBuilder builder: () throws -> Result<DistinguishedName, any Error>) throws {
self = try builder().get()
}
}
extension DistinguishedName: Hashable {}
extension DistinguishedName: Sendable {}
extension DistinguishedName: RandomAccessCollection, MutableCollection, RangeReplaceableCollection {
@inlinable
public var startIndex: Int {
self.rdns.startIndex
}
@inlinable
public var endIndex: Int {
self.rdns.endIndex
}
@inlinable
public subscript(position: Int) -> RelativeDistinguishedName {
get {
self.rdns[position]
}
set {
self.rdns[position] = newValue
}
}
@inlinable
public mutating func replaceSubrange<NewElements>(_ subrange: Range<Int>, with newElements: NewElements)
where NewElements: Collection, RelativeDistinguishedName == NewElements.Element {
self.rdns.replaceSubrange(subrange, with: newElements)
}
}
extension DistinguishedName: CustomStringConvertible {
@inlinable
public var description: String {
self.reversed().lazy.map { String(describing: $0) }.joined(separator: ",")
}
}
extension DistinguishedName: CustomDebugStringConvertible {
public var debugDescription: String {
String(reflecting: String(describing: self))
}
}
extension DistinguishedName: DERImplicitlyTaggable {
@inlinable
public static var defaultIdentifier: ASN1Identifier {
.sequence
}
@inlinable
public init(derEncoded rootNode: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
self.rdns = try DER.sequence(of: RelativeDistinguishedName.self, identifier: identifier, rootNode: rootNode)
}
@inlinable
public func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws {
try coder.appendConstructedNode(identifier: identifier) { rootCoder in
for element in self.rdns {
try element.serialize(into: &rootCoder)
}
}
}
}
|