Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ASN1ObjectIdentifiers internal representation be the encoded form #36

Merged
merged 4 commits into from
Aug 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 52 additions & 19 deletions Sources/SwiftASN1/Basic ASN1 Types/ObjectIdentifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,39 @@ public struct ASN1ObjectIdentifier: DERImplicitlyTaggable {
}

@usableFromInline
var _oidComponents: [UInt]
var bytes: ArraySlice<UInt8>

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

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

try Self.validateObjectIdentifierInEncodedForm(content)

self.bytes = content
}

@inlinable
static func validateObjectIdentifierInEncodedForm(_ content: ArraySlice<UInt8>) throws {
var content = content

guard content.count >= 1 else {
throw ASN1Error.invalidASN1Object(reason: "Zero components in OID")
}

while content.count > 0 {
_ = try content.readUIntUsing8BitBytesASN1Discipline()
}
}

@inlinable
var oidComponents: [UInt] {
var content = bytes
// We have to parse the content. From the spec:
//
// > Each subidentifier is represented as a series of (one or more) octets. Bit 8 of each octet indicates whether it
Expand All @@ -68,7 +89,16 @@ public struct ASN1ObjectIdentifier: DERImplicitlyTaggable {
// scheme, likely because I was in middle school at the time.
var subcomponents = [UInt]()
while content.count > 0 {
subcomponents.append(try content.readUIntUsing8BitBytesASN1Discipline())
do {
subcomponents.append(try content.readUIntUsing8BitBytesASN1Discipline())
} catch {
preconditionFailure(
"""
Error while trying to read UInt using 8 bit ASN.1 Discipline: \(error). \
ASN1ObjectIdentifier validates the encoded format during initialisation and this should be impossible.
"""
)
}
}

// Now we need to expand the subcomponents out. This means we need to undo the step above. We can do this by
Expand All @@ -79,31 +109,22 @@ public struct ASN1ObjectIdentifier: DERImplicitlyTaggable {
// We'd like to work on the slice here.
var subcomponentSlice = subcomponents[...]
guard let firstEncodedSubcomponent = subcomponentSlice.popFirst() else {
throw ASN1Error.invalidASN1Object(reason: "Zero components in OID")
preconditionFailure(
"Zero components in OID. ASN1ObjectIdentifier validates the encoded format during initialisation and this should be impossible."
)
}

let (firstSubcomponent, secondSubcomponent) = firstEncodedSubcomponent.quotientAndRemainder(dividingBy: 40)
oidComponents.append(firstSubcomponent)
oidComponents.append(secondSubcomponent)
oidComponents.append(contentsOf: subcomponentSlice)

self._oidComponents = oidComponents
return oidComponents
}

@inlinable
public func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws {
coder.appendPrimitiveNode(identifier: identifier) { bytes in
var components = self._oidComponents[...]
guard let firstComponent = components.popFirst(), let secondComponent = components.popFirst() else {
preconditionFailure("Invalid number of OID components: must be at least two!")
}

let serializedFirstComponent = (firstComponent * 40) + secondComponent
ASN1ObjectIdentifier._writeOIDSubidentifier(serializedFirstComponent, into: &bytes)

while let component = components.popFirst() {
ASN1ObjectIdentifier._writeOIDSubidentifier(component, into: &bytes)
}
bytes.append(contentsOf: self.bytes)
}
}

Expand All @@ -120,14 +141,26 @@ extension ASN1ObjectIdentifier: Sendable {}
extension ASN1ObjectIdentifier: ExpressibleByArrayLiteral {
@inlinable
public init(arrayLiteral elements: UInt...) {
self._oidComponents = elements
var bytes = [UInt8]()
var components = elements[...]
guard let firstComponent = components.popFirst(), let secondComponent = components.popFirst() else {
preconditionFailure("Invalid number of OID components: must be at least two!")
}

let serializedFirstComponent = (firstComponent * 40) + secondComponent
ASN1ObjectIdentifier._writeOIDSubidentifier(serializedFirstComponent, into: &bytes)

while let component = components.popFirst() {
ASN1ObjectIdentifier._writeOIDSubidentifier(component, into: &bytes)
}
self.bytes = bytes[...]
}
}

extension ASN1ObjectIdentifier: CustomStringConvertible {
@inlinable
public var description: String {
self._oidComponents.map { String($0) }.joined(separator: ".")
self.oidComponents.map { String($0) }.joined(separator: ".")
}
}

Expand Down