summaryrefslogtreecommitdiff
path: root/gtests/mozpkix_gtest/pkixcert_signature_algorithm_tests.cpp
blob: fd1ab6c9504c0f10193189ec5b2f03d4d7460b46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

#include "pkixgtest.h"

#include "mozpkix/pkixder.h"

#include "secoid.h"

using namespace mozilla::pkix;
using namespace mozilla::pkix::test;

/* These tests generate invalid certificates on the fly, We want to test
 * validation of those certificates, not the generation, so we
 * need to temporarily allow disallowed signature policies before
 * we do the actual certificate or ocsp signing
 */
class HashAlgorithmPolicies
{
   static const int numberOfHashes = 4; /* sigh */
   static const SECOidTag hashOids[numberOfHashes];

   PRUint32 savedPolicy[numberOfHashes];

public:
   void EnableHashSignaturePolicy(void);
   void RestoreHashSignaturePolicy(void);
};

const SECOidTag HashAlgorithmPolicies::hashOids[numberOfHashes] = {
   SEC_OID_MD2,
   SEC_OID_MD4,
   SEC_OID_MD5,
   SEC_OID_SHA1 };

void
HashAlgorithmPolicies::EnableHashSignaturePolicy(void)
{
    for (int i=0;i < numberOfHashes; i++) {
        ASSERT_EQ(SECSuccess,
                  NSS_GetAlgorithmPolicy(hashOids[i], &savedPolicy[i]));
        ASSERT_EQ(SECSuccess,
                  NSS_SetAlgorithmPolicy(hashOids[i], NSS_USE_ALG_IN_SIGNATURE, 0));
    }
}

void
HashAlgorithmPolicies::RestoreHashSignaturePolicy(void)
{
    for (int i=0;i < numberOfHashes; i++) {
        ASSERT_EQ(SECSuccess,
                  NSS_SetAlgorithmPolicy(hashOids[i], savedPolicy[i],
                                        NSS_USE_ALG_IN_SIGNATURE));
    }
}

static ByteString
CreateCert(const char* issuerCN,
           const char* subjectCN,
           EndEntityOrCA endEntityOrCA,
           const TestSignatureAlgorithm& signatureAlgorithm,
           /*out*/ ByteString& subjectDER)
{
  static long serialNumberValue = 0;
  ++serialNumberValue;
  ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
  EXPECT_FALSE(ENCODING_FAILED(serialNumber));

  ByteString issuerDER(CNToDERName(issuerCN));
  EXPECT_FALSE(ENCODING_FAILED(issuerDER));
  subjectDER = CNToDERName(subjectCN);
  EXPECT_FALSE(ENCODING_FAILED(subjectDER));

  ByteString extensions[2];
  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
    extensions[0] =
      CreateEncodedBasicConstraints(true, nullptr, Critical::Yes);
    EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
  }

  ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
  HashAlgorithmPolicies policies;
  policies.EnableHashSignaturePolicy();
  ByteString certDER(CreateEncodedCertificate(v3, signatureAlgorithm,
                                              serialNumber, issuerDER,
                                              oneDayBeforeNow, oneDayAfterNow,
                                              subjectDER, *reusedKey,
                                              extensions, *reusedKey,
                                              signatureAlgorithm));
  policies.RestoreHashSignaturePolicy();
  EXPECT_FALSE(ENCODING_FAILED(certDER));
  return certDER;
}


class AlgorithmTestsTrustDomain final : public DefaultCryptoTrustDomain
{
public:
  AlgorithmTestsTrustDomain(const ByteString& aRootDER,
                            const ByteString& aRootSubjectDER,
               /*optional*/ const ByteString& aIntDER,
               /*optional*/ const ByteString& aIntSubjectDER)
    : rootDER(aRootDER)
    , rootSubjectDER(aRootSubjectDER)
    , intDER(aIntDER)
    , intSubjectDER(aIntSubjectDER)
  {
  }

private:
  Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
                      /*out*/ TrustLevel& trustLevel) override
  {
    if (InputEqualsByteString(candidateCert, rootDER)) {
      trustLevel = TrustLevel::TrustAnchor;
    } else {
      trustLevel = TrustLevel::InheritsTrust;
    }
    return Success;
  }

  Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time)
                    override
  {
    ByteString* issuerDER = nullptr;
    if (InputEqualsByteString(encodedIssuerName, rootSubjectDER)) {
      issuerDER = &rootDER;
    } else if (InputEqualsByteString(encodedIssuerName, intSubjectDER)) {
      issuerDER = &intDER;
    } else {
      // FindIssuer just returns success if it can't find a potential issuer.
      return Success;
    }
    Input issuerCert;
    Result rv = issuerCert.Init(issuerDER->data(), issuerDER->length());
    if (rv != Success) {
      return rv;
    }
    bool keepGoing;
    return checker.Check(issuerCert, nullptr, keepGoing);
  }

  Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
                         const Input*, const Input*, const Input*) override
  {
    return Success;
  }

  Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
  {
    return Success;
  }

  ByteString rootDER;
  ByteString rootSubjectDER;
  ByteString intDER;
  ByteString intSubjectDER;
};

static const TestSignatureAlgorithm NO_INTERMEDIATE
{
  TestPublicKeyAlgorithm(ByteString()),
  TestDigestAlgorithmID::MD2,
  ByteString(),
  false
};

