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 199 200 201 202 203
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftCertificates open source project
//
// Copyright (c) 2022-2023 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
import _CertificateInternals
/// A ``RelativeDistinguishedName`` is a collection of elements at a single level of a hierarchical
/// ``DistinguishedName``.
///
/// 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.
///
/// ``RelativeDistinguishedName``s are the elements of a ``DistinguishedName``. Each ``RelativeDistinguishedName``
/// contains one or more ``RelativeDistinguishedName/Attribute`` which are considered equivalent: that is, they
/// are each a representation of the same piece of information.
///
/// ``RelativeDistinguishedName``s are organised into a hierarchy in a ``DistinguishedName``, in order from least
/// to most specific. In almost all current use-cases a ``RelativeDistinguishedName`` will contain only a single
/// ``Attribute``.
///
/// Note that ``RelativeDistinguishedName`` does not have a stable ordering of its elements. Inserting an element
/// at index `i` does not guarantee it will remain at that location. As a result, ``RelativeDistinguishedName`` is
/// not a `MutableCollection`.
public struct RelativeDistinguishedName {
@usableFromInline
var attributes: _TinyArray<Attribute>
/// Construct a ``RelativeDistinguishedName`` from a sequence of ``Attribute``.
///
/// - Parameter attributes: The sequence of ``Attribute``s that make up the ``DistinguishedName``.
@inlinable
public init<AttributeSequence: Sequence>(_ attributes: AttributeSequence)
where AttributeSequence.Element == RelativeDistinguishedName.Attribute {
self.attributes = .init(attributes)
Self._sortElements(&self.attributes)
}
/// Construct a ``RelativeDistinguishedName`` from a sequence of ``Attribute``.
///
/// - Parameter attributes: The sequence of ``Attribute``s that make up the ``DistinguishedName``.
@inlinable
public init(_ attribute: Attribute) {
self.init(CollectionOfOne(attribute))
}
@inlinable
init(_ attributes: DER.LazySetOfSequence<Attribute>) throws {
self.attributes = try .init(attributes)
Self._sortElements(&self.attributes)
}
/// Create an empty ``RelativeDistinguishedName``.
@inlinable
public init() {
self.attributes = .init()
}
}
extension RelativeDistinguishedName: Hashable {}
extension RelativeDistinguishedName: Sendable {}
extension RelativeDistinguishedName: RandomAccessCollection {
@inlinable
public var startIndex: Int {
self.attributes.startIndex
}
@inlinable
public var endIndex: Int {
self.attributes.endIndex
}
@inlinable
public subscript(position: Int) -> RelativeDistinguishedName.Attribute {
get {
self.attributes[position]
}
}
/// Insert a new ``Attribute`` into this ``RelativeDistinguishedName``.
///
/// - Parameter attribute: The ``Attribute`` to insert.
@inlinable
public mutating func insert(_ attribute: RelativeDistinguishedName.Attribute) {
self.attributes.append(attribute)
Self._sortElements(&self.attributes)
}
/// Insert a `Collection` of ``Attribute``s into this ``RelativeDistinguishedName``.
///
/// Note that the order of `attributes` will not be preserved.
///
/// - Parameter attributes: The ``Attribute``s to be inserted.
@inlinable
public mutating func insert<Attributes: Collection>(contentsOf attributes: Attributes)
where Attributes.Element == RelativeDistinguishedName.Attribute {
self.attributes.append(contentsOf: attributes)
Self._sortElements(&self.attributes)
}
/// Removes and returns the ``Attribute`` at the specified position.
///
/// - Parameter index: The position of the ``Attribute`` to remove.
/// - Returns: The ``Attribute`` at the specified index.
@inlinable
@discardableResult
public mutating func remove(at index: Int) -> Element {
self.attributes.remove(at: index)
// removing an element doesn't change the order and therefore sorting is not required
}
/// Removes all the ``Attribute``s that satisfy the given predicate.
/// - Parameter shouldBeRemoved: A closure that takes an ``Attribute`` of the
/// ``RelativeDistinguishedName`` as its argument and returns a Boolean value indicating
/// whether the ``Attribute`` should be removed from the ``RelativeDistinguishedName``.
@inlinable
public mutating func removeAll(where shouldBeRemoved: (Attribute) throws -> Bool) rethrows {
try self.attributes.removeAll(where: shouldBeRemoved)
// removing elements doesn't change the order and therefore sorting is not required
}
}
extension RelativeDistinguishedName: CustomStringConvertible {
@inlinable
public var description: String {
self.lazy.map {
String(describing: $0)
}.joined(separator: "+")
}
}
extension RelativeDistinguishedName: CustomDebugStringConvertible {
public var debugDescription: String {
String(reflecting: String(describing: self))
}
}
extension RelativeDistinguishedName: DERImplicitlyTaggable {
@inlinable
public static var defaultIdentifier: ASN1Identifier {
.set
}
@inlinable
public init(derEncoded rootNode: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
try self.init(DER.lazySet(identifier: identifier, rootNode: rootNode))
}
@inlinable
public func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws {
try coder.serializeSetOf(self.attributes, identifier: identifier)
}
@inlinable
static func _sortElements(_ elements: inout _TinyArray<RelativeDistinguishedName.Attribute>) {
// We keep the elements sorted at all times. This is dumb, but we assume that these objects get
// mutated infrequently.
// This is weird. We need to individually serialize each element, then lexicographically compare
// them and then write them out. We could do this in place but for now let's not worry about it.
try! elements.sort { lhs, rhs in
var serializer = DER.Serializer()
try serializer.serialize(lhs)
let lhsBytes = serializer.serializedBytes
serializer = DER.Serializer()
try serializer.serialize(rhs)
let rhsBytes = serializer.serializedBytes
// Compare up to the common length lexicographically.
for (leftByte, rightByte) in zip(lhsBytes, rhsBytes) {
if leftByte < rightByte {
// true means left comes before right
return true
} else if rightByte < leftByte {
// Right comes after left
return false
}
}
// We got to the end of the shorter element, so all current elements are equal.
// If lhs is shorter, it comes earlier, _unless_ all of rhs's trailing elements are zero.
let trailing = rhsBytes.dropFirst(lhsBytes.count)
if trailing.count == 0 || trailing.allSatisfy({ $0 == 0 }) {
// Must return false when the two elements are equal.
return false
}
return true
}
}
}
|