1 // Copyright (c) 2020 650 Industries, Inc. All rights reserved. 2 3 import ExpoModulesTestCore 4 5 @testable import EXUpdates 6 7 class CodeSigningConfigurationSpec : ExpoSpec { specnull8 override func spec() { 9 it("works with spearate certificate chain") { 10 let leafCert = getTestCertificate(TestCertificate.chainLeaf) 11 let intermediateCert = getTestCertificate(TestCertificate.chainIntermediate) 12 let rootCert = getTestCertificate(TestCertificate.chainRoot) 13 14 let testCert = getTestCertificate(TestCertificate.test) 15 16 let chain1 = CodeSigningConfiguration.separateCertificateChain(certificateChainInManifestResponse: leafCert) 17 expect(chain1.count) == 1 18 19 let chain2 = CodeSigningConfiguration.separateCertificateChain(certificateChainInManifestResponse: leafCert + intermediateCert) 20 expect(chain2.count) == 2 21 22 let chain3 = CodeSigningConfiguration.separateCertificateChain(certificateChainInManifestResponse: leafCert + intermediateCert + rootCert) 23 expect(chain3.count) == 3 24 25 let chainWithABunchOfNewlinesAndStuff = CodeSigningConfiguration.separateCertificateChain( 26 certificateChainInManifestResponse: testCert + "\n\n\n\n" + testCert 27 ) 28 expect(chainWithABunchOfNewlinesAndStuff.count) == 2 29 } 30 31 describe("createAcceptSignatureHeader") { 32 it("creates signature header default values") { 33 let cert = getTestCertificate(TestCertificate.test) 34 let configuration = try CodeSigningConfiguration( 35 embeddedCertificateString: cert, 36 metadata: [:], 37 includeManifestResponseCertificateChain: false, 38 allowUnsignedManifests: false 39 ) 40 let signatureHeader = configuration.createAcceptSignatureHeader() 41 expect(signatureHeader) == "sig, keyid=\"root\", alg=\"rsa-v1_5-sha256\"" 42 } 43 44 it("creates signature header values from config") { 45 let cert = getTestCertificate(TestCertificate.test) 46 let configuration = try CodeSigningConfiguration( 47 embeddedCertificateString: cert, 48 metadata: [ 49 CodeSigningMetadataFields.AlgorithmFieldKey: CodeSigningAlgorithm.RSA_SHA256.rawValue, 50 CodeSigningMetadataFields.KeyIdFieldKey: "test" 51 ], 52 includeManifestResponseCertificateChain: false, 53 allowUnsignedManifests: false 54 ) 55 let signatureHeader = configuration.createAcceptSignatureHeader() 56 expect(signatureHeader) == "sig, keyid=\"test\", alg=\"rsa-v1_5-sha256\"" 57 } 58 59 it("creates signature header escaped values") { 60 let cert = getTestCertificate(TestCertificate.test) 61 let configuration = try CodeSigningConfiguration( 62 embeddedCertificateString: cert, 63 metadata: [ 64 CodeSigningMetadataFields.AlgorithmFieldKey: CodeSigningAlgorithm.RSA_SHA256.rawValue, 65 CodeSigningMetadataFields.KeyIdFieldKey: #"test"hello\"# 66 ], 67 includeManifestResponseCertificateChain: false, 68 allowUnsignedManifests: false 69 ) 70 let signatureHeader = configuration.createAcceptSignatureHeader() 71 expect(signatureHeader) == #"sig, keyid="test\"hello\\", alg="rsa-v1_5-sha256""# 72 } 73 74 it("creates signature header throws invalid arg") { 75 let cert = getTestCertificate(TestCertificate.test) 76 expect { 77 try CodeSigningConfiguration( 78 embeddedCertificateString: cert, 79 metadata: [ 80 CodeSigningMetadataFields.AlgorithmFieldKey: "fake", 81 CodeSigningMetadataFields.KeyIdFieldKey: "test" 82 ], 83 includeManifestResponseCertificateChain: false, 84 allowUnsignedManifests: false) 85 }.to(throwError(CodeSigningError.AlgorithmParseError)) 86 } 87 } 88 89 describe("validateSignature") { 90 it("works for valid case") { 91 let cert = getTestCertificate(TestCertificate.test) 92 let configuration = try CodeSigningConfiguration( 93 embeddedCertificateString: cert, 94 metadata: [:], 95 includeManifestResponseCertificateChain: false, 96 allowUnsignedManifests: false 97 ) 98 let signatureValidationResult = try configuration.validateSignature( 99 signature: CertificateFixtures.testNewManifestBodySignature, 100 signedData: CertificateFixtures.testNewManifestBody.data(using: .utf8)!, 101 manifestResponseCertificateChain: nil 102 ) 103 expect(signatureValidationResult.validationResult) == ValidationResult.valid 104 expect(signatureValidationResult.expoProjectInformation).to(beNil()) 105 } 106 107 it("returns false when signature is invalid") { 108 let cert = getTestCertificate(TestCertificate.test) 109 let configuration = try CodeSigningConfiguration( 110 embeddedCertificateString: cert, 111 metadata: [:], 112 includeManifestResponseCertificateChain: false, 113 allowUnsignedManifests: false 114 ) 115 let signatureValidationResult = try configuration.validateSignature( 116 signature: "sig=\"aGVsbG8=\"", 117 signedData: CertificateFixtures.testNewManifestBody.data(using: .utf8)!, 118 manifestResponseCertificateChain: nil 119 ) 120 expect(signatureValidationResult.validationResult) == ValidationResult.invalid 121 expect(signatureValidationResult.expoProjectInformation).to(beNil()) 122 } 123 124 it("throws when key does not match") { 125 let cert = getTestCertificate(TestCertificate.test) 126 let configuration = try CodeSigningConfiguration( 127 embeddedCertificateString: cert, 128 metadata: [CodeSigningMetadataFields.KeyIdFieldKey: "test"], 129 includeManifestResponseCertificateChain: false, 130 allowUnsignedManifests: false 131 ) 132 expect { 133 try configuration.validateSignature( 134 signature: "sig=\"aGVsbG8=\", keyid=\"other\"", 135 signedData: CertificateFixtures.testNewManifestBody.data(using: .utf8)!, 136 manifestResponseCertificateChain: nil 137 ) 138 }.to(throwError(CodeSigningError.KeyIdMismatchError)) 139 } 140 141 it("does not use chain in manifest response if flag is false") { 142 let testCert = getTestCertificate(TestCertificate.test) 143 let leafCert = getTestCertificate(TestCertificate.chainLeaf) 144 let intermediateCert = getTestCertificate(TestCertificate.chainIntermediate) 145 let configuration = try CodeSigningConfiguration( 146 embeddedCertificateString: testCert, 147 metadata: [:], 148 includeManifestResponseCertificateChain: false, 149 allowUnsignedManifests: false 150 ) 151 let signatureValidationResult = try configuration.validateSignature( 152 signature: CertificateFixtures.testNewManifestBodySignature, 153 signedData: CertificateFixtures.testNewManifestBody.data(using: .utf8)!, 154 manifestResponseCertificateChain: leafCert + intermediateCert 155 ) 156 expect(signatureValidationResult.validationResult) == ValidationResult.valid 157 expect(signatureValidationResult.expoProjectInformation).to(beNil()) 158 } 159 160 it("does use chain in manifest response if flag is true") { 161 let leafCert = getTestCertificate(TestCertificate.chainLeaf) 162 let intermediateCert = getTestCertificate(TestCertificate.chainIntermediate) 163 let rootCert = getTestCertificate(TestCertificate.chainRoot) 164 let configuration = try CodeSigningConfiguration( 165 embeddedCertificateString: rootCert, 166 metadata: [CodeSigningMetadataFields.KeyIdFieldKey: "ca-root"], 167 includeManifestResponseCertificateChain: true, 168 allowUnsignedManifests: false 169 ) 170 let signatureValidationResult = try configuration.validateSignature( 171 signature: CertificateFixtures.testNewManifestBodyValidChainLeafSignature, 172 signedData: CertificateFixtures.testNewManifestBody.data(using: .utf8)!, 173 manifestResponseCertificateChain: leafCert + intermediateCert 174 ) 175 expect(signatureValidationResult.validationResult) == ValidationResult.valid 176 177 let expoProjectInformation = signatureValidationResult.expoProjectInformation 178 expect(expoProjectInformation?.scopeKey) == "@test/app" 179 expect(expoProjectInformation?.projectId) == "285dc9ca-a25d-4f60-93be-36dc312266d7" 180 } 181 182 it("AllowsUnsignedManifestIfAllowUnsignedFlagIsTrue") { 183 let testCert = getTestCertificate(TestCertificate.test) 184 let configuration = try CodeSigningConfiguration( 185 embeddedCertificateString: testCert, 186 metadata: [CodeSigningMetadataFields.KeyIdFieldKey: "test"], 187 includeManifestResponseCertificateChain: true, 188 allowUnsignedManifests: true 189 ) 190 let signatureValidationResult = try configuration.validateSignature( 191 signature: nil, 192 signedData: CertificateFixtures.testNewManifestBody.data(using: .utf8)!, 193 manifestResponseCertificateChain: nil 194 ) 195 expect(signatureValidationResult.validationResult) == ValidationResult.skipped 196 expect(signatureValidationResult.expoProjectInformation).to(beNil()) 197 } 198 199 it("ChecksSignedManifestIfAllowUnsignedFlagIsTrueButSignatureIsProvided") { 200 let testCert = getTestCertificate(TestCertificate.test) 201 let configuration = try CodeSigningConfiguration( 202 embeddedCertificateString: testCert, 203 metadata: [CodeSigningMetadataFields.KeyIdFieldKey: "test"], 204 includeManifestResponseCertificateChain: true, 205 allowUnsignedManifests: true 206 ) 207 let signatureValidationResult = try configuration.validateSignature( 208 signature: "sig=\"aGVsbG8=\"", 209 signedData: CertificateFixtures.testNewManifestBody.data(using: .utf8)!, 210 manifestResponseCertificateChain: nil 211 ) 212 expect(signatureValidationResult.validationResult) == ValidationResult.invalid 213 expect(signatureValidationResult.expoProjectInformation).to(beNil()) 214 } 215 } 216 } 217 } 218