struct ChainValidity final
{
  ChainValidity(const TestSignatureAlgorithm& aEndEntitySignatureAlgorithm,
                const TestSignatureAlgorithm& aOptionalIntSignatureAlgorithm,
                const TestSignatureAlgorithm& aRootSignatureAlgorithm,
                bool aIsValid)
    : endEntitySignatureAlgorithm(aEndEntitySignatureAlgorithm)
    , optionalIntermediateSignatureAlgorithm(aOptionalIntSignatureAlgorithm)
    , rootSignatureAlgorithm(aRootSignatureAlgorithm)
    , isValid(aIsValid)
  { }

  // In general, a certificate is generated for each of these.  However, if
  // optionalIntermediateSignatureAlgorithm is NO_INTERMEDIATE, then only 2
  // certificates are generated.
  // The certificate generated for the given rootSignatureAlgorithm is the
  // trust anchor.
  TestSignatureAlgorithm endEntitySignatureAlgorithm;
  TestSignatureAlgorithm optionalIntermediateSignatureAlgorithm;
  TestSignatureAlgorithm rootSignatureAlgorithm;
  bool isValid;
};

static const ChainValidity CHAIN_VALIDITY[] =
{
  // The trust anchor may have a signature with an unsupported signature
  // algorithm.
  ChainValidity(sha256WithRSAEncryption(),
                NO_INTERMEDIATE,
                md5WithRSAEncryption(),
                true),
  ChainValidity(sha256WithRSAEncryption(),
                NO_INTERMEDIATE,
                md2WithRSAEncryption(),
                true),

  // Certificates that are not trust anchors must not have a signature with an
  // unsupported signature algorithm.
  ChainValidity(md5WithRSAEncryption(),
                NO_INTERMEDIATE,
                sha256WithRSAEncryption(),
                false),
  ChainValidity(md2WithRSAEncryption(),
                NO_INTERMEDIATE,
                sha256WithRSAEncryption(),
                false),
  ChainValidity(md2WithRSAEncryption(),
                NO_INTERMEDIATE,
                md5WithRSAEncryption(),
                false),
  ChainValidity(sha256WithRSAEncryption(),
                md5WithRSAEncryption(),
                sha256WithRSAEncryption(),
                false),
  ChainValidity(sha256WithRSAEncryption(),
                md2WithRSAEncryption(),
                sha256WithRSAEncryption(),
                false),
  ChainValidity(sha256WithRSAEncryption(),
                md2WithRSAEncryption(),
                md5WithRSAEncryption(),
                false),
};

class pkixcert_IsValidChainForAlgorithm
  : public ::testing::Test
  , public ::testing::WithParamInterface<ChainValidity>
{
};

::std::ostream& operator<<(::std::ostream& os,
                           const pkixcert_IsValidChainForAlgorithm&)
{
  return os << "TODO (bug 1318770)";
}

::std::ostream& operator<<(::std::ostream& os, const ChainValidity&)
{
  return os << "TODO (bug 1318770)";
}

TEST_P(pkixcert_IsValidChainForAlgorithm, IsValidChainForAlgorithm)
{
  const ChainValidity& chainValidity(GetParam());
  const char* rootCN = "CN=Root";
  ByteString rootSubjectDER;
  ByteString rootEncoded(
    CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
               chainValidity.rootSignatureAlgorithm, rootSubjectDER));
  EXPECT_FALSE(ENCODING_FAILED(rootEncoded));
  EXPECT_FALSE(ENCODING_FAILED(rootSubjectDER));

  const char* issuerCN = rootCN;

  const char* intermediateCN = "CN=Intermediate";
  ByteString intermediateSubjectDER;
  ByteString intermediateEncoded;

  // If the the algorithmIdentifier is empty, then it's NO_INTERMEDIATE.
  if (!chainValidity.optionalIntermediateSignatureAlgorithm
                    .algorithmIdentifier.empty()) {
    intermediateEncoded =
      CreateCert(rootCN, intermediateCN, EndEntityOrCA::MustBeCA,
                 chainValidity.optionalIntermediateSignatureAlgorithm,
                 intermediateSubjectDER);
    EXPECT_FALSE(ENCODING_FAILED(intermediateEncoded));
    EXPECT_FALSE(ENCODING_FAILED(intermediateSubjectDER));
    issuerCN = intermediateCN;
  }

  AlgorithmTestsTrustDomain trustDomain(rootEncoded, rootSubjectDER,
                                        intermediateEncoded,
                                        intermediateSubjectDER);

  const char* endEntityCN = "CN=End Entity";
  ByteString endEntitySubjectDER;
  ByteString endEntityEncoded(
    CreateCert(issuerCN, endEntityCN, EndEntityOrCA::MustBeEndEntity,
               chainValidity.endEntitySignatureAlgorithm,
               endEntitySubjectDER));
  EXPECT_FALSE(ENCODING_FAILED(endEntityEncoded));
  EXPECT_FALSE(ENCODING_FAILED(endEntitySubjectDER));

  Input endEntity;
  ASSERT_EQ(Success, endEntity.Init(endEntityEncoded.data(),
                                    endEntityEncoded.length()));
  Result expectedResult = chainValidity.isValid
                        ? Success
                        : Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
  ASSERT_EQ(expectedResult,
            BuildCertChain(trustDomain, endEntity, Now(),
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::noParticularKeyUsageRequired,
                           KeyPurposeId::id_kp_serverAuth,
                           CertPolicyId::anyPolicy, nullptr));
}

INSTANTIATE_TEST_SUITE_P(pkixcert_IsValidChainForAlgorithm,
                        pkixcert_IsValidChainForAlgorithm,
                        testing::ValuesIn(CHAIN_VALIDITY));