|
@@ -0,0 +1,521 @@
|
|
|
|
+//
|
|
|
|
+// CryptoSwift
|
|
|
|
+//
|
|
|
|
+// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
|
|
|
+// This software is provided 'as-is', without any express or implied warranty.
|
|
|
|
+//
|
|
|
|
+// In no event will the authors be held liable for any damages arising from the use of this software.
|
|
|
|
+//
|
|
|
|
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
|
|
|
+//
|
|
|
|
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
|
|
|
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
|
|
|
+// - This notice may not be removed or altered from any source or binary distribution.
|
|
|
|
+//
|
|
|
|
+
|
|
|
|
+import XCTest
|
|
|
|
+@testable import CryptoSwift
|
|
|
|
+
|
|
|
|
+final class ASN1Tests: XCTestCase {
|
|
|
|
+
|
|
|
|
+ /// This test ensures that we get the same data out as we put in before and after encoding
|
|
|
|
+ ///
|
|
|
|
+ /// This test enforces that
|
|
|
|
+ /// 1) The data provided to each of the `ASN1.Node`s is preserved across encoding and decoding.
|
|
|
|
+ func testASN1NonDestructiveEncoding() throws {
|
|
|
|
+ let arbitraryData = Data(hex: "0123456789")
|
|
|
|
+
|
|
|
|
+ // Encode the serialized BigInteger
|
|
|
|
+ let node: ASN1.Node = .sequence(nodes: [
|
|
|
|
+ .integer(data: arbitraryData),
|
|
|
|
+ .bitString(data: arbitraryData),
|
|
|
|
+ .octetString(data: arbitraryData),
|
|
|
|
+ .null,
|
|
|
|
+ .objectIdentifier(data: arbitraryData)
|
|
|
|
+ ])
|
|
|
|
+
|
|
|
|
+ let encoded = ASN1.Encoder.encode(node)
|
|
|
|
+
|
|
|
|
+ // Decode the Encoding
|
|
|
|
+ let decoded = try ASN1.Decoder.decode(data: Data(encoded))
|
|
|
|
+ guard case .sequence(let sequence) = decoded else {
|
|
|
|
+ XCTFail("Failed to recover encoded SEQUENCE")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ XCTAssertEqual(sequence.count, 5)
|
|
|
|
+
|
|
|
|
+ guard case .integer(let integerData) = sequence[0] else {
|
|
|
|
+ XCTFail("Failed to recover encoded INTEGER")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(integerData, arbitraryData)
|
|
|
|
+
|
|
|
|
+ guard case .bitString(let bitStringData) = sequence[1] else {
|
|
|
|
+ XCTFail("Failed to recover encoded BITSTRING")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(bitStringData, arbitraryData)
|
|
|
|
+
|
|
|
|
+ guard case .octetString(let octetData) = sequence[2] else {
|
|
|
|
+ XCTFail("Failed to recover encoded OCTETSTRING")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(octetData, arbitraryData)
|
|
|
|
+
|
|
|
|
+ guard case .null = sequence[3] else {
|
|
|
|
+ XCTFail("Failed to recover encoded NULL")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ guard case .objectIdentifier(let objIDData) = sequence[4] else {
|
|
|
|
+ XCTFail("Failed to recover encoded OBJECTIDENTIFIER")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(objIDData, arbitraryData)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// The ASN1 Encoder / Decoder doesn't handle encoding / decoding Integers directly, it's your responsibility to accurately serialize your integers.
|
|
|
|
+ /// - Note: In this example we're using BigInteger's serialization technique to encode / decode Integers, which isn't the default method for handling integers. https://www.strozhevsky.com/free_docs/asn1_by_simple_words.pdf
|
|
|
|
+ ///
|
|
|
|
+ /// This test enforces that
|
|
|
|
+ /// 1) The data provided to the `ASN1.Node.integer` Node is preserved across encoding and decoding.
|
|
|
|
+ func testASN1DecodePrimitiveInteger() throws {
|
|
|
|
+ let tests = [
|
|
|
|
+ 0,
|
|
|
|
+ -0,
|
|
|
|
+ 128,
|
|
|
|
+ -128,
|
|
|
|
+ 136,
|
|
|
|
+ -136,
|
|
|
|
+ 8388607,
|
|
|
|
+ -8388607,
|
|
|
|
+ 3409934108352718734,
|
|
|
|
+ -3409934108352718734
|
|
|
|
+ ]
|
|
|
|
+
|
|
|
|
+ for test in tests {
|
|
|
|
+ let number = BigInteger(test)
|
|
|
|
+
|
|
|
|
+ // Encode the serialized Integer
|
|
|
|
+ let encoded = ASN1.Encoder.encode(.integer(data: number.serialize()))
|
|
|
|
+
|
|
|
|
+ // Ensure the Integer Prefix was added
|
|
|
|
+ XCTAssertEqual(Array<UInt8>(arrayLiteral: 0x02), Array(encoded.prefix(1)))
|
|
|
|
+
|
|
|
|
+ // Decode the Encoding
|
|
|
|
+ let decoded = try ASN1.Decoder.decode(data: Data(encoded))
|
|
|
|
+ guard case .integer(let num) = decoded else {
|
|
|
|
+ XCTFail("Failed to recover encoded integer")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ensure the original BigInteger was recovered
|
|
|
|
+ XCTAssertEqual(BigInteger(num), number)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Another test showing that Integers are stored as arbitrary data and the proper serialization and signage are the responsibilities of the user and their application.
|
|
|
|
+ ///
|
|
|
|
+ /// This test enforces that
|
|
|
|
+ /// 1) The data provided to the `ASN1.Node.integer` Node is preserved across encoding and decoding.
|
|
|
|
+ func testASN1DecodeLargeBigInteger() throws {
|
|
|
|
+ // Because INTEGERS are stored as arbitrary data we should be able to store an integer that would otherwise overflow by using the BigInteger library
|
|
|
|
+ let largeBigInt = BigInteger("1541235134652345698374107823450134507610354876134950342785028743653")
|
|
|
|
+
|
|
|
|
+ // Encode the serialized BigInteger
|
|
|
|
+ let encoded = ASN1.Encoder.encode(.integer(data: largeBigInt.serialize()))
|
|
|
|
+
|
|
|
|
+ // Decode the Encoding
|
|
|
|
+ let decoded = try ASN1.Decoder.decode(data: Data(encoded))
|
|
|
|
+ guard case .integer(let num) = decoded else {
|
|
|
|
+ XCTFail("Failed to recover encoded integer")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ensure the original BigInteger was recovered
|
|
|
|
+ XCTAssertEqual(BigInteger(num), largeBigInt)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// This tests decodes an RSA Public Key from a Base64 DER Encoded representation
|
|
|
|
+ ///
|
|
|
|
+ /// This test enforces that
|
|
|
|
+ /// 1) The decoding process yeilds the expected format.
|
|
|
|
+ /// 2) The re-encoding of the data yeilds the exact same starting data.
|
|
|
|
+ func testANS1DERDecodingPublicKey() throws {
|
|
|
|
+ /// An example of an RSA Public Key ASN1 DER Encoding
|
|
|
|
+ ///
|
|
|
|
+ /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.1)
|
|
|
|
+ /// ```
|
|
|
|
+ /// =========================
|
|
|
|
+ /// RSA PublicKey Structure
|
|
|
|
+ /// =========================
|
|
|
|
+ ///
|
|
|
|
+ /// RSAPublicKey ::= SEQUENCE {
|
|
|
|
+ /// modulus INTEGER, -- n
|
|
|
|
+ /// publicExponent INTEGER, -- e
|
|
|
|
+ /// }
|
|
|
|
+ /// ```
|
|
|
|
+ let publicDER = """
|
|
|
|
+ MIGJAoGBAMGeZvIG84vyKAATwKkKz2g+PeNaZ63rxk/zEnLGxkMCVKnUZ6jPAtzYOKM24949yIhfxYBC/bOCPwRK4wbr4YyIx3WB2v+Zcqe8pRM/BThUpNIx3K2+jbJBhAopf1GXJ3i31RuiLMh9HWhxzkVamz1KnDjCuTZguCRRHIv+r3XTAgMBAAE=
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ guard let publicDERData = Data(base64Encoded: publicDER) else {
|
|
|
|
+ XCTFail("Failed to convert base64 string into data for decoding.")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let decoded = try ASN1.Decoder.decode(data: publicDERData)
|
|
|
|
+
|
|
|
|
+ // Ensure the first node is of type Sequence
|
|
|
|
+ guard case .sequence(let nodes) = decoded else {
|
|
|
|
+ XCTFail("Expected the top level node to be a sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ensure there are two nodes within the top level sequence (our integers, n and e)
|
|
|
|
+ XCTAssertEqual(nodes.count, 2)
|
|
|
|
+
|
|
|
|
+ // Ensure that the first node within our sequence is of type Integer
|
|
|
|
+ guard case .integer(let n) = nodes[0] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ensure the second node within our sequence is of type Integer
|
|
|
|
+ guard case .integer(let e) = nodes[1] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ensure that n contains the data we expected
|
|
|
|
+ XCTAssertEqual(n.toHexString(), "00c19e66f206f38bf2280013c0a90acf683e3de35a67adebc64ff31272c6c6430254a9d467a8cf02dcd838a336e3de3dc8885fc58042fdb3823f044ae306ebe18c88c77581daff9972a7bca5133f053854a4d231dcadbe8db241840a297f51972778b7d51ba22cc87d1d6871ce455a9b3d4a9c38c2b93660b824511c8bfeaf75d3")
|
|
|
|
+
|
|
|
|
+ // Ensure that e contains the data we expected
|
|
|
|
+ XCTAssertEqual(e.toHexString(), "010001") // 65537
|
|
|
|
+
|
|
|
|
+ // Re Encode the data
|
|
|
|
+ let asn1: ASN1.Node = .sequence(nodes: [
|
|
|
|
+ .integer(data: n),
|
|
|
|
+ .integer(data: e)
|
|
|
|
+ ])
|
|
|
|
+
|
|
|
|
+ let encoded = ASN1.Encoder.encode(asn1)
|
|
|
|
+
|
|
|
|
+ // Ensure the re-encoded data matches the original exactly
|
|
|
|
+ XCTAssertEqual(Data(encoded), publicDERData)
|
|
|
|
+ XCTAssertEqual(encoded, publicDERData.bytes)
|
|
|
|
+ XCTAssertEqual(encoded.toBase64(), publicDER)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// This tests decodes an RSA Private Key from a Base64 DER Encoded representation
|
|
|
|
+ ///
|
|
|
|
+ /// This test enforces that
|
|
|
|
+ /// 1) The decoding process yeilds the expected format.
|
|
|
|
+ /// 2) The re-encoding of the data yeilds the exact same starting data.
|
|
|
|
+ func testANS1DERDecodingPrivateKey() throws {
|
|
|
|
+ /// An example of an RSA Private Key ASN1 DER Encoding
|
|
|
|
+ ///
|
|
|
|
+ /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.2)
|
|
|
|
+ /// ```
|
|
|
|
+ /// ==========================
|
|
|
|
+ /// RSA PrivateKey Structure
|
|
|
|
+ /// ==========================
|
|
|
|
+ ///
|
|
|
|
+ /// RSAPrivateKey ::= SEQUENCE {
|
|
|
|
+ /// version Version,
|
|
|
|
+ /// modulus INTEGER, -- n
|
|
|
|
+ /// publicExponent INTEGER, -- e
|
|
|
|
+ /// privateExponent INTEGER, -- d
|
|
|
|
+ /// prime1 INTEGER, -- p
|
|
|
|
+ /// prime2 INTEGER, -- q
|
|
|
|
+ /// exponent1 INTEGER, -- d mod (p-1)
|
|
|
|
+ /// exponent2 INTEGER, -- d mod (q-1)
|
|
|
|
+ /// coefficient INTEGER, -- (inverse of q) mod p
|
|
|
|
+ /// otherPrimeInfos OtherPrimeInfos OPTIONAL
|
|
|
|
+ /// }
|
|
|
|
+ /// ```
|
|
|
|
+ let privateDER = """
|
|
|
|
+ MIICXQIBAAKBgQDBnmbyBvOL8igAE8CpCs9oPj3jWmet68ZP8xJyxsZDAlSp1GeozwLc2DijNuPePciIX8WAQv2zgj8ESuMG6+GMiMd1gdr/mXKnvKUTPwU4VKTSMdytvo2yQYQKKX9Rlyd4t9UboizIfR1occ5FWps9Spw4wrk2YLgkURyL/q910wIDAQABAoGAGJkNLxZe/pqHJmtcAJ3U98NgjW/A2EGp8iJJZ7eFHKJBK0pG2RVjobb+iw3AKU3kGh9AsijQnmufoeX5rblt7/ojgpfVhS7NHsKCi8Nx7U92bNnP0RP4mogpvzGWVknUdv6jW7dX83FKgEywbNKa5CPQk1XinqXL33gNjWdOh/ECQQDjdE4kNdVwKA59ddWRShvJiOMOG8+TjE5HvcZzKQ+UMlBwbknL5tIJE7KnN9ZEfNihVmyrMAzJAfe2PCyZAip/AkEA2esFkG+ScgeVYlGrUqrqUkvzj1j6F8R+8rGvCjq2WnDL8TzO7NoT7qivW/+6E9osX1WwWAtj/84eN7dvLLxCrQJBAN7GomZq58MzKIYPLH9iI3cwAJtn99ZfHKi9oipW9DBFW23TR6pTSDKlvVx0nwNzeEYFPOgqZstVhwZRR6kRawcCQHx/u0QTmjUvg/cR9bFbGFhAMDxbdzaQ+n4paXmMpZXyD3IZbZb/2JdnJBiJd4PUB7nHuOH0UANbfQQT9p42SFkCQQCcdFRTZEZv5TjmcUn0GBUzRmnswiRc1YEg81DSDlvD3dEIVSl6PLkzcNNItrgD5SfC5MxCv6PIUlJVhnkavEjS
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ guard let privateDERData = Data(base64Encoded: privateDER) else {
|
|
|
|
+ XCTFail("Failed to convert base64 string into data for decoding.")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let decoded = try ASN1.Decoder.decode(data: privateDERData)
|
|
|
|
+
|
|
|
|
+ // Ensure the first Node is of type SEQUENCE
|
|
|
|
+ guard case .sequence(let nodes) = decoded else {
|
|
|
|
+ XCTFail("Expected the top level node to be a sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ensure there are nine (9) Nodes within the top level SEQUENCE
|
|
|
|
+ XCTAssertEqual(nodes.count, 9)
|
|
|
|
+
|
|
|
|
+ // Deconstruct the parameters
|
|
|
|
+ guard case .integer(let version) = nodes[0] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(version, Data(hex: "00"))
|
|
|
|
+
|
|
|
|
+ guard case .integer(let n) = nodes[1] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(n, Data(hex: "00c19e66f206f38bf2280013c0a90acf683e3de35a67adebc64ff31272c6c6430254a9d467a8cf02dcd838a336e3de3dc8885fc58042fdb3823f044ae306ebe18c88c77581daff9972a7bca5133f053854a4d231dcadbe8db241840a297f51972778b7d51ba22cc87d1d6871ce455a9b3d4a9c38c2b93660b824511c8bfeaf75d3"))
|
|
|
|
+
|
|
|
|
+ guard case .integer(let e) = nodes[2] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(e, Data(hex: "010001"))
|
|
|
|
+
|
|
|
|
+ guard case .integer(let d) = nodes[3] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(d, Data(hex: "18990d2f165efe9a87266b5c009dd4f7c3608d6fc0d841a9f2224967b7851ca2412b4a46d91563a1b6fe8b0dc0294de41a1f40b228d09e6b9fa1e5f9adb96deffa238297d5852ecd1ec2828bc371ed4f766cd9cfd113f89a8829bf31965649d476fea35bb757f3714a804cb06cd29ae423d09355e29ea5cbdf780d8d674e87f1"))
|
|
|
|
+
|
|
|
|
+ guard case .integer(let p) = nodes[4] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(p, Data(hex: "00e3744e2435d570280e7d75d5914a1bc988e30e1bcf938c4e47bdc673290f943250706e49cbe6d20913b2a737d6447cd8a1566cab300cc901f7b63c2c99022a7f"))
|
|
|
|
+
|
|
|
|
+ guard case .integer(let q) = nodes[5] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(q, Data(hex: "00d9eb05906f927207956251ab52aaea524bf38f58fa17c47ef2b1af0a3ab65a70cbf13cceecda13eea8af5bffba13da2c5f55b0580b63ffce1e37b76f2cbc42ad"))
|
|
|
|
+
|
|
|
|
+ guard case .integer(let exp1) = nodes[6] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(exp1, Data(hex: "00dec6a2666ae7c33328860f2c7f62237730009b67f7d65f1ca8bda22a56f430455b6dd347aa534832a5bd5c749f03737846053ce82a66cb5587065147a9116b07"))
|
|
|
|
+
|
|
|
|
+ guard case .integer(let exp2) = nodes[7] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(exp2, Data(hex: "7c7fbb44139a352f83f711f5b15b185840303c5b773690fa7e2969798ca595f20f72196d96ffd897672418897783d407b9c7b8e1f450035b7d0413f69e364859"))
|
|
|
|
+
|
|
|
|
+ guard case .integer(let coefficient) = nodes[8] else {
|
|
|
|
+ XCTFail("Expected an integer within our sequence and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ XCTAssertEqual(coefficient, Data(hex: "009c74545364466fe538e67149f41815334669ecc2245cd58120f350d20e5bc3ddd10855297a3cb93370d348b6b803e527c2e4cc42bfa3c852525586791abc48d2"))
|
|
|
|
+
|
|
|
|
+ // Ensure re-encoding the data yeilds the exact same starting data
|
|
|
|
+ let asn: ASN1.Node = .sequence(nodes: [
|
|
|
|
+ .integer(data: version),
|
|
|
|
+ .integer(data: n),
|
|
|
|
+ .integer(data: e),
|
|
|
|
+ .integer(data: d),
|
|
|
|
+ .integer(data: p),
|
|
|
|
+ .integer(data: q),
|
|
|
|
+ .integer(data: exp1),
|
|
|
|
+ .integer(data: exp2),
|
|
|
|
+ .integer(data: coefficient)
|
|
|
|
+ ])
|
|
|
|
+
|
|
|
|
+ // Encode the ASN1 Nodes
|
|
|
|
+ let encodedData = ASN1.Encoder.encode(asn)
|
|
|
|
+
|
|
|
|
+ // Ensure the re-encoded data matches the original data exactly
|
|
|
|
+ XCTAssertEqual(Data(encodedData), privateDERData)
|
|
|
|
+ XCTAssertEqual(encodedData, privateDERData.bytes)
|
|
|
|
+ XCTAssertEqual(encodedData.toBase64(), privateDER.replacingOccurrences(of: "\n", with: ""))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// This tests decodes an Encrypted RSA Private Key from a Base64 PEM Encoded representation
|
|
|
|
+ ///
|
|
|
|
+ /// This test enforces that
|
|
|
|
+ /// 1) The decoding process yeilds the expected format.
|
|
|
|
+ /// 2) The re-encoding of the data yeilds the exact same starting data.
|
|
|
|
+ func testASN1DecodingEncryptedPEM() throws {
|
|
|
|
+ /// ==========================
|
|
|
|
+ /// Encrypted PEM Structure
|
|
|
|
+ /// ==========================
|
|
|
|
+ ///
|
|
|
|
+ /// EncryptedPrivateKey ::= SEQUENCE {
|
|
|
|
+ ///
|
|
|
|
+ /// PEMStructure ::= SEQUENCE {
|
|
|
|
+ /// pemObjID OBJECTIDENTIFIER
|
|
|
|
+ ///
|
|
|
|
+ /// EncryptionAlgorithms ::= SEQUENCE {
|
|
|
|
+ ///
|
|
|
|
+ /// PBKDFAlgorithms ::= SEQUENCE {
|
|
|
|
+ /// pbkdfObjID OBJECTIDENTIFIER
|
|
|
|
+ /// PBKDFParams ::= SEQUENCE {
|
|
|
|
+ /// salt OCTETSTRING,
|
|
|
|
+ /// iterations INTEGER
|
|
|
|
+ /// }
|
|
|
|
+ /// },
|
|
|
|
+ ///
|
|
|
|
+ /// CIPHERAlgorithms ::= SEQUENCE {
|
|
|
|
+ /// cipherObjID OBJECTIDENTIFIER,
|
|
|
|
+ /// iv OCTETSTRING
|
|
|
|
+ /// }
|
|
|
|
+ /// }
|
|
|
|
+ /// }
|
|
|
|
+ /// encryptedData OCTETSTRING
|
|
|
|
+ /// }
|
|
|
|
+ ///
|
|
|
|
+ /// /*
|
|
|
|
+ /// * Generated with
|
|
|
|
+ /// * openssl genpkey -algorithm RSA
|
|
|
|
+ /// * -pkeyopt rsa_keygen_bits:1024
|
|
|
|
+ /// * -pkeyopt rsa_keygen_pubexp:65537
|
|
|
|
+ /// * -out foo.pem
|
|
|
|
+ /// * openssl pkcs8 -in foo.pem -topk8 -v2 aes-128-cbc -passout pass:mypassword
|
|
|
|
+ /// */
|
|
|
|
+ let encryptedPEMFormat = """
|
|
|
|
+ MIICzzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIP5QK2RfqUl4CAggA
|
|
|
|
+ MB0GCWCGSAFlAwQBAgQQj3OyM9gnW2dd/eRHkxjGrgSCAoCpM5GZB0v27cxzZsGc
|
|
|
|
+ O4/xqgwB0c/bSJ6QogtYU2KVoc7ZNQ5q9jtzn3I4ONvneOkpm9arzYz0FWnJi2C3
|
|
|
|
+ BPiF0D1NkfvjvMLv56bwiG2A1oBECacyAb2pXYeJY7SdtYKvcbgs3jx65uCm6TF2
|
|
|
|
+ BylteH+n1ewTQN9DLfASp1n81Ajq9lQGaK03SN2MUtcAPp7N9gnxJrlmDGeqlPRs
|
|
|
|
+ KpQYRcot+kE6Ew8a5jAr7mAxwpqvr3SM4dMvADZmRQsM4Uc/9+YMUdI52DG87EWc
|
|
|
|
+ 0OUB+fnQ8jw4DZgOE9KKM5/QTWc3aEw/dzXr/YJsrv01oLazhqVHnEMG0Nfr0+DP
|
|
|
|
+ q+qac1AsCsOb71VxaRlRZcVEkEfAq3gidSPD93qmlDrCnmLYTilcLanXUepda7ez
|
|
|
|
+ qhjkHtpwBLN5xRZxOn3oUuLGjk8VRwfmFX+RIMYCyihjdmbEDYpNUVkQVYFGi/F/
|
|
|
|
+ 1hxOyl9yhGdL0hb9pKHH10GGIgoqo4jSTLlb4ennihGMHCjehAjLdx/GKJkOWShy
|
|
|
|
+ V9hj8rAuYnRNb+tUW7ChXm1nLq14x9x1tX0ciVVn3ap/NoMkbFTr8M3pJ4bQlpAn
|
|
|
|
+ wCT2erYqwQtgSpOJcrFeph9TjIrNRVE7Zlmr7vayJrB/8/oPssVdhf82TXkna4fB
|
|
|
|
+ PcmO0YWLa117rfdeNM/Duy0ThSdTl39Qd+4FxqRZiHjbt+l0iSa/nOjTv1TZ/QqF
|
|
|
|
+ wqrO6EtcM45fbFJ1Y79o2ptC2D6MB4HKJq9WCt064/8zQCVx3XPbb3X8Z5o/6koy
|
|
|
|
+ ePGbz+UtSb9xczvqpRCOiFLh2MG1dUgWuHazjOtUcVWvilKnkjCMzZ9s1qG0sUDj
|
|
|
|
+ nPyn
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ guard let pemData = Data(base64Encoded: encryptedPEMFormat.replacingOccurrences(of: "\n", with: "")) else {
|
|
|
|
+ XCTFail("Failed to convert base64 string to data")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let asn = try ASN1.Decoder.decode(data: pemData)
|
|
|
|
+
|
|
|
|
+ // Ensure the first node is a Sequence
|
|
|
|
+ guard case .sequence(let encryptedPEMWrapper) = asn else {
|
|
|
|
+ XCTFail("Expected top level Node to be a SEQUENCE object")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ // Ensure the first Sequence contains exactly 2 nodes (another Sequence and our OctetString of encrypted data)
|
|
|
|
+ XCTAssertEqual(encryptedPEMWrapper.count, 2)
|
|
|
|
+
|
|
|
|
+ // Ensure the first node within the top level sequence is another sequence
|
|
|
|
+ guard case .sequence(let encryptionInfoWrapper) = encryptedPEMWrapper[0] else {
|
|
|
|
+ XCTFail("Expected the first Node within our top level SEQUENCE to be a SEQUENCE object and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ // Ensure this sequence contains exactly two nodes (the PEMs ObjectIdentifier and another sequence that houses the encryption algorithms)
|
|
|
|
+ XCTAssertEqual(encryptionInfoWrapper.count, 2)
|
|
|
|
+
|
|
|
|
+ // Ensure the first Node is the PEM's OBJECTIDENTIFIER Node
|
|
|
|
+ guard case .objectIdentifier(let pemObjID) = encryptionInfoWrapper[0] else {
|
|
|
|
+ XCTFail("Expected an OBJECTIDENTIFIER and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ensure the second Node is another SEQUENCE Node
|
|
|
|
+ guard case .sequence(let encryptionAlgorithmsWrapper) = encryptionInfoWrapper[1] else {
|
|
|
|
+ XCTFail("Expected another SEQUENCE Node and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ // Ensure this sequence contains exactly two nodes (the PEMs ObjectIdentifier and another sequence that houses the encryption algorithms)
|
|
|
|
+ XCTAssertEqual(encryptionAlgorithmsWrapper.count, 2)
|
|
|
|
+
|
|
|
|
+ // Ensure the first Node is another SEQUENCE Node
|
|
|
|
+ guard case .sequence(let pbkdfAlgorithmWrapper) = encryptionAlgorithmsWrapper[0] else {
|
|
|
|
+ XCTFail("Expected another SEQUENCE Node and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ // Ensure this sequence contains exactly two nodes (the PBKDF ObjectIdentifier and another sequence that houses the PBKDF params)
|
|
|
|
+ XCTAssertEqual(pbkdfAlgorithmWrapper.count, 2)
|
|
|
|
+
|
|
|
|
+ guard case .objectIdentifier(let pbkdfObjID) = pbkdfAlgorithmWrapper[0] else {
|
|
|
|
+ XCTFail("Expected an OBJECTIDENTIFIER and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ guard case .sequence(let pbkdfParamsWrapper) = pbkdfAlgorithmWrapper[1] else {
|
|
|
|
+ XCTFail("Expected an OCTETSTRING and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ // Ensure this sequence contains exactly two nodes (the PBKDF Salt as an OCTETSTRING and the PBKDF Iterations as an INTEGER)
|
|
|
|
+ XCTAssertEqual(pbkdfParamsWrapper.count, 2)
|
|
|
|
+ guard case .octetString(let pbkdfSalt) = pbkdfParamsWrapper[0] else {
|
|
|
|
+ XCTFail("Expected an OCTETSTRING and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ guard case .integer(let pbkdfIterations) = pbkdfParamsWrapper[1] else {
|
|
|
|
+ XCTFail("Expected an INTEGER and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ensure the second Node is another SEQUENCE Node
|
|
|
|
+ guard case .sequence(let cipherAlgorithmWrapper) = encryptionAlgorithmsWrapper[1] else {
|
|
|
|
+ XCTFail("Expected another SEQUENCE Node and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ // Ensure this sequence contains exactly two nodes (the CIPHER ObjectIdentifier and an OCTETSTRING that contains the CIPHERs InitialVector)
|
|
|
|
+ XCTAssertEqual(cipherAlgorithmWrapper.count, 2)
|
|
|
|
+
|
|
|
|
+ guard case .objectIdentifier(let cipherObjID) = cipherAlgorithmWrapper[0] else {
|
|
|
|
+ XCTFail("Expected an OBJECTIDENTIFIER and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ guard case .octetString(let cipherInitialVector) = cipherAlgorithmWrapper[1] else {
|
|
|
|
+ XCTFail("Expected an OCTETSTRING and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ensure the last (2nd) Node in the top level SEQUENCE Node is an OCTETSTRING that contains the encrypted key data
|
|
|
|
+ guard case .octetString(let encryptedData) = encryptedPEMWrapper[1] else {
|
|
|
|
+ XCTFail("Expected the last Node in the first SEQUENCE Node to be an OCTETSTRING and it wasn't")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Now lets ensure we can re encode the object and get back the exact same data
|
|
|
|
+ let nodes: ASN1.Node = .sequence(nodes: [
|
|
|
|
+ .sequence(nodes: [
|
|
|
|
+ .objectIdentifier(data: pemObjID),
|
|
|
|
+ .sequence(nodes: [
|
|
|
|
+ .sequence(nodes: [
|
|
|
|
+ .objectIdentifier(data: pbkdfObjID),
|
|
|
|
+ .sequence(nodes: [
|
|
|
|
+ .octetString(data: pbkdfSalt),
|
|
|
|
+ .integer(data: pbkdfIterations)
|
|
|
|
+ ])
|
|
|
|
+ ]),
|
|
|
|
+ .sequence(nodes: [
|
|
|
|
+ .objectIdentifier(data: cipherObjID),
|
|
|
|
+ .octetString(data: cipherInitialVector)
|
|
|
|
+ ])
|
|
|
|
+ ])
|
|
|
|
+ ]),
|
|
|
|
+ .octetString(data: encryptedData)
|
|
|
|
+ ])
|
|
|
|
+
|
|
|
|
+ // Encode the ASN1 Nodes
|
|
|
|
+ let encodedData = ASN1.Encoder.encode(nodes)
|
|
|
|
+
|
|
|
|
+ // Ensure the re-encoded data matches the original data exactly
|
|
|
|
+ XCTAssertEqual(Data(encodedData), pemData)
|
|
|
|
+ XCTAssertEqual(encodedData, pemData.bytes)
|
|
|
|
+ XCTAssertEqual(encodedData.toBase64(), encryptedPEMFormat.replacingOccurrences(of: "\n", with: ""))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static let allTests = [
|
|
|
|
+ ("testASN1NonDestructiveEncoding", testASN1NonDestructiveEncoding),
|
|
|
|
+ ("testASN1DecodePrimitiveInteger", testASN1DecodePrimitiveInteger),
|
|
|
|
+ ("testASN1DecodeLargeBigInteger", testASN1DecodeLargeBigInteger),
|
|
|
|
+ ("testANS1DERDecodingPublicKey", testANS1DERDecodingPublicKey),
|
|
|
|
+ ("testANS1DERDecodingPrivateKey", testANS1DERDecodingPrivateKey),
|
|
|
|
+ ("testASN1DecodingEncryptedPEM", testASN1DecodingEncryptedPEM)
|
|
|
|
+ ]
|
|
|
|
+}
|