Skip to content

Commit

Permalink
Make ASN1ObjectIdentifiers internal representation be the encoded f…
Browse files Browse the repository at this point in the history
…orm (#36)

* Make `ASN1ObjectIdentifier`s internal representation be the encoded form

* validate during initialisation

* swift-format

* formatting
  • Loading branch information
dnadoba committed Aug 17, 2023
1 parent 43e0170 commit ce1faa4
Showing 1 changed file with 52 additions and 19 deletions.
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

0 comments on commit ce1faa4

Please sign in to comment.