File: ASN1BitString.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (129 lines) | stat: -rw-r--r-- 4,705 bytes parent folder | download
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
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftASN1 open source project
//
// Copyright (c) 2019-2020 Apple Inc. and the SwiftASN1 project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftASN1 project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

/// A bitstring is a representation of a sequence of bits.
///
/// In ASN.1, bitstrings serve two different use-cases. The first is as arbitrary-length sequences of bits.
/// These are typically used to encode cryptographic keys, such as RSA keys. The second is as a form of bitset.
///
/// In the case of a bitset, DER has additional requirements as to how to represent the object. This type does not
/// enforce those additional rules: users are expected to implement that validation themselves.
public struct ASN1BitString: DERImplicitlyTaggable {
    /// The default identifier for this type.
    ///
    /// Evaluates to ``ASN1Identifier/bitString``.
    @inlinable
    public static var defaultIdentifier: ASN1Identifier {
        .bitString
    }

    /// The raw bytes that make up this bitstring.
    ///
    /// The last ``paddingBits`` number of bits in the final octet of this byte sequence must be zero.
    public var bytes: ArraySlice<UInt8> {
        didSet {
            try! self._validate()
        }
    }

    /// The number of bits in the last octet of ``bytes`` that are not part of this bitstring.
    ///
    /// The excluded bits are the least significant bits.
    ///
    /// If ``bytes`` is empty then this value must be 0.
    public var paddingBits: Int {
        didSet {
            precondition((0..<8).contains(self.paddingBits))
            try! self._validate()
        }
    }

    @inlinable
    public init(derEncoded node: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
        guard node.identifier == identifier else {
            throw ASN1Error.unexpectedFieldType(node.identifier)
        }

        guard case .primitive(let content) = node.content else {
            preconditionFailure("ASN.1 parser generated primitive node with constructed content")
        }

        // The initial octet explains how many of the bits in the _final_ octet are not part of the bitstring.
        guard let paddingBits = content.first, (0..<8).contains(paddingBits) else {
            throw ASN1Error.invalidASN1Object(
                reason: "Unable to determine a valid number of padding bits for ASN1BitString"
            )
        }

        self.paddingBits = Int(paddingBits)
        self.bytes = content.dropFirst()

        try self._validate()
    }

    /// Construct an ``ASN1BitString`` from raw components.
    ///
    /// - parameters:
    ///     - bytes: The bytes to represent this bitstring
    ///     - paddingBits: The number of bits in the trailing byte that are not actually part of this bitstring.
    @inlinable
    public init(bytes: ArraySlice<UInt8>, paddingBits: Int = 0) {
        self.bytes = bytes
        self.paddingBits = paddingBits
        try! self._validate()
    }

    @inlinable
    public func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws {
        coder.appendPrimitiveNode(identifier: identifier) { bytes in
            bytes.append(UInt8(truncatingIfNeeded: self.paddingBits))
            bytes.append(contentsOf: self.bytes)
        }
    }

    @inlinable
    internal func _validate() throws {
        guard let finalByte = self.bytes.last else {
            if self.paddingBits != 0 {
                // If there are no bytes, there must be no padding bits.
                throw ASN1Error.invalidASN1Object(
                    reason: "Invalid number of padding bits for ASN1BitString: \(self.paddingBits)"
                )
            }

            return
        }

        // Oooh, bit twiddling.
        //
        // All joking aside, this sets the bottom `self.paddingBits` to 1.
        let mask = ~(UInt8.max << self.paddingBits)
        if (finalByte & mask) != 0 {
            throw ASN1Error.invalidASN1Object(
                reason: "Invalid padding bits in ASN1BitString: \(self.paddingBits) of padding, \(finalByte) final byte"
            )
        }
    }
}

extension ASN1BitString: Hashable {}

extension ASN1BitString: Sendable {}

extension ASN1BitString {
    @inlinable
    public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
        return try self.bytes.withUnsafeBytes(body)
    }
}