164 lines
4.8 KiB
Swift
164 lines
4.8 KiB
Swift
//
|
|
// X509Certificate.swift
|
|
// SwiftyRSA
|
|
//
|
|
// Created by Stchepinsky Nathan on 24/06/2021.
|
|
// Copyright © 2021 Scoop. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Encoding/Decoding lengths as octets
|
|
private extension NSInteger {
|
|
func encodedOctets() -> [CUnsignedChar] {
|
|
// Short form
|
|
if self < 128 {
|
|
return [CUnsignedChar(self)]
|
|
}
|
|
|
|
// Long form
|
|
let long = Int(log2(Double(self)) / 8 + 1)
|
|
var len = self
|
|
var result: [CUnsignedChar] = [CUnsignedChar(long + 0x80)]
|
|
|
|
for _ in 0..<long {
|
|
result.insert(CUnsignedChar(len & 0xFF), at: 1)
|
|
len = len >> 8
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
init?(octetBytes: [CUnsignedChar], startIdx: inout NSInteger) {
|
|
if octetBytes[startIdx] < 128 {
|
|
// Short form
|
|
self.init(octetBytes[startIdx])
|
|
startIdx += 1
|
|
} else {
|
|
// Long form
|
|
let octets = NSInteger(octetBytes[startIdx] as UInt8 - 128)
|
|
|
|
if octets > octetBytes.count - startIdx {
|
|
self.init(0)
|
|
return nil
|
|
}
|
|
|
|
var result = UInt64(0)
|
|
|
|
for octet in 1...octets {
|
|
result = (result << 8)
|
|
result = result + UInt64(octetBytes[startIdx + octet])
|
|
}
|
|
|
|
startIdx += 1 + octets
|
|
self.init(result)
|
|
}
|
|
}
|
|
}
|
|
|
|
public extension Data {
|
|
// This code source come from Heimdall project https://github.com/henrinormak/Heimdall published under MIT Licence
|
|
|
|
/// This method prepend the X509 header to a given public key
|
|
func prependx509Header() -> Data {
|
|
let result = NSMutableData()
|
|
|
|
let encodingLength: Int = (self.count + 1).encodedOctets().count
|
|
let OID: [CUnsignedChar] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
|
|
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00]
|
|
|
|
var builder: [CUnsignedChar] = []
|
|
|
|
// ASN.1 SEQUENCE
|
|
builder.append(0x30)
|
|
|
|
// Overall size, made of OID + bitstring encoding + actual key
|
|
let size = OID.count + 2 + encodingLength + self.count
|
|
let encodedSize = size.encodedOctets()
|
|
builder.append(contentsOf: encodedSize)
|
|
result.append(builder, length: builder.count)
|
|
result.append(OID, length: OID.count)
|
|
builder.removeAll(keepingCapacity: false)
|
|
|
|
builder.append(0x03)
|
|
builder.append(contentsOf: (self.count + 1).encodedOctets())
|
|
builder.append(0x00)
|
|
result.append(builder, length: builder.count)
|
|
|
|
// Actual key bytes
|
|
result.append(self)
|
|
|
|
return result as Data
|
|
}
|
|
|
|
func hasX509Header() throws -> Bool {
|
|
let node: Asn1Parser.Node
|
|
do {
|
|
node = try Asn1Parser.parse(data: self)
|
|
} catch {
|
|
throw SwiftyRSAError.asn1ParsingFailed
|
|
}
|
|
|
|
// Ensure the raw data is an ASN1 sequence
|
|
guard case .sequence(let nodes) = node else {
|
|
return false
|
|
}
|
|
|
|
// Must contain 2 elements, a sequence and a bit string
|
|
if nodes.count != 2 {
|
|
return false
|
|
}
|
|
|
|
// Ensure the first node is an ASN1 sequence
|
|
guard case .sequence(let firstNode) = nodes[0] else {
|
|
return false
|
|
}
|
|
|
|
// Must contain 2 elements, an object id and NULL
|
|
if firstNode.count != 2 {
|
|
return false
|
|
}
|
|
|
|
guard case .objectIdentifier(_) = firstNode[0] else {
|
|
return false
|
|
}
|
|
|
|
guard case .null = firstNode[1] else {
|
|
return false
|
|
}
|
|
|
|
// The 2sd child has to be a bit string containing a sequence of 2 int
|
|
let last = nodes[1]
|
|
if case .bitString(let secondChildSequence) = last {
|
|
return try secondChildSequence.isAnHeaderlessKey()
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
func isAnHeaderlessKey() throws -> Bool {
|
|
let node: Asn1Parser.Node
|
|
do {
|
|
node = try Asn1Parser.parse(data: self)
|
|
} catch {
|
|
throw SwiftyRSAError.asn1ParsingFailed
|
|
}
|
|
|
|
// Ensure the raw data is an ASN1 sequence
|
|
guard case .sequence(let nodes) = node else {
|
|
return false
|
|
}
|
|
|
|
// Detect whether the sequence only has integers, in which case it's a headerless key
|
|
let onlyHasIntegers = nodes.filter { node -> Bool in
|
|
if case .integer = node {
|
|
return false
|
|
}
|
|
return true
|
|
}.isEmpty
|
|
|
|
// Headerless key
|
|
return onlyHasIntegers
|
|
}
|
|
}
|