From 2c20c9d41800b26d41bcb11297c36c81c5501f57 Mon Sep 17 00:00:00 2001 From: Kevin Wooten Date: Sun, 15 Jan 2023 22:44:09 -0700 Subject: [PATCH] Update ASN1 docs --- Sources/PotentASN1/ASN1.swift | 14 +++++----- Sources/PotentASN1/ASN1Decoder.swift | 38 +++++++++++++++++++++++++++- Sources/PotentASN1/ASN1Encoder.swift | 33 +++++++++++++++++++++++- Sources/PotentASN1/Schema.swift | 35 +++++++++++++++++++++++-- 4 files changed, 109 insertions(+), 11 deletions(-) diff --git a/Sources/PotentASN1/ASN1.swift b/Sources/PotentASN1/ASN1.swift index dc02f819c..bfc54f5ec 100644 --- a/Sources/PotentASN1/ASN1.swift +++ b/Sources/PotentASN1/ASN1.swift @@ -300,13 +300,13 @@ public extension ASN1 { return value } - /// Values as ``BigInt`` or `nil` if this is not an ``integer(_:)``. - var integerValue: BigInt? { + /// Values as ``ASN1/Integer`` or `nil` if this is not an ``integer(_:)``. + var integerValue: ASN1.Integer? { guard case .integer(let value) = absolute else { return nil } return value } - /// Values as ``BitString`` or `nil` if this is not a ``bitString(_:)``. + /// Values as ``BitString`` or `nil` if this is not a ``bitString(_:_:)``. var bitStringValue: BitString? { guard case .bitString(let length, let bytes) = absolute else { return nil } return BitString(length: length, bytes: bytes) @@ -318,7 +318,7 @@ public extension ASN1 { return value } - /// Values as ``[UInt64]`` or `nil` if this is not an ``objectIdentifier(_:)``. + /// Values as `[UInt64]` or `nil` if this is not an ``objectIdentifier(_:)``. var objectIdentifierValue: ObjectIdentifier? { guard case .objectIdentifier(let value) = absolute else { return nil } return ObjectIdentifier(value) @@ -378,13 +378,13 @@ public extension ASN1 { return AnyString(value, kind: .ia5) } - /// Value as ``AnyTime `` or `nil` if this is not a ``utcTime(_:)``. + /// Value as ``AnyTime`` or `nil` if this is not a ``utcTime(_:)``. var utcTimeValue: AnyTime? { guard case .utcTime(let value) = absolute else { return nil } return AnyTime(value, kind: .utc) } - /// Value as ``AnyTime `` or `nil` if this is not a ``generalizedTime(_:)``. + /// Value as ``AnyTime`` or `nil` if this is not a ``generalizedTime(_:)``. var generalizedTimeValue: AnyTime? { guard case .generalizedTime(let value) = absolute else { return nil } return AnyTime(value, kind: .generalized) @@ -454,7 +454,7 @@ public extension ASN1 { } } - /// Value as array of ``ASN`` or `nil` if this is not a ``sequence(_:)`` or ``set(_:)``. + /// Value as array of ``ASN1`` or `nil` if this is not a ``sequence(_:)`` or ``set(_:)``. var collectionValue: [ASN1]? { switch absolute { case .set(let value): return value diff --git a/Sources/PotentASN1/ASN1Decoder.swift b/Sources/PotentASN1/ASN1Decoder.swift index e70dc1e36..9430a3887 100644 --- a/Sources/PotentASN1/ASN1Decoder.swift +++ b/Sources/PotentASN1/ASN1Decoder.swift @@ -12,7 +12,25 @@ import Foundation import PotentCodables -/// Decoding of ASN.1 values into `Decodable` types. +/// Decodes `Decodable` types from ASN.1/DER data or ``ASN1`` value trees using a ``Schema``. +/// +/// When decoding a `Decodable` type from ASN.1, the ``ASN1Decoder`` is initialized with a +/// ``Schema`` to direct decoding. +/// +/// Decode an example `TBSCertificate` type with an associated `TBSCertificateSchema` as follows: +/// ```swift +/// ASN1Decoder(schema: TBSCertificateSchema) +/// .decode(TBSCertificate.self, from: asn1Data) +/// ``` +/// +/// See ``Schema`` to learn about defining ASN.1 schemas. +/// +/// If the example `TBSCertificate` adopts the ``SchemaSpecified`` protocol, static utility +/// functions can be used to decode types without having to initialize the decoder with a +/// schema. This also ensures the correct schema is always provided to the decoder. +/// ```swift +/// ASN1Decoder.decode(TBSCertificate.self, from: asn1Data) +/// ``` /// public class ASN1Decoder: ValueDecoder, DecodesFromData { @@ -27,15 +45,33 @@ public class ASN1Decoder: ValueDecoder, DecodesFromD ) } + /// Initialize decoder with a specified ``schema``. + /// + /// - Parameter schema: Schema to use for decoding. + /// public required init(schema: Schema) { self.schema = schema super.init() } + /// Decode a `Decodable` & ``SchemaSpecified`` type from ASN.1/DER encoded data. + /// + /// - Parameters: + /// - type: Type of value to decode. + /// - data: ASN.1/DER encoded data. + /// - Returns: Decoded value of `type`. + /// - Throws: public static func decode(_ type: T.Type, from data: Data) throws -> T { return try Self(schema: T.asn1Schema).decode(type, from: data) } + /// Decode a `Decodable` & ``SchemaSpecified`` type from an ``ASN1`` value tree. + /// + /// - Parameters: + /// - type: Type of value to decode. + /// - asn1: ASN1 value tree. + /// - Returns: Decoded value of `type`. + /// public static func decodeTree(_ type: T.Type, from asn1: ASN1) throws -> T { return try Self(schema: T.asn1Schema).decodeTree(type, from: asn1) } diff --git a/Sources/PotentASN1/ASN1Encoder.swift b/Sources/PotentASN1/ASN1Encoder.swift index 81b707921..ca2028568 100644 --- a/Sources/PotentASN1/ASN1Encoder.swift +++ b/Sources/PotentASN1/ASN1Encoder.swift @@ -12,7 +12,25 @@ import Foundation import PotentCodables -/// Encoding of `Encodable` types into ``ASN1`` values. +/// Encodes `Encodable` types into ASN.1/DER data or ``ASN1`` value trees using a ``Schema``. +/// +/// When encoding an `Encodable` value to ASN.1, the ``ASN1Encoder`` is initialized with a +/// ``Schema`` to direct encoding. +/// +/// Encode an example `TBSCertificate` type with an associated `TBSCertificateSchema` as follows: +/// ```swift +/// ASNEncoder(schema: TBSCertificateSchema) +/// .encode(someCertificate) +/// ``` +/// +/// See ``Schema`` to learn about defining ASN.1 schemas. +/// +/// If the example `TBSCertificate` adopts the ``SchemaSpecified`` protocol, static utility +/// functions can be used to encode values without having to initialize the encoder with a +/// schema. This also ensures the correct schema is always provided to the encoder. +/// ```swift +/// ASN1Encoder.encode(someCertificate) +/// ``` /// public class ASN1Encoder: ValueEncoder, EncodesToData { @@ -28,15 +46,28 @@ public class ASN1Encoder: ValueEncoder, EncodesToDat ) } + /// Initialize encoder with a specified ``Schema``. + /// + /// - Parameters schema: public required init(schema: Schema) { self.schema = schema super.init() } + /// Encode an `Encodeable` & ``SchemaSpecified`` value to ASN.1/DER encoded data. + /// + /// - Parameters value: Value to encode. + /// - Throws: `DecodingError` or ``ASN1DERWriter/Error``. + /// public static func encode(_ value: T) throws -> Data { return try Self(schema: T.asn1Schema).encode(value) } + /// Encode an `Encodeable` & ``SchemaSpecified`` value to an ``ASN1`` value tree. + /// + /// - Parameters value: Value to encode. + /// - Throws: `DecodingError` or ``ASN1DERWriter/Error``. + /// public static func encodeTree(_ value: T) throws -> ASN1 { return try Self(schema: T.asn1Schema).encodeTree(value) } diff --git a/Sources/PotentASN1/Schema.swift b/Sources/PotentASN1/Schema.swift index bd68947c9..4d2962def 100644 --- a/Sources/PotentASN1/Schema.swift +++ b/Sources/PotentASN1/Schema.swift @@ -14,8 +14,39 @@ import OrderedCollections import PotentCodables -/// ASN.1 schema specification. -/// +/// ASN.1 schema specification DSL. +/// +/// The most common usage of ASN.1 encoding and decoding requires following a schema. +/// ``Schema`` provides a simple DSL to specify ASN.1 schemas that provides the same +/// capabilities as the official ASN.1 syntax. +/// +/// For example, the following is the ``Schema`` for RFC-5280's `TBSCertificate`: +/// ```swift +/// let TBSCertificateSchema: Schema = +/// .sequence([ +/// "version": .version(.explicit(0, Version)), +/// "serialNumber": CertificateSerialNumber, +/// "signature": AlgorithmIdentifier(SignatureAlgorithms), +/// "issuer": Name, +/// "validity": Validity, +/// "subject": Name, +/// "subjectPublicKeyInfo": SubjectPublicKeyInfo, +/// "issuerUniqueID": .versioned(range: 1...2, .implicit(1, UniqueIdentifier)), +/// "subjectUniqueID": .versioned(range: 1...2, .implicit(2, UniqueIdentifier)), +/// "extensions": .versioned(range: 2...2, .explicit(3, Extensions)) +/// ]) +/// ``` +/// +/// When encoding an `Encodable` type to ASN.1, the ``ASN1Encoder`` is initialized with a +/// ``Schema`` to direct its or encoding. Likewise, when decoding a `Decodable` type from ASN.1 +/// the ``ASN1Decoder`` is initialized with a ``Schema``. +/// +/// Decode an example `TBSCertificate` type using the above schema as follows: +/// ```swift +/// ASNDecoder(schema: TBSCertificateSchema) +/// .decode(TBSCertificate.self, from: data) +/// ``` +/// public indirect enum Schema { public typealias DynamicMap = [ASN1: Schema]