From c2c74e4404192be3275de3099ed050295bfcb6d4 Mon Sep 17 00:00:00 2001 From: Kevin Wooten Date: Fri, 9 Jun 2023 14:40:30 -0700 Subject: [PATCH 1/7] Add OIDs for handling PKCS8 protected and unprotected key formats --- Sources/ShieldOID/ISO-ITU.swift | 150 +++++++++++++++++++++++++++++++- Sources/ShieldOID/ISO.swift | 38 ++++++++ 2 files changed, 187 insertions(+), 1 deletion(-) diff --git a/Sources/ShieldOID/ISO-ITU.swift b/Sources/ShieldOID/ISO-ITU.swift index 79447c49b..2fee95c91 100644 --- a/Sources/ShieldOID/ISO-ITU.swift +++ b/Sources/ShieldOID/ISO-ITU.swift @@ -26,7 +26,7 @@ public struct iso_itu: OIDBranch { public struct ds: OIDBranch { public static let id: UInt64 = 5 public static let names = ["ds"] - internal static let children: [OIDNode.Type] = [attributeType.self, certificateExtension.self] + internal static let children: [OIDNode.Type] = [attributeType.self, certificateExtension.self, algorithm.self] public enum attributeType: OID, CaseIterable, OIDLeaf { public static let id: UInt64 = 4 @@ -193,5 +193,153 @@ public struct iso_itu: OIDBranch { case noRevAvail = "2.5.29.56" case acceptablePrivilegePolicies = "2.5.29.57" } + + public struct algorithm: OIDBranch { + public static let id: UInt64 = 44 + public static let names = ["algorithm"] + internal static let children: [OIDNode.Type] = [aes.self] + + public enum aes: OID, CaseIterable, OIDLeaf { + public static let id: UInt64 = 2 + public static let names = ["aes"] + internal static let children: [OIDNode.Type] = [] + + case aes_cbc_128 = "2.5.44.2.1" + case aes_cbc_192 = "2.5.44.2.2" + case aes_cbc_256 = "2.5.44.2.3" + + case aes_ofb_128 = "2.5.44.2.5" + case aes_ofb_192 = "2.5.44.2.6" + case aes_ofb_256 = "2.5.44.2.7" + + case aes_cfb_128 = "2.5.44.2.9" + case aes_cfb_192 = "2.5.44.2.10" + case aes_cfb_256 = "2.5.44.2.11" + + case aes_gcm_128 = "2.5.44.2.17" + case aes_gcm_192 = "2.5.44.2.18" + case aes_gcm_256 = "2.5.44.2.19" + + case aes_gcm_siv_128 = "2.5.44.2.21" + case aes_gcm_siv_192 = "2.5.44.2.22" + case aes_gcm_siv_256 = "2.5.44.2.23" + + case aes_ccm_128 = "2.5.44.2.25" + case aes_ccm_192 = "2.5.44.2.26" + case aes_ccm_256 = "2.5.44.2.27" + + case aes_gmac_128 = "2.5.44.2.29" + case aes_gmac_192 = "2.5.44.2.30" + case aes_gmac_256 = "2.5.44.2.31" + } + } + } + + public struct country: OIDBranch { + public static let id: UInt64 = 16 + public static let names = ["country"] + internal static let children: [OIDNode.Type] = [us.self] + + public struct us: OIDBranch { + public static let id: UInt64 = 840 + public static let names = ["us"] + internal static let children: [OIDNode.Type] = [organization.self] + + public struct organization: OIDBranch { + public static let id: UInt64 = 1 + public static let names = ["organization"] + internal static let children: [OIDNode.Type] = [gov.self] + + public struct gov: OIDBranch { + public static let id: UInt64 = 101 + public static let names = ["gov"] + internal static let children: [OIDNode.Type] = [csor.self] + + public struct csor: OIDBranch { + public static let id: UInt64 = 3 + public static let names = ["country"] + internal static let children: [OIDNode.Type] = [nistAlgorithms.self] + + public struct nistAlgorithms: OIDBranch { + public static let id: UInt64 = 4 + public static let names = ["nistAlgorithms"] + internal static let children: [OIDNode.Type] = [aes.self, hashAlgs.self] + + public enum aes: OID, CaseIterable, OIDLeaf { + public static let id: UInt64 = 1 + public static let names = ["aes"] + internal static let children: [OIDNode.Type] = [] + + case aes128_ECB = "2.16.840.1.101.3.4.1.1" + case aes128_CBC_PAD = "2.16.840.1.101.3.4.1.2" + case aes128_OFB = "2.16.840.1.101.3.4.1.3" + case aes128_CFB = "2.16.840.1.101.3.4.1.4" + case aes128_wrap = "2.16.840.1.101.3.4.1.5" + case aes128_GCM = "2.16.840.1.101.3.4.1.6" + case aes128_CCM = "2.16.840.1.101.3.4.1.7" + case aes128_wrap_pad = "2.16.840.1.101.3.4.1.8" + case aes128_GMAC = "2.16.840.1.101.3.4.1.9" + + case aes192_ECB = "2.16.840.1.101.3.4.1.21" + case aes192_CBC_PAD = "2.16.840.1.101.3.4.1.22" + case aes192_OFB = "2.16.840.1.101.3.4.1.23" + case aes192_CFB = "2.16.840.1.101.3.4.1.24" + case aes192_wrap = "2.16.840.1.101.3.4.1.25" + case aes192_GCM = "2.16.840.1.101.3.4.1.26" + case aes192_CCM = "2.16.840.1.101.3.4.1.27" + case aes192_wrap_pad = "2.16.840.1.101.3.4.1.28" + case aes192_GMAC = "2.16.840.1.101.3.4.1.29" + + case aes256_ECB = "2.16.840.1.101.3.4.1.41" + case aes256_CBC_PAD = "2.16.840.1.101.3.4.1.42" + case aes256_OFB = "2.16.840.1.101.3.4.1.43" + case aes256_CFB = "2.16.840.1.101.3.4.1.44" + case aes256_wrap = "2.16.840.1.101.3.4.1.45" + case aes256_GCM = "2.16.840.1.101.3.4.1.46" + case aes256_CCM = "2.16.840.1.101.3.4.1.47" + case aes256_wrap_pad = "2.16.840.1.101.3.4.1.48" + case aes256_GMAC = "2.16.840.1.101.3.4.1.49" + } + + public enum hashAlgs: OID, CaseIterable, OIDLeaf { + public static let id: UInt64 = 2 + public static let names = ["hashAlgs", "hashalgs"] + internal static let children: [OIDNode.Type] = [] + + case sha256 = "2.16.840.1.101.3.4.2.1" + case sha384 = "2.16.840.1.101.3.4.2.2" + case sha512 = "2.16.840.1.101.3.4.2.3" + case sha224 = "2.16.840.1.101.3.4.2.4" + case sha512_224 = "2.16.840.1.101.3.4.2.5" + case sha512_256 = "2.16.840.1.101.3.4.2.6" + + case sha3_224 = "2.16.840.1.101.3.4.2.7" + case sha3_256 = "2.16.840.1.101.3.4.2.8" + case sha3_384 = "2.16.840.1.101.3.4.2.9" + case sha3_512 = "2.16.840.1.101.3.4.2.10" + + case shake128 = "2.16.840.1.101.3.4.2.11" + case shake256 = "2.16.840.1.101.3.4.2.12" + + case hmacWithSHA3_224 = "2.16.840.1.101.3.4.2.13" + case hmacWithSHA3_256 = "2.16.840.1.101.3.4.2.14" + case hmacWithSHA3_384 = "2.16.840.1.101.3.4.2.15" + case hmacWithSHA3_512 = "2.16.840.1.101.3.4.2.16" + + case shake128_len = "2.16.840.1.101.3.4.2.17" + case shake256_len = "2.16.840.1.101.3.4.2.18" + + + case kmac128 = "2.16.840.1.101.3.4.2.19" + case kmac256 = "2.16.840.1.101.3.4.2.20" + + case KMACXOF128 = "2.16.840.1.101.3.4.2.21" + case KACXOF256 = "2.16.840.1.101.3.4.2.22" + } + } + } + } + } + } } } diff --git a/Sources/ShieldOID/ISO.swift b/Sources/ShieldOID/ISO.swift index f73feb0e6..9fd4751d1 100644 --- a/Sources/ShieldOID/ISO.swift +++ b/Sources/ShieldOID/ISO.swift @@ -58,6 +58,14 @@ public struct iso: OIDBranch { case sha224WithRSAEncryption = "1.2.840.113549.1.1.14" } + public enum pkcs5: OID, CaseIterable, OIDLeaf { + public static let id: UInt64 = 5 + public static let names = ["pkcs-5"] + + case pbkdf2 = "1.2.840.113549.1.5.12" + case pbes2 = "1.2.840.113549.1.5.13" + } + public enum pkcs9: OID, CaseIterable, OIDLeaf { public static let id: UInt64 = 9 public static let names = ["pkcs-9"] @@ -74,6 +82,36 @@ public struct iso: OIDBranch { case extensionRequest = "1.2.840.113549.1.9.14" } } + + public enum digestAlgorithm: OID, CaseIterable, OIDLeaf { + public static let id: UInt64 = 2 + public static let names = ["digestAlgorithm"] + + case hmacWithSHA1 = "1.2.840.113549.2.7" + case hmacWithSHA224 = "1.2.840.113549.2.8" + case hmacWithSHA256 = "1.2.840.113549.2.9" + case hmacWithSHA384 = "1.2.840.113549.2.10" + case hmacWithSHA512 = "1.2.840.113549.2.11" + case hhmacWithSHA512_224 = "1.2.840.113549.2.12" + case hhmacWithSHA512_256 = "1.2.840.113549.2.13" + } + + public enum encryptionAlgorithm: OID, CaseIterable, OIDLeaf { + public static let id: UInt64 = 3 + public static let names = ["encryptionAlgorithm", "encryptionalgorithm"] + + case rc2CBC = "1.2.840.113549.3.2" + case rc2ECB = "1.2.840.113549.3.3" + case rc4 = "1.2.840.113549.3.4" + case rc4WithMAC = "1.2.840.113549.3.5" + case desxCBC = "1.2.840.113549.3.6" + case desEDE3CBC = "1.2.840.113549.3.7" + case rc5CBC = "1.2.840.113549.3.8" + case rc5CBCPad = "1.2.840.113549.3.9" + case desCDMF = "1.2.840.113549.3.10" + case desEDE3 = "1.2.840.113549.3.17" + } + } public struct ansix962: OIDBranch { From 473c33591d7b17d9eabe49724f97fbbad4052fd1 Mon Sep 17 00:00:00 2001 From: Kevin Wooten Date: Fri, 9 Jun 2023 14:43:16 -0700 Subject: [PATCH 2/7] Rename PBKDF psuduo random algorithms to include hmac and deprectate old --- Sources/ShieldCrypto/PBKDF.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Sources/ShieldCrypto/PBKDF.swift b/Sources/ShieldCrypto/PBKDF.swift index d1e6aaf31..6986f63fa 100644 --- a/Sources/ShieldCrypto/PBKDF.swift +++ b/Sources/ShieldCrypto/PBKDF.swift @@ -45,13 +45,23 @@ public enum PBKDF { self.name = name } + @available(*, deprecated, message: "Use hmacSha1") public static let sha1 = PsuedoRandomAlgorithm(rawValue: UInt32(kCCPRFHmacAlgSHA1), name: "SHA1") + public static let hmacSha1 = PsuedoRandomAlgorithm(rawValue: UInt32(kCCPRFHmacAlgSHA1), name: "SHA1") + @available(*, deprecated, message: "Use hmacSha224") public static let sha224 = PsuedoRandomAlgorithm(rawValue: UInt32(kCCPRFHmacAlgSHA224), name: "SHA224") + public static let hmacSha224 = PsuedoRandomAlgorithm(rawValue: UInt32(kCCPRFHmacAlgSHA224), name: "SHA224") + @available(*, deprecated, message: "Use hmacSha256") public static let sha256 = PsuedoRandomAlgorithm(rawValue: UInt32(kCCPRFHmacAlgSHA256), name: "SHA256") + public static let hmacSha256 = PsuedoRandomAlgorithm(rawValue: UInt32(kCCPRFHmacAlgSHA256), name: "SHA256") + @available(*, deprecated, message: "Use hmacSha384") public static let sha384 = PsuedoRandomAlgorithm(rawValue: UInt32(kCCPRFHmacAlgSHA384), name: "SHA384") + public static let hmacSha384 = PsuedoRandomAlgorithm(rawValue: UInt32(kCCPRFHmacAlgSHA384), name: "SHA384") + @available(*, deprecated, message: "Use hmacSha512") public static let sha512 = PsuedoRandomAlgorithm(rawValue: UInt32(kCCPRFHmacAlgSHA512), name: "SHA512") + public static let hmacSha512 = PsuedoRandomAlgorithm(rawValue: UInt32(kCCPRFHmacAlgSHA512), name: "SHA512") - public static let allCases: [PsuedoRandomAlgorithm] = [.sha1, .sha224, .sha256, .sha384, .sha512] + public static let allCases: [PsuedoRandomAlgorithm] = [.hmacSha1, .hmacSha224, .hmacSha256, .hmacSha384, .hmacSha512] public var description: String { return name @@ -97,7 +107,7 @@ public enum PBKDF { saltLength: Int, keyLength: Int, using algorithm: Algorithm = .pbkdf2, - psuedoRandomAlgorithm: PsuedoRandomAlgorithm = .sha512, + psuedoRandomAlgorithm: PsuedoRandomAlgorithm = .hmacSha512, taking: TimeInterval ) throws -> Int { let rounds = CCCalibratePBKDF( From fbb9d3579ae32f3e4b7cbf49c49812edd46a1879 Mon Sep 17 00:00:00 2001 From: Kevin Wooten Date: Fri, 9 Jun 2023 15:42:04 -0700 Subject: [PATCH 3/7] Add types for PKCS8 protected and unprotected key handling with ASN.1 schemas Also moves types from ShieldPKCS into ShieldX509 to stop circular library references --- Package.swift | 4 +- Sources/ShieldPKCS/Moved.swift | 23 +++++ .../ECParameters.swift | 0 Sources/ShieldX509/ECPrivateKey.swift | 49 ++++++++++ .../ShieldX509/EncryptedPrivateKeyInfo.swift | 43 +++++++++ Sources/ShieldX509/PBES2Params.swift | 89 +++++++++++++++++++ Sources/ShieldX509/PBKDF2Params.swift | 54 +++++++++++ Sources/ShieldX509/PrivateKeyInfo.swift | 50 +++++++++++ .../RSAPrivateKey.swift | 24 +++++ .../RSAPublicKey.swift | 0 Sources/ShieldX509/TBSCertificate.swift | 3 +- 11 files changed, 335 insertions(+), 4 deletions(-) create mode 100644 Sources/ShieldPKCS/Moved.swift rename Sources/{ShieldPKCS => ShieldX509}/ECParameters.swift (100%) create mode 100644 Sources/ShieldX509/ECPrivateKey.swift create mode 100644 Sources/ShieldX509/EncryptedPrivateKeyInfo.swift create mode 100644 Sources/ShieldX509/PBES2Params.swift create mode 100644 Sources/ShieldX509/PBKDF2Params.swift create mode 100644 Sources/ShieldX509/PrivateKeyInfo.swift rename Sources/{ShieldPKCS => ShieldX509}/RSAPrivateKey.swift (73%) rename Sources/{ShieldPKCS => ShieldX509}/RSAPublicKey.swift (100%) diff --git a/Package.swift b/Package.swift index ee83f6044..6e141b21d 100644 --- a/Package.swift +++ b/Package.swift @@ -37,11 +37,11 @@ let package = Package( ), .target( name: "ShieldPKCS", - dependencies: ["ShieldX500", "PotentCodables"] + dependencies: ["ShieldX509", "PotentCodables"] ), .target( name: "ShieldX509", - dependencies: ["ShieldCrypto", "ShieldX500", "ShieldOID", "ShieldPKCS", "PotentCodables"] + dependencies: ["ShieldCrypto", "ShieldX500", "ShieldOID", "PotentCodables", "Algorithms"] ), .target( name: "ShieldCrypto" diff --git a/Sources/ShieldPKCS/Moved.swift b/Sources/ShieldPKCS/Moved.swift new file mode 100644 index 000000000..e52f917e1 --- /dev/null +++ b/Sources/ShieldPKCS/Moved.swift @@ -0,0 +1,23 @@ +// +// Moved.swift +// +// +// Created by Kevin Wooten on 6/8/23. +// + +import ShieldX509 +import PotentASN1 + +// The following types have been moved to ShieldX509 due to issues with circular references + +public typealias RSAPrivateKey = ShieldX509.RSAPrivateKey +public typealias RSAPublicKey = ShieldX509.RSAPublicKey +public typealias ECParameters = ShieldX509.ECParameters + +public extension Schemas { + static let RSAPrivateKey = ShieldX509.Schemas.RSAPrivateKey + static let RSAPrivateKeyOtherPrimeInfos = ShieldX509.Schemas.RSAPrivateKeyOtherPrimeInfos + static let RSAPrivateKeyOtherPrimeInfo = ShieldX509.Schemas.RSAPrivateKeyOtherPrimeInfo + static let RSAPublicKey = ShieldX509.Schemas.RSAPublicKey + static let ECParameters = ShieldX509.Schemas.ECParameters +} diff --git a/Sources/ShieldPKCS/ECParameters.swift b/Sources/ShieldX509/ECParameters.swift similarity index 100% rename from Sources/ShieldPKCS/ECParameters.swift rename to Sources/ShieldX509/ECParameters.swift diff --git a/Sources/ShieldX509/ECPrivateKey.swift b/Sources/ShieldX509/ECPrivateKey.swift new file mode 100644 index 000000000..c3a35c1d2 --- /dev/null +++ b/Sources/ShieldX509/ECPrivateKey.swift @@ -0,0 +1,49 @@ +// +// ECPrivateKey.swift +// +// +// Created by Kevin Wooten on 6/8/23. +// + +import Foundation +import PotentASN1 + + +public struct ECPrivateKey: Equatable, Hashable, Codable { + + public enum Version: Int, CaseIterable, Equatable, Hashable, Codable { + case one = 1 + } + + public var version: Version + public var privateKey: Data + public var parameters: ECParameters? + public var publicKey: BitString? + + public init(version: Version = .one, privateKey: Data, parameters: ECParameters? = nil, publicKey: BitString? = nil) { + self.version = version + self.privateKey = privateKey + self.parameters = parameters + self.publicKey = publicKey + } + +} + +extension ECPrivateKey: SchemaSpecified { + + public static var asn1Schema: Schema { Schemas.ECPrivateKey } + +} + +public extension Schemas { + + static let ECPrivateKey: Schema = + .sequence([ + "version": .version(.integer(allowed: 1 ..< 2)), + "privateKey": .octetString(), + "parameters": .optional(.explicit(0, ECParameters)), + "publicKey": .optional(.explicit(1, .bitString())), + ]) + +} + diff --git a/Sources/ShieldX509/EncryptedPrivateKeyInfo.swift b/Sources/ShieldX509/EncryptedPrivateKeyInfo.swift new file mode 100644 index 000000000..a7ecb7faf --- /dev/null +++ b/Sources/ShieldX509/EncryptedPrivateKeyInfo.swift @@ -0,0 +1,43 @@ +// +// EncryptedPrivateKeyInfo.swift +// +// +// Created by Kevin Wooten on 6/8/23. +// + +import Foundation +import PotentASN1 +import ShieldOID + +public struct EncryptedPrivateKeyInfo: Equatable, Hashable, Codable { + + public var encryptionAlgorithm: AlgorithmIdentifier + public var encryptedData: Data + + public init(encryptionAlgorithm: AlgorithmIdentifier, encryptedData: Data) { + self.encryptionAlgorithm = encryptionAlgorithm + self.encryptedData = encryptedData + } + +} + +extension EncryptedPrivateKeyInfo: SchemaSpecified { + + public static var asn1Schema: Schema { Schemas.EncryptedPrivateKeyInfo } + +} + +public extension Schemas { + + static let EncryptedPrivateKeyInfoAlgorithms: Schema.DynamicMap = [ + iso.memberBody.us.rsadsi.pkcs.pkcs1.rsaEncryption.asn1: .null, + iso.memberBody.us.ansix962.keyType.ecPublicKey.asn1: ECParameters, + ] + + static let EncryptedPrivateKeyInfo: Schema = + .sequence([ + "encryptionAlgorithm": algorithmIdentifier(EncryptedPrivateKeyInfoAlgorithms), + "encryptedData": .octetString() + ]) + +} diff --git a/Sources/ShieldX509/PBES2Params.swift b/Sources/ShieldX509/PBES2Params.swift new file mode 100644 index 000000000..7421a3ba3 --- /dev/null +++ b/Sources/ShieldX509/PBES2Params.swift @@ -0,0 +1,89 @@ +// +// PBES.swift +// +// +// Created by Kevin Wooten on 6/8/23. +// + +import BigInt +import Foundation +import PotentASN1 +import ShieldOID + +public struct PBES2Params: Equatable, Hashable, Codable { + + public var keyDerivationFunc: AlgorithmIdentifier + public var encryptionScheme: AlgorithmIdentifier + + public init(keyDerivationFunc: AlgorithmIdentifier, encryptionScheme: AlgorithmIdentifier) { + self.keyDerivationFunc = keyDerivationFunc + self.encryptionScheme = encryptionScheme + } +} + +extension PBES2Params: SchemaSpecified { + + public static var asn1Schema: Schema { Schemas.PBES2Params } + +} + +public extension Schemas { + + private typealias rsadisDigAlg = iso.memberBody.us.rsadsi.digestAlgorithm + private typealias rsadisEncAlg = iso.memberBody.us.rsadsi.encryptionAlgorithm + private typealias nistAlgs = iso_itu.country.us.organization.gov.csor.nistAlgorithms.aes + + static let PBES2ParamsKeyDerivationFuncAlgorithms: Schema.DynamicMap = [ + iso.memberBody.us.rsadsi.pkcs.pkcs5.pbkdf2.asn1: PBKDF2Params + ] + + static let PBES2ParamsEncryptionSchemeAlgorithms: Schema.DynamicMap = [ + rsadisEncAlg.rc2CBC.asn1: .null, + rsadisEncAlg.rc2ECB.asn1: .null, + rsadisEncAlg.rc4.asn1: .null, + rsadisEncAlg.rc4WithMAC.asn1: .null, + rsadisEncAlg.desxCBC.asn1: .null, + rsadisEncAlg.desEDE3CBC.asn1: .null, + rsadisEncAlg.rc5CBC.asn1: .null, + rsadisEncAlg.rc5CBCPad.asn1: .null, + rsadisEncAlg.desCDMF.asn1: .null, + rsadisEncAlg.desEDE3.asn1: .null, + + nistAlgs.aes128_ECB.asn1: .null, + nistAlgs.aes128_CBC_PAD.asn1: .null, + nistAlgs.aes128_OFB.asn1: .null, + nistAlgs.aes128_CFB.asn1: .null, + nistAlgs.aes128_wrap.asn1: .null, + nistAlgs.aes128_GCM.asn1: .null, + nistAlgs.aes128_CCM.asn1: .null, + nistAlgs.aes128_wrap_pad.asn1: .null, + nistAlgs.aes128_GMAC.asn1: .null, + + nistAlgs.aes192_ECB.asn1: .null, + nistAlgs.aes192_CBC_PAD.asn1: .null, + nistAlgs.aes192_OFB.asn1: .null, + nistAlgs.aes192_CFB.asn1: .null, + nistAlgs.aes192_wrap.asn1: .null, + nistAlgs.aes192_GCM.asn1: .null, + nistAlgs.aes192_CCM.asn1: .null, + nistAlgs.aes192_wrap_pad.asn1: .null, + nistAlgs.aes192_GMAC.asn1: .null, + + nistAlgs.aes256_ECB.asn1: .null, + nistAlgs.aes256_CBC_PAD.asn1: .null, + nistAlgs.aes256_OFB.asn1: .null, + nistAlgs.aes256_CFB.asn1: .null, + nistAlgs.aes256_wrap.asn1: .null, + nistAlgs.aes256_GCM.asn1: .null, + nistAlgs.aes256_CCM.asn1: .null, + nistAlgs.aes256_wrap_pad.asn1: .null, + nistAlgs.aes256_GMAC.asn1: .null, + ] + + static let PBES2Params: Schema = + .sequence([ + "keyDerivationFunc": algorithmIdentifier(PBES2ParamsKeyDerivationFuncAlgorithms), + "encryptionScheme": algorithmIdentifier(PBES2ParamsEncryptionSchemeAlgorithms) + ]) + +} diff --git a/Sources/ShieldX509/PBKDF2Params.swift b/Sources/ShieldX509/PBKDF2Params.swift new file mode 100644 index 000000000..41c1e0d8d --- /dev/null +++ b/Sources/ShieldX509/PBKDF2Params.swift @@ -0,0 +1,54 @@ +// +// PBKDF2Params.swift +// +// +// Created by Kevin Wooten on 6/8/23. +// + +import Foundation +import PotentASN1 +import ShieldOID + +public struct PBKDF2Params: Equatable, Hashable, Codable { + + public var salt: Data + public var iterationCount: UInt64 + public var keyLength: UInt64 + public var prf: AlgorithmIdentifier + + public init(salt: Data, iterationCount: UInt64, keyLength: UInt64, prf: AlgorithmIdentifier) { + self.salt = salt + self.iterationCount = iterationCount + self.keyLength = keyLength + self.prf = prf + } + +} + +extension PBKDF2Params: SchemaSpecified { + + public static var asn1Schema: Schema { Schemas.PBKDF2Params } + +} + +public extension Schemas { + + private typealias digAlgs = iso.memberBody.us.rsadsi.digestAlgorithm + + private static let PRFAglorithms: Schema.DynamicMap = [ + digAlgs.hmacWithSHA1.asn1: .null, + digAlgs.hmacWithSHA224.asn1: .null, + digAlgs.hmacWithSHA256.asn1: .null, + digAlgs.hmacWithSHA384.asn1: .null, + digAlgs.hmacWithSHA512.asn1: .null, + ] + + static let PBKDF2Params: Schema = + .sequence([ + "salt": .choiceOf([.octetString(), .objectIdentifier()]), + "iterationCount": .integer(), + "keyLength": .optional(.integer()), + "prf": algorithmIdentifier(PRFAglorithms) + ]) + +} diff --git a/Sources/ShieldX509/PrivateKeyInfo.swift b/Sources/ShieldX509/PrivateKeyInfo.swift new file mode 100644 index 000000000..ef3f71578 --- /dev/null +++ b/Sources/ShieldX509/PrivateKeyInfo.swift @@ -0,0 +1,50 @@ +// +// PrivateKeyInfo.swift +// +// +// Created by Kevin Wooten on 6/7/23. +// + +import Foundation +import ShieldOID +import PotentASN1 + +public struct PrivateKeyInfo: Equatable, Hashable, Codable { + + public enum Version: Int, CaseIterable, Equatable, Hashable, Codable { + case zero = 0 + } + + public var version: Version + public var privateKeyAlgorithm: AlgorithmIdentifier + public var privateKey: Data + + public init(version: Version = .zero, privateKeyAlgorithm: AlgorithmIdentifier, privateKey: Data) { + self.version = version + self.privateKeyAlgorithm = privateKeyAlgorithm + self.privateKey = privateKey + } + +} + +extension PrivateKeyInfo: SchemaSpecified { + public static var asn1Schema: Schema { Schemas.PrivateKeyInfo } +} + +public extension Schemas { + + static let PrivateKeyInfoAlgorithms: Schema.DynamicMap = [ + iso.memberBody.us.rsadsi.pkcs.pkcs1.rsaEncryption.asn1: .null, + iso.memberBody.us.ansix962.keyType.ecPublicKey.asn1: ECParameters, + ] + + static let PrivateKeyInfoVersion: Schema = .integer(allowed: 0 ..< 1) + + static let PrivateKeyInfo: Schema = + .sequence([ + "version": .version(PrivateKeyInfoVersion), + "privateKeyAlgorithm": algorithmIdentifier(PrivateKeyInfoAlgorithms), + "privateKey": .octetString(), + ]) + +} diff --git a/Sources/ShieldPKCS/RSAPrivateKey.swift b/Sources/ShieldX509/RSAPrivateKey.swift similarity index 73% rename from Sources/ShieldPKCS/RSAPrivateKey.swift rename to Sources/ShieldX509/RSAPrivateKey.swift index 03677ce18..b2b56f353 100644 --- a/Sources/ShieldPKCS/RSAPrivateKey.swift +++ b/Sources/ShieldX509/RSAPrivateKey.swift @@ -35,6 +35,30 @@ public struct RSAPrivateKey: Equatable, Hashable, Codable { public var exponent2: ASN1.Integer public var coefficient: ASN1.Integer public var otherPrimeInfos: [OtherPrimeInfo]? + + public init( + version: Version, + modulus: ASN1.Integer, + publicExponent: ASN1.Integer, + privateExponent: ASN1.Integer, + prime1: ASN1.Integer, + prime2: ASN1.Integer, + exponent1: ASN1.Integer, + exponent2: ASN1.Integer, + coefficient: ASN1.Integer, + otherPrimeInfos: [OtherPrimeInfo]? = nil + ) { + self.version = version + self.modulus = modulus + self.publicExponent = publicExponent + self.privateExponent = privateExponent + self.prime1 = prime1 + self.prime2 = prime2 + self.exponent1 = exponent1 + self.exponent2 = exponent2 + self.coefficient = coefficient + self.otherPrimeInfos = otherPrimeInfos + } } diff --git a/Sources/ShieldPKCS/RSAPublicKey.swift b/Sources/ShieldX509/RSAPublicKey.swift similarity index 100% rename from Sources/ShieldPKCS/RSAPublicKey.swift rename to Sources/ShieldX509/RSAPublicKey.swift diff --git a/Sources/ShieldX509/TBSCertificate.swift b/Sources/ShieldX509/TBSCertificate.swift index 1324753a1..db43895c2 100644 --- a/Sources/ShieldX509/TBSCertificate.swift +++ b/Sources/ShieldX509/TBSCertificate.swift @@ -11,7 +11,6 @@ import Foundation import PotentASN1 import ShieldOID -import ShieldPKCS import ShieldX500 @@ -91,7 +90,7 @@ public extension Schemas { static let PKInfoAlgorithms: Schema.DynamicMap = [ iso.memberBody.us.rsadsi.pkcs.pkcs1.rsaEncryption.asn1: .null, - iso.memberBody.us.ansix962.keyType.ecPublicKey.asn1: ShieldPKCS.Schemas.ECParameters, + iso.memberBody.us.ansix962.keyType.ecPublicKey.asn1: ECParameters, ] static let SignatureAlgorithms: Schema.DynamicMap = [ From ea25547d9644abc6274f01596cf99db0dc1cf2b2 Mon Sep 17 00:00:00 2001 From: Kevin Wooten Date: Fri, 9 Jun 2023 15:45:02 -0700 Subject: [PATCH 4/7] Switch `SecKeyPair` `import`/`export` to use standard PKCS#8 encrypted format --- Package.resolved | 9 + Package.swift | 1 + Sources/ShieldSecurity/SecKeyPair.swift | 353 ++++++++++++++++++++---- 3 files changed, 311 insertions(+), 52 deletions(-) diff --git a/Package.resolved b/Package.resolved index 8486532ba..10a34e90b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -37,6 +37,15 @@ "version": "2.1.1" } }, + { + "package": "swift-algorithms", + "repositoryURL": "https://github.com/apple/swift-algorithms", + "state": { + "branch": null, + "revision": "b14b7f4c528c942f121c8b860b9410b2bf57825e", + "version": "1.0.0" + } + }, { "package": "swift-collections", "repositoryURL": "https://github.com/apple/swift-collections.git", diff --git a/Package.swift b/Package.swift index 6e141b21d..56c4b1236 100644 --- a/Package.swift +++ b/Package.swift @@ -18,6 +18,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/outfoxx/PotentCodables.git", from: "3.0.0"), .package(url: "https://github.com/sharplet/Regex.git", from: "2.1.0"), + .package(name: "Algorithms", url: "https://github.com/apple/swift-algorithms", from: "1.0.0"), ], targets: [ .target( diff --git a/Sources/ShieldSecurity/SecKeyPair.swift b/Sources/ShieldSecurity/SecKeyPair.swift index 38bfed10d..f3688d59c 100644 --- a/Sources/ShieldSecurity/SecKeyPair.swift +++ b/Sources/ShieldSecurity/SecKeyPair.swift @@ -8,24 +8,32 @@ // Distributed under the MIT License, See LICENSE for details. // -import CryptoKit +import Algorithms import Foundation import PotentASN1 import Security import ShieldCrypto -import ShieldPKCS +import ShieldOID +import ShieldX509 /// Public and private key of an asymmetric key pair. /// public struct SecKeyPair { - /// Default final key length for PBKDF generated export keys. + /// Default final key size for PBKDF generated export keys. /// /// ## See Also /// - ``export(password:derivedKeyLength:keyDerivationTiming:)`` /// - public static let exportDerivedKeyLengthDefault = 32 + public static let exportDerivedKeySizeDefault: ExportKeySize = .bits256 + + /// Default final psuedorandom algorthm for PBKDF generated export keys. + /// + /// ## See Also + /// - ``export(password:derivedKeyLength:keyDerivationTiming:)`` + /// + public static let exportPsuedoRandomAlgorithmDefault: PBKDF.PsuedoRandomAlgorithm = .hmacSha512 /// Default PBKDF generation time for generated export keys. /// @@ -40,6 +48,7 @@ public struct SecKeyPair { case noMatchingKey case itemAddFailed case itemDeleteFailed + case invalidEncodedPrivateKey public static func build(error: Error, message: String, status: OSStatus) -> NSError { let error = error as NSError @@ -292,25 +301,10 @@ public struct SecKeyPair { } #endif - - /// Structure representing keys exported using ``export(password:derivedKeyLength:keyDerivationTiming:)``. - /// - public struct ExportedKey: Codable, SchemaSpecified { - - public static let asn1Schema: Schema = - .sequence([ - "keyType": .integer(), - "exportKeyLength": .integer(), - "exportKeyRounds": .integer(), - "exportKeySalt": .octetString(), - "keyMaterial": .octetString(), - ]) - - public var keyType: SecKeyType - public var exportKeyLength: UInt64 - public var exportKeyRounds: UInt64 - public var exportKeySalt: Data - public var keyMaterial: Data + public enum ExportKeySize: Int { + case bits128 = 16 + case bits192 = 24 + case bits256 = 32 } /// Encrypt and encode the key pair's private key using PBKDF. @@ -323,56 +317,71 @@ public struct SecKeyPair { /// /// - Parameters: /// - password: Password use for key encryption. - /// - derivedKeyLength: PBKDF target key length. + /// - derivedKeySize: PBKDF target key size. /// - keyDerivationTiming: Time PBKDF function should take to generate encryption key. /// - Returns: Encoded encrypted key and PBKDF paraemters. /// public func export( password: String, - derivedKeyLength: Int = exportDerivedKeyLengthDefault, + derivedKeySize: ExportKeySize = exportDerivedKeySizeDefault, + psuedoRandomAlgorithm: PBKDF.PsuedoRandomAlgorithm = exportPsuedoRandomAlgorithmDefault, keyDerivationTiming: TimeInterval = exportKeyDerivationTimingDefault ) throws -> Data { + // Derive key from password + let passwordData = password.data(using: String.Encoding.utf8)! - let exportKeySalt = try Random.generate(count: derivedKeyLength) + let exportKeySalt = try Random.generate(count: derivedKeySize.rawValue) let exportKeyRounds = try PBKDF.calibrate( passwordLength: passwordData.count, saltLength: exportKeySalt.count, - keyLength: derivedKeyLength, + keyLength: derivedKeySize.rawValue, using: .pbkdf2, - psuedoRandomAlgorithm: .sha512, + psuedoRandomAlgorithm: psuedoRandomAlgorithm, taking: keyDerivationTiming ) let exportKey = try PBKDF.derive( - length: derivedKeyLength, + length: derivedKeySize.rawValue, from: passwordData, salt: exportKeySalt, using: .pbkdf2, - psuedoRandomAlgorithm: .sha512, + psuedoRandomAlgorithm: psuedoRandomAlgorithm, rounds: exportKeyRounds ) - let keyMaterial = try encodedPrivateKey() + // Encode and encrypt PKCS#8 PrivateKeyInfo - let encryptedKeyBox = try AES.GCM.seal(keyMaterial, using: SymmetricKey(data: exportKey)) + let encodedPrivateKey = try privateKey.encodePKCS8() - guard let encryptedKeyMaterial = encryptedKeyBox.combined else { - fatalError("Combined sealed box should be available") - } + let encryptedPrivateKeyIV = try Random.generate(count: 16) - let keyType = try privateKey.keyType() + let encryptedPrivateKey = try Cryptor.crypt(encodedPrivateKey, + operation: .encrypt, + using: .aes, + options: .pkcs7Padding, + key: exportKey, + iv: encryptedPrivateKeyIV) - return try ASN1Encoder.encode(ExportedKey( - keyType: keyType, - exportKeyLength: UInt64(derivedKeyLength), - exportKeyRounds: UInt64(exportKeyRounds), - exportKeySalt: exportKeySalt, - keyMaterial: encryptedKeyMaterial - )) + // Build PKCS#8 EncryptedPrivateKeyInfo + + let encryptedPrivateKeyInfo = + try EncryptedPrivateKeyInfo.build(encryptedData: encryptedPrivateKey, + pbkdf2Salt: exportKeySalt, + pbkdf2IterationCount: UInt64(exportKeyRounds), + pbkdf2KeyLength: UInt64(derivedKeySize.rawValue), + pbkdf2Prf: psuedoRandomAlgorithm.prfAlgorithm.oid, + aesEncryptionScheme: derivedKeySize.aesCBCAlgorithm.oid, + aesIV: encryptedPrivateKeyIV) + + let encryptedPrivateKeyInfoData = try ASN1Encoder.encode(encryptedPrivateKeyInfo) + + printPEM(data: encryptedPrivateKeyInfoData, type: "ENCRYPTED PRIVATE KEY") + + return encryptedPrivateKeyInfoData } /// Decode and decrypt a previously exported private key. @@ -387,21 +396,59 @@ public struct SecKeyPair { /// public static func `import`(fromData data: Data, withPassword password: String) throws -> SecKeyPair { - let info = try ASN1Decoder.decode(ExportedKey.self, from: data) + typealias nist = iso_itu.country.us.organization.gov.csor.nistAlgorithms + typealias rsadsi = iso.memberBody.us.rsadsi + typealias pkcs = rsadsi.pkcs + let supportedEncOids = [nist.aes.aes128_CBC_PAD.oid, nist.aes.aes192_CBC_PAD.oid, nist.aes.aes256_CBC_PAD.oid] - let exportKey = try PBKDF.derive( - length: Int(info.exportKeyLength), + let info = try ASN1.Decoder.decode(EncryptedPrivateKeyInfo.self, from: data) + + // Convert and validate requirements (PBKDF2 and AES-CBC-PAD encryption) + + guard + info.encryptionAlgorithm.algorithm == rsadsi.pkcs.pkcs5.pbes2.oid, + let encAlgParams = try? info.encryptionAlgorithm.parameters.map({ try ASN1.Decoder.decodeTree(PBES2Params.self, from: $0) }), + encAlgParams.keyDerivationFunc.algorithm == pkcs.pkcs5.pbkdf2.oid, + let pbkdf2Params = try? encAlgParams.keyDerivationFunc.parameters.map({ try ASN1.Decoder.decodeTree(PBKDF2Params.self, from: $0) }), + supportedEncOids.contains(encAlgParams.encryptionScheme.algorithm), + let aesIV = encAlgParams.encryptionScheme.parameters?.octetStringValue + else { + throw Error.invalidEncodedPrivateKey + } + + // Derive import key from password and PBKDF2 params in encrypted PKCS#8 data + + let importKey = try PBKDF.derive( + length: Int(pbkdf2Params.keyLength), from: password.data(using: .utf8)!, - salt: info.exportKeySalt, + salt: pbkdf2Params.salt, using: .pbkdf2, - psuedoRandomAlgorithm: .sha512, - rounds: Int(info.exportKeyRounds) + psuedoRandomAlgorithm: try PBKDF.PsuedoRandomAlgorithm.from(oid: pbkdf2Params.prf.algorithm), + rounds: Int(pbkdf2Params.iterationCount) ) - let keyMaterial = try AES.GCM.open(AES.GCM.SealedBox(combined: info.keyMaterial), - using: SymmetricKey(data: exportKey)) + // Decrypt & decode PKCS#8 PrivateKeyInfo + + let privateKeyInfoData = try Cryptor.crypt(info.encryptedData, + operation: .decrypt, + using: .aes, + options: .pkcs7Padding, + key: importKey, + iv: aesIV) - return try Self(type: info.keyType, privateKeyData: keyMaterial) + let privateKeyInfo: PrivateKeyInfo + do { + privateKeyInfo = try ASN1.Decoder.decode(PrivateKeyInfo.self, from: privateKeyInfoData) + } + catch { + throw SecKeyPair.Error.invalidEncodedPrivateKey + } + + // Convert to SecKey decode params + + let (keyType, keyDecodeData) = try SecKey.extractDecodeParams(privateKeyInfo: privateKeyInfo) + + return try Self(type: keyType, privateKeyData: keyDecodeData) } } @@ -427,3 +474,205 @@ extension SecKeyPair: Codable { } } + +private extension SecKey { + + static func extractDecodeParams(privateKeyInfo: PrivateKeyInfo) throws -> (SecKeyType, Data) { + + let keyType: SecKeyType + let importKeyData: Data + switch privateKeyInfo.privateKeyAlgorithm.algorithm { + case iso.memberBody.us.rsadsi.pkcs.pkcs1.rsaEncryption.oid: + keyType = .rsa + importKeyData = privateKeyInfo.privateKey + + case iso.memberBody.us.ansix962.keyType.ecPublicKey.oid: + let ecPrivateKey = try ASN1.Decoder.decode(ECPrivateKey.self, from: privateKeyInfo.privateKey) + guard let publicKey = ecPrivateKey.publicKey else { + throw SecKeyPair.Error.invalidEncodedPrivateKey + } + + keyType = .ec + importKeyData = publicKey.bytes + ecPrivateKey.privateKey + + default: + throw AlgorithmIdentifier.Error.unsupportedAlgorithm + } + + return (keyType, importKeyData) + } + + func encodePKCS8() throws -> Data { + let privateKeyInfo: PrivateKeyInfo + switch try keyType() { + case .rsa: + privateKeyInfo = try generateRSAPrivateKeyInfo() + + case .ec: + privateKeyInfo = try generateECPrivateKeyInfo() + } + + let encoded = try ASN1.Encoder.encode(privateKeyInfo) + + printPEM(data: encoded, type: "PRIVATE KEY") + + return encoded + } + + private func generateRSAPrivateKeyInfo() throws -> PrivateKeyInfo { + let encodedPrivateKey = try encode() + + printPEM(data: encodedPrivateKey, type: "RSA PRIVATE KEY") + + return PrivateKeyInfo(privateKeyAlgorithm: .init(algorithm: iso.memberBody.us.rsadsi.pkcs.pkcs1.rsaEncryption.oid), + privateKey: encodedPrivateKey) + } + + private func generateECPrivateKeyInfo() throws -> PrivateKeyInfo { + let encodedPrivateKey = try encode() + + let (curveOID, keyNumberSize) = try getECCurveAndNumberSize() + + let parts = encodedPrivateKey.dropFirst().chunks(ofCount: keyNumberSize) + if parts.count != 3 { + throw SecKeyPair.Error.invalidEncodedPrivateKey + } + + let encodedPublicKey = Data(encodedPrivateKey.prefix(1) + parts.dropLast().joined()) + + let privateKey = ECPrivateKey(version: .one, + privateKey: parts.last!, + parameters: curveOID, + publicKey: BitString(bytes: encodedPublicKey)) + + let privateKeyData = try ASN1.Encoder.encode(privateKey) + + printPEM(data: privateKeyData, type: "EC PRIVATE KEY") + + + return PrivateKeyInfo(version: .zero, + privateKeyAlgorithm: .init(algorithm: iso.memberBody.us.ansix962.keyType.ecPublicKey.oid, + parameters: ASN1.objectIdentifier(curveOID.fields)), + privateKey: privateKeyData) + } + + func getECCurveAndNumberSize() throws -> (OID, Int) { + + switch try attributes()[kSecAttrKeySizeInBits as String] as? Int ?? 0 { + case 192: + // P-192, secp192r1 + return (iso.memberBody.us.ansix962.curves.prime.prime192v1.oid, 24) + case 256: + // P-256, secp256r1 + return (iso.memberBody.us.ansix962.curves.prime.prime256v1.oid, 32) + case 384: + // P-384, secp384r1 + return (iso.org.certicom.curve.ansip384r1.oid, 48) + case 521: + // P-521, secp521r1 + return (iso.org.certicom.curve.ansip521r1.oid, 66) + default: + throw AlgorithmIdentifier.Error.unsupportedECKeySize + } + } + +} + +private extension PBKDF.PsuedoRandomAlgorithm { + + var prfAlgorithm: iso.memberBody.us.rsadsi.digestAlgorithm { + typealias algs = iso.memberBody.us.rsadsi.digestAlgorithm + + switch self { + case .hmacSha1: + return algs.hmacWithSHA1 + case .hmacSha224: + return algs.hmacWithSHA224 + case .hmacSha256: + return algs.hmacWithSHA256 + case .hmacSha384: + return algs.hmacWithSHA384 + case .hmacSha512: + return algs.hmacWithSHA512 + default: + fatalError("Unsupported PBKDF Psuedo Random Algorithm") + } + } + + static func from(oid: OID) throws -> Self { + typealias algs = iso.memberBody.us.rsadsi.digestAlgorithm + + switch oid { + case algs.hmacWithSHA1.oid: + return .hmacSha1 + case algs.hmacWithSHA224.oid: + return .hmacSha224 + case algs.hmacWithSHA256.oid: + return .hmacSha256 + case algs.hmacWithSHA384.oid: + return .hmacSha384 + case algs.hmacWithSHA512.oid: + return .hmacSha512 + default: + throw SecKeyPair.Error.invalidEncodedPrivateKey + } + } + +} + +private extension SecKeyPair.ExportKeySize { + + var aesCBCAlgorithm: iso_itu.country.us.organization.gov.csor.nistAlgorithms.aes { + typealias aes = iso_itu.country.us.organization.gov.csor.nistAlgorithms.aes + + switch self { + case .bits128: + return aes.aes128_CBC_PAD + case .bits192: + return aes.aes192_CBC_PAD + case .bits256: + return aes.aes256_CBC_PAD + } + } + +} + +private extension EncryptedPrivateKeyInfo { + + static func build( + encryptedData: Data, + pbkdf2Salt: Data, + pbkdf2IterationCount: UInt64, + pbkdf2KeyLength: UInt64, + pbkdf2Prf: OID, + aesEncryptionScheme: OID, + aesIV: Data + ) throws -> EncryptedPrivateKeyInfo { + typealias pkcs5 = iso.memberBody.us.rsadsi.pkcs.pkcs5 + + let pbkdf2Params = PBKDF2Params(salt: pbkdf2Salt, + iterationCount: pbkdf2IterationCount, + keyLength: pbkdf2KeyLength, + prf: .init(algorithm: pbkdf2Prf)) + + let encAlgParams = PBES2Params(keyDerivationFunc: .init(algorithm: pkcs5.pbkdf2.oid, + parameters: try ASN1.Encoder.encodeTree(pbkdf2Params)), + encryptionScheme: .init(algorithm: aesEncryptionScheme, + parameters: .octetString(aesIV))) + + let encAlgId = AlgorithmIdentifier(algorithm: pkcs5.pbes2.oid, + parameters: try ASN1.Encoder.encodeTree(encAlgParams)) + + return EncryptedPrivateKeyInfo(encryptionAlgorithm: encAlgId, + encryptedData: encryptedData) + } +} + +private func printPEM(data: Data, type: String) { + + let base64 = data.base64EncodedString() + let pemBase64 = base64.chunks(ofCount: 64).joined(separator: "\n") + + print("-----BEGIN \(type)-----\n\(pemBase64)\n-----END \(type)-----") +} + From aa8895f5c90d33ea72360f4f45a50fa0c847a5df Mon Sep 17 00:00:00 2001 From: Kevin Wooten Date: Fri, 9 Jun 2023 15:48:31 -0700 Subject: [PATCH 5/7] Update to PotentCodables 3.0.3 to include support for random data resilience This is required to ensure when attempting to decode structures decrypted with an incorrect key an error is thrown. This is because PKCS#8 requires AES-CBC which is not authenticated and cannot itself detect invalid passwords. --- Package.resolved | 4 ++-- Package.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Package.resolved b/Package.resolved index 10a34e90b..f5c2c0f0e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/outfoxx/PotentCodables.git", "state": { "branch": null, - "revision": "e7241008d1d52e106f84674161652e12cf8dfc32", - "version": "3.0.1" + "revision": "9c5adf2563d12ce0a3cd1c19efa19f7f8d8ca078", + "version": "3.0.3" } }, { diff --git a/Package.swift b/Package.swift index 56c4b1236..0cdb06868 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ let package = Package( targets: ["Shield", "ShieldSecurity", "ShieldCrypto", "ShieldOID", "ShieldPKCS", "ShieldX509", "ShieldX500"]), ], dependencies: [ - .package(url: "https://github.com/outfoxx/PotentCodables.git", from: "3.0.0"), + .package(url: "https://github.com/outfoxx/PotentCodables.git", from: "3.0.3"), .package(url: "https://github.com/sharplet/Regex.git", from: "2.1.0"), .package(name: "Algorithms", url: "https://github.com/apple/swift-algorithms", from: "1.0.0"), ], From 0e64a5457c2d1c7e93658c324d337eec2d368021 Mon Sep 17 00:00:00 2001 From: Kevin Wooten Date: Fri, 9 Jun 2023 15:48:46 -0700 Subject: [PATCH 6/7] Add test for EC import/export --- Tests/SecIdentityTests.swift | 2 +- Tests/SecKeyPairTests.swift | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Tests/SecIdentityTests.swift b/Tests/SecIdentityTests.swift index 1e0dc70c9..5b8fab885 100644 --- a/Tests/SecIdentityTests.swift +++ b/Tests/SecIdentityTests.swift @@ -39,7 +39,7 @@ class SecIdentityTests: XCTestCase { kSecClass as String: kSecClassCertificate, kSecMatchItemList as String: [cert] as CFArray, kSecMatchLimit as String: kSecMatchLimitOne, - ] as CFDictionary) + ] as [String: Any] as CFDictionary) } // Ensure all went well diff --git a/Tests/SecKeyPairTests.swift b/Tests/SecKeyPairTests.swift index 9d97765c9..55133df92 100644 --- a/Tests/SecKeyPairTests.swift +++ b/Tests/SecKeyPairTests.swift @@ -162,7 +162,7 @@ class SecKeyPairTests: XCTestCase { } #endif - func testImportExport() throws { + func testImportExportRSA() throws { let exportedKeyData = try rsaKeyPair.export(password: "123") @@ -195,6 +195,15 @@ class SecKeyPairTests: XCTestCase { } + func testImportExportEC() throws { + + let exportedKeyData = try ecKeyPair.export(password: "123") + + _ = try SecKeyPair.import(fromData: exportedKeyData, withPassword: "123") + + XCTAssertThrowsError(try SecKeyPair.import(fromData: exportedKeyData, withPassword: "456")) + } + func testCodable() throws { let rsaData = try JSONEncoder().encode(rsaKeyPair) From 38bb3625bee3eb7c9290a603a36b32b90484686d Mon Sep 17 00:00:00 2001 From: Kevin Wooten Date: Fri, 9 Jun 2023 16:07:25 -0700 Subject: [PATCH 7/7] Fix lint --- Sources/ShieldOID/ISO-ITU.swift | 2 +- Sources/ShieldOID/ISO.swift | 2 +- Sources/ShieldPKCS/Moved.swift | 5 +- Sources/ShieldSecurity/SecKeyPair.swift | 51 +++++----- Sources/ShieldX509/ECPrivateKey.swift | 8 +- .../ShieldX509/EncryptedPrivateKeyInfo.swift | 9 +- Sources/ShieldX509/PBES2Params.swift | 95 ++++++++++--------- Sources/ShieldX509/PBKDF2Params.swift | 21 ++-- Sources/ShieldX509/PrivateKeyInfo.swift | 5 +- 9 files changed, 107 insertions(+), 91 deletions(-) diff --git a/Sources/ShieldOID/ISO-ITU.swift b/Sources/ShieldOID/ISO-ITU.swift index 2fee95c91..9e3b7ece3 100644 --- a/Sources/ShieldOID/ISO-ITU.swift +++ b/Sources/ShieldOID/ISO-ITU.swift @@ -11,7 +11,7 @@ import Foundation // swiftformat:disable consecutiveSpaces -// swiftlint:disable type_name identifier_name +// swiftlint:disable type_name identifier_name nesting /// Areas of joint work between ISO/IEC (International Organization for Standardization/International Electrotechnical Commission) /// and ITU-T (International Telecommunication Union - Telecommunication standardization sector), and other international work diff --git a/Sources/ShieldOID/ISO.swift b/Sources/ShieldOID/ISO.swift index 9fd4751d1..9b90f7ab0 100644 --- a/Sources/ShieldOID/ISO.swift +++ b/Sources/ShieldOID/ISO.swift @@ -12,7 +12,7 @@ import Foundation import PotentASN1 // swiftformat:disable consecutiveSpaces -// swiftlint:disable type_name nesting +// swiftlint:disable type_name identifier_name nesting /// International Organization for Standardization (ISO) /// diff --git a/Sources/ShieldPKCS/Moved.swift b/Sources/ShieldPKCS/Moved.swift index e52f917e1..c6caf17c1 100644 --- a/Sources/ShieldPKCS/Moved.swift +++ b/Sources/ShieldPKCS/Moved.swift @@ -1,8 +1,11 @@ // // Moved.swift +// Shield // +// Copyright © 2019 Outfox, inc. // -// Created by Kevin Wooten on 6/8/23. +// +// Distributed under the MIT License, See LICENSE for details. // import ShieldX509 diff --git a/Sources/ShieldSecurity/SecKeyPair.swift b/Sources/ShieldSecurity/SecKeyPair.swift index f3688d59c..f17bd925b 100644 --- a/Sources/ShieldSecurity/SecKeyPair.swift +++ b/Sources/ShieldSecurity/SecKeyPair.swift @@ -396,19 +396,19 @@ public struct SecKeyPair { /// public static func `import`(fromData data: Data, withPassword password: String) throws -> SecKeyPair { - typealias nist = iso_itu.country.us.organization.gov.csor.nistAlgorithms - typealias rsadsi = iso.memberBody.us.rsadsi - typealias pkcs = rsadsi.pkcs - let supportedEncOids = [nist.aes.aes128_CBC_PAD.oid, nist.aes.aes192_CBC_PAD.oid, nist.aes.aes256_CBC_PAD.oid] + typealias Nist = iso_itu.country.us.organization.gov.csor.nistAlgorithms + typealias RSADSI = iso.memberBody.us.rsadsi + typealias PKCS = RSADSI.pkcs + let supportedEncOids = [Nist.aes.aes128_CBC_PAD.oid, Nist.aes.aes192_CBC_PAD.oid, Nist.aes.aes256_CBC_PAD.oid] let info = try ASN1.Decoder.decode(EncryptedPrivateKeyInfo.self, from: data) // Convert and validate requirements (PBKDF2 and AES-CBC-PAD encryption) guard - info.encryptionAlgorithm.algorithm == rsadsi.pkcs.pkcs5.pbes2.oid, + info.encryptionAlgorithm.algorithm == RSADSI.pkcs.pkcs5.pbes2.oid, let encAlgParams = try? info.encryptionAlgorithm.parameters.map({ try ASN1.Decoder.decodeTree(PBES2Params.self, from: $0) }), - encAlgParams.keyDerivationFunc.algorithm == pkcs.pkcs5.pbkdf2.oid, + encAlgParams.keyDerivationFunc.algorithm == PKCS.pkcs5.pbkdf2.oid, let pbkdf2Params = try? encAlgParams.keyDerivationFunc.parameters.map({ try ASN1.Decoder.decodeTree(PBKDF2Params.self, from: $0) }), supportedEncOids.contains(encAlgParams.encryptionScheme.algorithm), let aesIV = encAlgParams.encryptionScheme.parameters?.octetStringValue @@ -581,37 +581,37 @@ private extension SecKey { private extension PBKDF.PsuedoRandomAlgorithm { var prfAlgorithm: iso.memberBody.us.rsadsi.digestAlgorithm { - typealias algs = iso.memberBody.us.rsadsi.digestAlgorithm + typealias Algs = iso.memberBody.us.rsadsi.digestAlgorithm switch self { case .hmacSha1: - return algs.hmacWithSHA1 + return Algs.hmacWithSHA1 case .hmacSha224: - return algs.hmacWithSHA224 + return Algs.hmacWithSHA224 case .hmacSha256: - return algs.hmacWithSHA256 + return Algs.hmacWithSHA256 case .hmacSha384: - return algs.hmacWithSHA384 + return Algs.hmacWithSHA384 case .hmacSha512: - return algs.hmacWithSHA512 + return Algs.hmacWithSHA512 default: fatalError("Unsupported PBKDF Psuedo Random Algorithm") } } static func from(oid: OID) throws -> Self { - typealias algs = iso.memberBody.us.rsadsi.digestAlgorithm + typealias Algs = iso.memberBody.us.rsadsi.digestAlgorithm switch oid { - case algs.hmacWithSHA1.oid: + case Algs.hmacWithSHA1.oid: return .hmacSha1 - case algs.hmacWithSHA224.oid: + case Algs.hmacWithSHA224.oid: return .hmacSha224 - case algs.hmacWithSHA256.oid: + case Algs.hmacWithSHA256.oid: return .hmacSha256 - case algs.hmacWithSHA384.oid: + case Algs.hmacWithSHA384.oid: return .hmacSha384 - case algs.hmacWithSHA512.oid: + case Algs.hmacWithSHA512.oid: return .hmacSha512 default: throw SecKeyPair.Error.invalidEncodedPrivateKey @@ -623,15 +623,15 @@ private extension PBKDF.PsuedoRandomAlgorithm { private extension SecKeyPair.ExportKeySize { var aesCBCAlgorithm: iso_itu.country.us.organization.gov.csor.nistAlgorithms.aes { - typealias aes = iso_itu.country.us.organization.gov.csor.nistAlgorithms.aes + typealias AES = iso_itu.country.us.organization.gov.csor.nistAlgorithms.aes switch self { case .bits128: - return aes.aes128_CBC_PAD + return AES.aes128_CBC_PAD case .bits192: - return aes.aes192_CBC_PAD + return AES.aes192_CBC_PAD case .bits256: - return aes.aes256_CBC_PAD + return AES.aes256_CBC_PAD } } @@ -648,19 +648,19 @@ private extension EncryptedPrivateKeyInfo { aesEncryptionScheme: OID, aesIV: Data ) throws -> EncryptedPrivateKeyInfo { - typealias pkcs5 = iso.memberBody.us.rsadsi.pkcs.pkcs5 + typealias PKCS = iso.memberBody.us.rsadsi.pkcs.pkcs5 let pbkdf2Params = PBKDF2Params(salt: pbkdf2Salt, iterationCount: pbkdf2IterationCount, keyLength: pbkdf2KeyLength, prf: .init(algorithm: pbkdf2Prf)) - let encAlgParams = PBES2Params(keyDerivationFunc: .init(algorithm: pkcs5.pbkdf2.oid, + let encAlgParams = PBES2Params(keyDerivationFunc: .init(algorithm: PKCS.pbkdf2.oid, parameters: try ASN1.Encoder.encodeTree(pbkdf2Params)), encryptionScheme: .init(algorithm: aesEncryptionScheme, parameters: .octetString(aesIV))) - let encAlgId = AlgorithmIdentifier(algorithm: pkcs5.pbes2.oid, + let encAlgId = AlgorithmIdentifier(algorithm: PKCS.pbes2.oid, parameters: try ASN1.Encoder.encodeTree(encAlgParams)) return EncryptedPrivateKeyInfo(encryptionAlgorithm: encAlgId, @@ -675,4 +675,3 @@ private func printPEM(data: Data, type: String) { print("-----BEGIN \(type)-----\n\(pemBase64)\n-----END \(type)-----") } - diff --git a/Sources/ShieldX509/ECPrivateKey.swift b/Sources/ShieldX509/ECPrivateKey.swift index c3a35c1d2..c4b4b8ef8 100644 --- a/Sources/ShieldX509/ECPrivateKey.swift +++ b/Sources/ShieldX509/ECPrivateKey.swift @@ -1,8 +1,11 @@ // // ECPrivateKey.swift -// +// Shield // -// Created by Kevin Wooten on 6/8/23. +// Copyright © 2019 Outfox, inc. +// +// +// Distributed under the MIT License, See LICENSE for details. // import Foundation @@ -46,4 +49,3 @@ public extension Schemas { ]) } - diff --git a/Sources/ShieldX509/EncryptedPrivateKeyInfo.swift b/Sources/ShieldX509/EncryptedPrivateKeyInfo.swift index a7ecb7faf..33bf59a70 100644 --- a/Sources/ShieldX509/EncryptedPrivateKeyInfo.swift +++ b/Sources/ShieldX509/EncryptedPrivateKeyInfo.swift @@ -1,8 +1,11 @@ // // EncryptedPrivateKeyInfo.swift -// +// Shield // -// Created by Kevin Wooten on 6/8/23. +// Copyright © 2019 Outfox, inc. +// +// +// Distributed under the MIT License, See LICENSE for details. // import Foundation @@ -37,7 +40,7 @@ public extension Schemas { static let EncryptedPrivateKeyInfo: Schema = .sequence([ "encryptionAlgorithm": algorithmIdentifier(EncryptedPrivateKeyInfoAlgorithms), - "encryptedData": .octetString() + "encryptedData": .octetString(), ]) } diff --git a/Sources/ShieldX509/PBES2Params.swift b/Sources/ShieldX509/PBES2Params.swift index 7421a3ba3..2f14b6ad7 100644 --- a/Sources/ShieldX509/PBES2Params.swift +++ b/Sources/ShieldX509/PBES2Params.swift @@ -1,8 +1,11 @@ // // PBES.swift -// +// Shield // -// Created by Kevin Wooten on 6/8/23. +// Copyright © 2019 Outfox, inc. +// +// +// Distributed under the MIT License, See LICENSE for details. // import BigInt @@ -29,61 +32,61 @@ extension PBES2Params: SchemaSpecified { public extension Schemas { - private typealias rsadisDigAlg = iso.memberBody.us.rsadsi.digestAlgorithm - private typealias rsadisEncAlg = iso.memberBody.us.rsadsi.encryptionAlgorithm - private typealias nistAlgs = iso_itu.country.us.organization.gov.csor.nistAlgorithms.aes + private typealias RSADSIDigAlgs = iso.memberBody.us.rsadsi.digestAlgorithm + private typealias RSADSIEncAlgs = iso.memberBody.us.rsadsi.encryptionAlgorithm + private typealias NISTAlgs = iso_itu.country.us.organization.gov.csor.nistAlgorithms.aes static let PBES2ParamsKeyDerivationFuncAlgorithms: Schema.DynamicMap = [ iso.memberBody.us.rsadsi.pkcs.pkcs5.pbkdf2.asn1: PBKDF2Params ] static let PBES2ParamsEncryptionSchemeAlgorithms: Schema.DynamicMap = [ - rsadisEncAlg.rc2CBC.asn1: .null, - rsadisEncAlg.rc2ECB.asn1: .null, - rsadisEncAlg.rc4.asn1: .null, - rsadisEncAlg.rc4WithMAC.asn1: .null, - rsadisEncAlg.desxCBC.asn1: .null, - rsadisEncAlg.desEDE3CBC.asn1: .null, - rsadisEncAlg.rc5CBC.asn1: .null, - rsadisEncAlg.rc5CBCPad.asn1: .null, - rsadisEncAlg.desCDMF.asn1: .null, - rsadisEncAlg.desEDE3.asn1: .null, - - nistAlgs.aes128_ECB.asn1: .null, - nistAlgs.aes128_CBC_PAD.asn1: .null, - nistAlgs.aes128_OFB.asn1: .null, - nistAlgs.aes128_CFB.asn1: .null, - nistAlgs.aes128_wrap.asn1: .null, - nistAlgs.aes128_GCM.asn1: .null, - nistAlgs.aes128_CCM.asn1: .null, - nistAlgs.aes128_wrap_pad.asn1: .null, - nistAlgs.aes128_GMAC.asn1: .null, - - nistAlgs.aes192_ECB.asn1: .null, - nistAlgs.aes192_CBC_PAD.asn1: .null, - nistAlgs.aes192_OFB.asn1: .null, - nistAlgs.aes192_CFB.asn1: .null, - nistAlgs.aes192_wrap.asn1: .null, - nistAlgs.aes192_GCM.asn1: .null, - nistAlgs.aes192_CCM.asn1: .null, - nistAlgs.aes192_wrap_pad.asn1: .null, - nistAlgs.aes192_GMAC.asn1: .null, - - nistAlgs.aes256_ECB.asn1: .null, - nistAlgs.aes256_CBC_PAD.asn1: .null, - nistAlgs.aes256_OFB.asn1: .null, - nistAlgs.aes256_CFB.asn1: .null, - nistAlgs.aes256_wrap.asn1: .null, - nistAlgs.aes256_GCM.asn1: .null, - nistAlgs.aes256_CCM.asn1: .null, - nistAlgs.aes256_wrap_pad.asn1: .null, - nistAlgs.aes256_GMAC.asn1: .null, + RSADSIEncAlgs.rc2CBC.asn1: .null, + RSADSIEncAlgs.rc2ECB.asn1: .null, + RSADSIEncAlgs.rc4.asn1: .null, + RSADSIEncAlgs.rc4WithMAC.asn1: .null, + RSADSIEncAlgs.desxCBC.asn1: .null, + RSADSIEncAlgs.desEDE3CBC.asn1: .null, + RSADSIEncAlgs.rc5CBC.asn1: .null, + RSADSIEncAlgs.rc5CBCPad.asn1: .null, + RSADSIEncAlgs.desCDMF.asn1: .null, + RSADSIEncAlgs.desEDE3.asn1: .null, + + NISTAlgs.aes128_ECB.asn1: .null, + NISTAlgs.aes128_CBC_PAD.asn1: .null, + NISTAlgs.aes128_OFB.asn1: .null, + NISTAlgs.aes128_CFB.asn1: .null, + NISTAlgs.aes128_wrap.asn1: .null, + NISTAlgs.aes128_GCM.asn1: .null, + NISTAlgs.aes128_CCM.asn1: .null, + NISTAlgs.aes128_wrap_pad.asn1: .null, + NISTAlgs.aes128_GMAC.asn1: .null, + + NISTAlgs.aes192_ECB.asn1: .null, + NISTAlgs.aes192_CBC_PAD.asn1: .null, + NISTAlgs.aes192_OFB.asn1: .null, + NISTAlgs.aes192_CFB.asn1: .null, + NISTAlgs.aes192_wrap.asn1: .null, + NISTAlgs.aes192_GCM.asn1: .null, + NISTAlgs.aes192_CCM.asn1: .null, + NISTAlgs.aes192_wrap_pad.asn1: .null, + NISTAlgs.aes192_GMAC.asn1: .null, + + NISTAlgs.aes256_ECB.asn1: .null, + NISTAlgs.aes256_CBC_PAD.asn1: .null, + NISTAlgs.aes256_OFB.asn1: .null, + NISTAlgs.aes256_CFB.asn1: .null, + NISTAlgs.aes256_wrap.asn1: .null, + NISTAlgs.aes256_GCM.asn1: .null, + NISTAlgs.aes256_CCM.asn1: .null, + NISTAlgs.aes256_wrap_pad.asn1: .null, + NISTAlgs.aes256_GMAC.asn1: .null, ] static let PBES2Params: Schema = .sequence([ "keyDerivationFunc": algorithmIdentifier(PBES2ParamsKeyDerivationFuncAlgorithms), - "encryptionScheme": algorithmIdentifier(PBES2ParamsEncryptionSchemeAlgorithms) + "encryptionScheme": algorithmIdentifier(PBES2ParamsEncryptionSchemeAlgorithms), ]) } diff --git a/Sources/ShieldX509/PBKDF2Params.swift b/Sources/ShieldX509/PBKDF2Params.swift index 41c1e0d8d..58cf7fbec 100644 --- a/Sources/ShieldX509/PBKDF2Params.swift +++ b/Sources/ShieldX509/PBKDF2Params.swift @@ -1,8 +1,11 @@ // // PBKDF2Params.swift -// +// Shield // -// Created by Kevin Wooten on 6/8/23. +// Copyright © 2019 Outfox, inc. +// +// +// Distributed under the MIT License, See LICENSE for details. // import Foundation @@ -33,14 +36,14 @@ extension PBKDF2Params: SchemaSpecified { public extension Schemas { - private typealias digAlgs = iso.memberBody.us.rsadsi.digestAlgorithm + private typealias DigAlgs = iso.memberBody.us.rsadsi.digestAlgorithm private static let PRFAglorithms: Schema.DynamicMap = [ - digAlgs.hmacWithSHA1.asn1: .null, - digAlgs.hmacWithSHA224.asn1: .null, - digAlgs.hmacWithSHA256.asn1: .null, - digAlgs.hmacWithSHA384.asn1: .null, - digAlgs.hmacWithSHA512.asn1: .null, + DigAlgs.hmacWithSHA1.asn1: .null, + DigAlgs.hmacWithSHA224.asn1: .null, + DigAlgs.hmacWithSHA256.asn1: .null, + DigAlgs.hmacWithSHA384.asn1: .null, + DigAlgs.hmacWithSHA512.asn1: .null, ] static let PBKDF2Params: Schema = @@ -48,7 +51,7 @@ public extension Schemas { "salt": .choiceOf([.octetString(), .objectIdentifier()]), "iterationCount": .integer(), "keyLength": .optional(.integer()), - "prf": algorithmIdentifier(PRFAglorithms) + "prf": algorithmIdentifier(PRFAglorithms), ]) } diff --git a/Sources/ShieldX509/PrivateKeyInfo.swift b/Sources/ShieldX509/PrivateKeyInfo.swift index ef3f71578..87ce59c5a 100644 --- a/Sources/ShieldX509/PrivateKeyInfo.swift +++ b/Sources/ShieldX509/PrivateKeyInfo.swift @@ -1,8 +1,11 @@ // // PrivateKeyInfo.swift +// Shield // +// Copyright © 2019 Outfox, inc. // -// Created by Kevin Wooten on 6/7/23. +// +// Distributed under the MIT License, See LICENSE for details. // import Foundation