File: CertificateSerialNumber.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 (130 lines) | stat: -rw-r--r-- 5,226 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
130
//===----------------------------------------------------------------------===//
//
// 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

extension Certificate {
    /// A number that uniquely identifies a certificate issued by a specific
    /// certificate authority.
    ///
    /// Serial numbers are often extremely large (up to 20 bytes), so they cannot typically
    /// be represented using a fixed-width integer type. This type therefore represents the
    /// raw big-endian bytes of the serial number, suitable for loading into a more specific
    /// type.
    public struct SerialNumber {
        /// The raw big-endian bytes of the serial number.
        public var bytes: ArraySlice<UInt8>

        /// Construct a serial number from its raw big-endian bytes.
        /// - Parameter bytes: The raw big-endian bytes of the serial number.
        @inlinable
        public init(bytes: ArraySlice<UInt8>) {
            self.bytes = ArraySlice(normalisingToASN1IntegerForm: bytes)
        }

        /// Construct a serial number from its raw big-endian bytes.
        /// - Parameter bytes: The raw big-endian bytes of the serial number.
        @inlinable
        public init(bytes: [UInt8]) {
            self.bytes = ArraySlice(normalisingToASN1IntegerForm: bytes[...])
        }

        /// Construct a serial number from its raw big-endian bytes.
        /// - Parameter bytes: The raw big-endian bytes of the serial number.
        @inlinable
        public init<Bytes: Collection>(bytes: Bytes) where Bytes.Element == UInt8 {
            self.bytes = ArraySlice(normalisingToASN1IntegerForm: bytes)
        }

        /// Construct a serial number from a fixed width integer.
        ///
        /// In general this API should only be used for testing, as fixed width integers
        /// are not sufficiently large for use in certificates. Using this API for production
        /// use-cases may expose users to hash collision attacks on generated certificates.
        ///
        /// Prefer using ``Certificate/SerialNumber-swift.struct/init(integerLiteral:)``
        /// with a `StaticBigInt` which enables arbitrary-precision.
        ///
        /// - Parameter bytes: The raw big-endian bytes of the serial number.
        @inlinable
        public init<Number: FixedWidthInteger>(_ number: Number) {
            // `IntegerBytesCollection` already trims leading zeros
            self.bytes = ArraySlice(IntegerBytesCollection(number))
        }

        /// Construct a random 20-byte serial number.
        ///
        /// Serial numbers should be generated randomly, and may contain up to 20 bytes. This
        /// initializer generates an appropriate serial number.
        @inlinable
        public init() {
            var rng = SystemRandomNumberGenerator()
            self.init(generator: &rng)
        }

        /// Construct a random 20-byte serial number.
        ///
        /// Serial numbers should be generated randomly, and may contain up to 20 bytes. This
        /// initializer generates a serial number with random numbers from the given `generator`.
        /// - Parameter generator: the generator used to generate random number for the serial number
        @inlinable
        internal init(generator: inout some RandomNumberGenerator) {
            // drop leading zeros as required by the ASN.1 spec for INTEGERs
            self.bytes = ArraySlice(normalisingToASN1IntegerForm: generator.bytes(count: 20))
        }
    }
}

extension Certificate.SerialNumber: Hashable {}

extension Certificate.SerialNumber: Sendable {}

extension Certificate.SerialNumber: CustomStringConvertible {
    public var description: String {
        return self.bytes.lazy.map { String($0, radix: 16) }.joined(separator: ":")
    }
}

#if swift(>=5.8)
@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *)
extension Certificate.SerialNumber: ExpressibleByIntegerLiteral {
    /// Constructs a serial number from an integer.
    ///
    /// - Parameter integerLiteral: The raw big-endian bytes of the serial number.
    @inlinable
    public init(integerLiteral number: StaticBigInt) {
        var bytes = [UInt8]()
        let wordCount = (number.bitWidth - 1) / (MemoryLayout<UInt>.size * 8) + 1
        bytes.reserveCapacity(wordCount / MemoryLayout<UInt>.size)

        for wordIndex in (0..<wordCount).reversed() {
            bytes.appendBigEndianBytes(number[wordIndex])
        }

        self.bytes = ArraySlice(normalisingToASN1IntegerForm: bytes)
    }
}
#endif

extension [UInt8] {
    @inlinable
    mutating func appendBigEndianBytes(_ number: UInt) {
        let number = number.bigEndian

        for byte in 0..<(MemoryLayout<UInt>.size) {
            let shifted = number >> (byte * 8)
            self.append(UInt8(truncatingIfNeeded: shifted))
        }
    }
}