File: ManifestSignatureParser.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 (164 lines) | stat: -rw-r--r-- 7,798 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
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
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Basics
import struct Foundation.Data

public enum ManifestSignatureParser {
    public static func parse(manifestPath: AbsolutePath, fileSystem: FileSystem) throws -> ManifestSignature? {
        let manifestContents: String
        do {
            manifestContents = try fileSystem.readFileContents(manifestPath)
        } catch {
            throw Error.inaccessibleManifest(path: manifestPath, reason: String(describing: error))
        }
        return try self.parse(utf8String: manifestContents)
    }

    public static func parse(utf8String: String) throws -> ManifestSignature? {
        let manifestComponents = Self.split(utf8String)

        guard let signatureComponents = manifestComponents.signatureComponents else {
            return .none
        }

        guard let signature = Data(base64Encoded: String(signatureComponents.signatureBase64Encoded)) else {
            throw Error.malformedManifestSignature
        }

        return ManifestSignature(
            contents: Array(String(manifestComponents.contentsBeforeSignatureComponents).utf8),
            signatureFormat: String(signatureComponents.signatureFormat),
            signature: Array(signature)
        )
    }

    /// Splits the given manifest into its constituent components.
    ///
    /// A **signed** manifest consists of the following parts:
    ///
    ///                                                    ⎫
    ///                                                    ┇
    ///                                                    ⎬ manifest's contents (returned by this function)
    ///                                                    ┇
    ///                                                    ⎭
    ///       ┌ manifest signature
    ///       ⌄~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ///       //  signature:  cms-1.0.0;MIIFujCCBKKgAw...  } the manifest signature line
    ///     ⌃~⌃~⌃~⌃~~~~~~~~~⌃~⌃~~~~~~~~^^~~~~~~~~~~~~~~~~
    ///     | | | |         | |        |└ signature base64-encoded (returned by this function)
    ///     | │ │ └ label   │ |        └ signature format terminator
    ///     | | |           | └ signature format (returned by this function)
    ///     | │ └ spacing   └ spacing
    ///     | └ comment marker
    ///     └ additional leading whitespace
    ///
    /// - Parameter manifest: The UTF-8-encoded content of the manifest.
    /// - Returns: The components of the given manifest.
    private static func split(_ manifest: String) -> ManifestComponents {
        // The signature, if any, is the last line in the manifest.
        let endIndexOfSignatureLine = manifest.lastIndex(where: { !$0.isWhitespace }) ?? manifest.endIndex
        let endIndexOfManifestContents = manifest[..<endIndexOfSignatureLine]
            .lastIndex(where: { $0.isNewline }) ?? manifest.endIndex
        let startIndexOfCommentMarker = manifest[endIndexOfManifestContents...]
            .firstIndex(where: { $0 == "/" }) ?? manifest.endIndex

        // There doesn't seem to be a signature, return manifest as-is.
        guard startIndexOfCommentMarker < endIndexOfSignatureLine else {
            return ManifestComponents(contentsBeforeSignatureComponents: manifest[...], signatureComponents: .none)
        }

        let endIndexOfCommentMarker = manifest[startIndexOfCommentMarker...]
            .firstIndex(where: { $0 != "/" }) ?? manifest.endIndex

        let startIndexOfLabel = manifest[endIndexOfCommentMarker...].firstIndex(where: { !$0.isWhitespace }) ?? manifest
            .endIndex
        let endIndexOfLabel = manifest[startIndexOfLabel...].firstIndex(where: { $0 == ":" }) ?? manifest.endIndex

        // Missing "signature:" label, assume there is no signature.
        guard startIndexOfLabel < endIndexOfLabel,
              String(manifest[startIndexOfLabel ..< endIndexOfLabel]).lowercased() == "signature"
        else {
            return ManifestComponents(contentsBeforeSignatureComponents: manifest[...], signatureComponents: .none)
        }

        let startIndexOfSignatureFormat = manifest[endIndexOfLabel...]
            .firstIndex(where: { $0 != ":" && !$0.isWhitespace }) ?? manifest.endIndex
        let endIndexOfSignatureFormat = manifest[startIndexOfSignatureFormat...]
            .firstIndex(where: { $0 == ";" }) ?? manifest.endIndex

        // Missing signature format, assume there is no signature.
        guard startIndexOfSignatureFormat < endIndexOfSignatureFormat else {
            return ManifestComponents(contentsBeforeSignatureComponents: manifest[...], signatureComponents: .none)
        }

        let startIndexOfSignatureBase64Encoded = manifest[endIndexOfSignatureFormat...]
            .firstIndex(where: { $0 != ";" }) ?? manifest.endIndex

        // Missing base64-encoded signature, assume there is no signature.
        guard startIndexOfSignatureBase64Encoded < endIndexOfSignatureLine else {
            return ManifestComponents(contentsBeforeSignatureComponents: manifest[...], signatureComponents: .none)
        }

        return ManifestComponents(
            contentsBeforeSignatureComponents: manifest[..<endIndexOfManifestContents],
            signatureComponents: SignatureComponents(
                signatureFormat: manifest[startIndexOfSignatureFormat ..< endIndexOfSignatureFormat],
                signatureBase64Encoded: manifest[startIndexOfSignatureBase64Encoded ... endIndexOfSignatureLine]
            )
        )
    }

    public struct ManifestSignature {
        public let contents: [UInt8]
        public let signatureFormat: String
        public let signature: [UInt8]
    }

    public enum Error: Swift.Error {
        /// Package manifest file is inaccessible (missing, unreadable, etc).
        case inaccessibleManifest(path: AbsolutePath, reason: String)
        /// Malformed manifest signature.
        case malformedManifestSignature
    }
}

extension ManifestSignatureParser {
    /// A representation of a manifest in its constituent parts.
    public struct ManifestComponents {
        /// The contents of the manifest up to the signature line.
        /// A manifest doesn't have to be signed so this can be the entire manifest contents.
        public let contentsBeforeSignatureComponents: Substring
        /// The manifest signature (if any) represented in its constituent parts.
        public let signatureComponents: SignatureComponents?
    }

    /// A representation of manifest signature in its constituent parts.
    ///
    /// A manifest signature consists of the following parts:
    ///
    ///     //  signature:  cms-1.0.0;MIIFujCCBKKgAwIBAgIBATANBgkqhkiG9w0BAQUFAD...
    ///     ⌃~⌃~⌃~~~~~~~~~⌃~⌃~~~~~~~~^^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ///     | | |         | |        |└ signature base64-encoded
    ///     │ │ └ label   │ |        └ signature format terminator
    ///     | |           | └ signature format
    ///     │ └ spacing   └ spacing
    ///     └ comment marker
    ///
    public struct SignatureComponents {
        /// The signature format.
        public let signatureFormat: Substring

        /// The base64-encoded signature.
        public let signatureBase64Encoded: Substring
    }
}