зеркало из https://github.com/mozilla/gecko-dev.git
Bug 974715 - Create more flexible OCSP response generation code. r=briansmith, r=cviecco
This commit is contained in:
Родитель
2bd5689ee7
Коммит
954d7d0bfb
|
@ -23,6 +23,12 @@ LOCAL_INCLUDES += [
|
||||||
'../insanity/include',
|
'../insanity/include',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
DIRS += [
|
||||||
|
'../insanity/test/lib',
|
||||||
|
]
|
||||||
|
|
||||||
FAIL_ON_WARNINGS = True
|
FAIL_ON_WARNINGS = True
|
||||||
|
|
||||||
|
LIBRARY_NAME = 'certverifier'
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||||
|
# Copyright 2013 Mozilla Foundation
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
UNIFIED_SOURCES += [
|
||||||
|
'pkixtestutil.cpp',
|
||||||
|
]
|
||||||
|
|
||||||
|
LIBRARY_NAME = 'pkixtestutil'
|
||||||
|
|
||||||
|
LOCAL_INCLUDES += [
|
||||||
|
'../../include',
|
||||||
|
'../../lib',
|
||||||
|
]
|
||||||
|
|
||||||
|
FAIL_ON_WARNINGS = True
|
|
@ -0,0 +1,610 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* Copyright 2013 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pkixcheck.h"
|
||||||
|
#include "pkixder.h"
|
||||||
|
#include "pkixtestutil.h"
|
||||||
|
|
||||||
|
#include "cryptohi.h"
|
||||||
|
#include "hasht.h"
|
||||||
|
#include "pk11pub.h"
|
||||||
|
#include "prinit.h"
|
||||||
|
#include "secder.h"
|
||||||
|
|
||||||
|
namespace insanity { namespace test {
|
||||||
|
|
||||||
|
class Output
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Output()
|
||||||
|
: numItems(0)
|
||||||
|
, length(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes a shallow copy of the input item. All input items must have a
|
||||||
|
// lifetime that extends at least to where Squash is called.
|
||||||
|
der::Result Add(const SECItem* item)
|
||||||
|
{
|
||||||
|
PR_ASSERT(item);
|
||||||
|
PR_ASSERT(item->data);
|
||||||
|
|
||||||
|
if (numItems >= MaxSequenceItems) {
|
||||||
|
return der::Fail(SEC_ERROR_INVALID_ARGS);
|
||||||
|
}
|
||||||
|
if (length + item->len > 65535) {
|
||||||
|
return der::Fail(SEC_ERROR_INVALID_ARGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
contents[numItems] = item;
|
||||||
|
numItems++;
|
||||||
|
length += item->len;
|
||||||
|
return der::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECItem* Squash(PLArenaPool* arena, uint8_t tag)
|
||||||
|
{
|
||||||
|
PR_ASSERT(arena);
|
||||||
|
|
||||||
|
size_t lengthLength = length < 128 ? 1
|
||||||
|
: length < 256 ? 2
|
||||||
|
: 3;
|
||||||
|
size_t totalLength = 1 + lengthLength + length;
|
||||||
|
SECItem* output = SECITEM_AllocItem(arena, nullptr, totalLength);
|
||||||
|
if (!output) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
uint8_t* d = output->data;
|
||||||
|
*d++ = tag;
|
||||||
|
EncodeLength(d, length, lengthLength);
|
||||||
|
d += lengthLength;
|
||||||
|
for (size_t i = 0; i < numItems; i++) {
|
||||||
|
memcpy(d, contents[i]->data, contents[i]->len);
|
||||||
|
d += contents[i]->len;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
EncodeLength(uint8_t* data, size_t length, size_t lengthLength)
|
||||||
|
{
|
||||||
|
switch (lengthLength) {
|
||||||
|
case 1:
|
||||||
|
data[0] = length;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
data[0] = 0x81;
|
||||||
|
data[1] = length;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
data[0] = 0x82;
|
||||||
|
data[1] = length / 256;
|
||||||
|
data[2] = length % 256;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PR_NOT_REACHED("EncodeLength: bad lengthLength");
|
||||||
|
PR_Abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const size_t MaxSequenceItems = 5;
|
||||||
|
const SECItem* contents[MaxSequenceItems];
|
||||||
|
size_t numItems;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
Output(const Output&) /* = delete */;
|
||||||
|
void operator=(const Output&) /* = delete */;
|
||||||
|
};
|
||||||
|
|
||||||
|
static SECItem* ResponseBytes(OCSPResponseContext& context);
|
||||||
|
static SECItem* BasicOCSPResponse(OCSPResponseContext& context);
|
||||||
|
static SECItem* ResponseData(OCSPResponseContext& context);
|
||||||
|
static SECItem* ResponderID(OCSPResponseContext& context);
|
||||||
|
static SECItem* KeyHash(OCSPResponseContext& context);
|
||||||
|
static SECItem* SingleResponse(OCSPResponseContext& context);
|
||||||
|
static SECItem* CertID(OCSPResponseContext& context);
|
||||||
|
static SECItem* CertStatus(OCSPResponseContext& context);
|
||||||
|
|
||||||
|
static SECItem*
|
||||||
|
EncodeNested(PLArenaPool* arena, uint8_t tag, SECItem* inner)
|
||||||
|
{
|
||||||
|
Output output;
|
||||||
|
if (output.Add(inner) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return output.Squash(arena, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A return value of 0 is an error, but this should never happen in practice
|
||||||
|
// because this function aborts in that case.
|
||||||
|
static size_t
|
||||||
|
HashAlgorithmToLength(SECOidTag hashAlg)
|
||||||
|
{
|
||||||
|
switch (hashAlg) {
|
||||||
|
case SEC_OID_SHA1:
|
||||||
|
return SHA1_LENGTH;
|
||||||
|
case SEC_OID_SHA256:
|
||||||
|
return SHA256_LENGTH;
|
||||||
|
case SEC_OID_SHA384:
|
||||||
|
return SHA384_LENGTH;
|
||||||
|
case SEC_OID_SHA512:
|
||||||
|
return SHA512_LENGTH;
|
||||||
|
default:
|
||||||
|
PR_NOT_REACHED("HashAlgorithmToLength: bad hashAlg");
|
||||||
|
PR_Abort();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SECItem*
|
||||||
|
HashedOctetString(PLArenaPool* arena, const SECItem* bytes, SECOidTag hashAlg)
|
||||||
|
{
|
||||||
|
size_t hashLen = HashAlgorithmToLength(hashAlg);
|
||||||
|
if (hashLen == 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* hashBuf = SECITEM_AllocItem(arena, nullptr, hashLen);
|
||||||
|
if (!hashBuf) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (PK11_HashBuf(hashAlg, hashBuf->data, bytes->data, bytes->len)
|
||||||
|
!= SECSuccess) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EncodeNested(arena, der::OCTET_STRING, hashBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SECItem*
|
||||||
|
KeyHashHelper(PLArenaPool* arena, const CERTCertificate* cert)
|
||||||
|
{
|
||||||
|
// We only need a shallow copy here.
|
||||||
|
SECItem spk = cert->subjectPublicKeyInfo.subjectPublicKey;
|
||||||
|
DER_ConvertBitString(&spk); // bits to bytes
|
||||||
|
return HashedOctetString(arena, &spk, SEC_OID_SHA1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SECItem*
|
||||||
|
AlgorithmIdentifier(PLArenaPool* arena, SECOidTag algTag)
|
||||||
|
{
|
||||||
|
SECAlgorithmIDStr aid;
|
||||||
|
aid.algorithm.data = nullptr;
|
||||||
|
aid.algorithm.len = 0;
|
||||||
|
aid.parameters.data = nullptr;
|
||||||
|
aid.parameters.len = 0;
|
||||||
|
if (SECOID_SetAlgorithmID(arena, &aid, algTag, nullptr) != SECSuccess) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
static const SEC_ASN1Template algorithmIDTemplate[] = {
|
||||||
|
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECAlgorithmID) },
|
||||||
|
{ SEC_ASN1_OBJECT_ID, offsetof(SECAlgorithmID, algorithm) },
|
||||||
|
{ SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, offsetof(SECAlgorithmID, parameters) },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
SECItem* algorithmID = SEC_ASN1EncodeItem(arena, nullptr, &aid,
|
||||||
|
algorithmIDTemplate);
|
||||||
|
return algorithmID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SECItem*
|
||||||
|
PRTimeToEncodedTime(PLArenaPool* arena, PRTime time)
|
||||||
|
{
|
||||||
|
SECItem derTime;
|
||||||
|
if (DER_TimeToGeneralizedTimeArena(arena, &derTime, time) != SECSuccess) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return EncodeNested(arena, der::GENERALIZED_TIME, &derTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECItem*
|
||||||
|
CreateEncodedOCSPResponse(OCSPResponseContext& context)
|
||||||
|
{
|
||||||
|
if (!context.arena || !context.cert || !context.issuerCert ||
|
||||||
|
!context.signerCert) {
|
||||||
|
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OCSPResponse ::= SEQUENCE {
|
||||||
|
// responseStatus OCSPResponseStatus,
|
||||||
|
// responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
|
||||||
|
|
||||||
|
// OCSPResponseStatus ::= ENUMERATED {
|
||||||
|
// successful (0), -- Response has valid confirmations
|
||||||
|
// malformedRequest (1), -- Illegal confirmation request
|
||||||
|
// internalError (2), -- Internal error in issuer
|
||||||
|
// tryLater (3), -- Try again later
|
||||||
|
// -- (4) is not used
|
||||||
|
// sigRequired (5), -- Must sign the request
|
||||||
|
// unauthorized (6) -- Request unauthorized
|
||||||
|
// }
|
||||||
|
SECItem* responseStatus = SECITEM_AllocItem(context.arena, nullptr, 3);
|
||||||
|
if (!responseStatus) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
responseStatus->data[0] = der::ENUMERATED;
|
||||||
|
responseStatus->data[1] = 1;
|
||||||
|
responseStatus->data[2] = context.responseStatus;
|
||||||
|
|
||||||
|
SECItem* responseBytes = ResponseBytes(context);
|
||||||
|
if (!responseBytes) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECItem* responseBytesNested = EncodeNested(context.arena,
|
||||||
|
der::CONSTRUCTED |
|
||||||
|
der::CONTEXT_SPECIFIC |
|
||||||
|
0,
|
||||||
|
responseBytes);
|
||||||
|
if (!responseBytesNested) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Output output;
|
||||||
|
if (output.Add(responseStatus) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(responseBytesNested) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return output.Squash(context.arena, der::SEQUENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseBytes ::= SEQUENCE {
|
||||||
|
// responseType OBJECT IDENTIFIER,
|
||||||
|
// response OCTET STRING }
|
||||||
|
SECItem*
|
||||||
|
ResponseBytes(OCSPResponseContext& context)
|
||||||
|
{
|
||||||
|
// Includes tag and length
|
||||||
|
static const uint8_t id_pkix_ocsp_basic_encoded[] = {
|
||||||
|
0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
|
||||||
|
};
|
||||||
|
SECItem id_pkix_ocsp_basic = {
|
||||||
|
siBuffer,
|
||||||
|
const_cast<uint8_t*>(id_pkix_ocsp_basic_encoded),
|
||||||
|
PR_ARRAY_SIZE(id_pkix_ocsp_basic_encoded)
|
||||||
|
};
|
||||||
|
SECItem* response = BasicOCSPResponse(context);
|
||||||
|
if (!response) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* responseNested = EncodeNested(context.arena, der::OCTET_STRING,
|
||||||
|
response);
|
||||||
|
if (!responseNested) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Output output;
|
||||||
|
if (output.Add(&id_pkix_ocsp_basic) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(responseNested) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return output.Squash(context.arena, der::SEQUENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BasicOCSPResponse ::= SEQUENCE {
|
||||||
|
// tbsResponseData ResponseData,
|
||||||
|
// signatureAlgorithm AlgorithmIdentifier,
|
||||||
|
// signature BIT STRING,
|
||||||
|
// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
|
||||||
|
SECItem*
|
||||||
|
BasicOCSPResponse(OCSPResponseContext& context)
|
||||||
|
{
|
||||||
|
SECItem* tbsResponseData = ResponseData(context);
|
||||||
|
if (!tbsResponseData) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pkix::ScopedPtr<SECKEYPrivateKey, SECKEY_DestroyPrivateKey> privKey(
|
||||||
|
PK11_FindKeyByAnyCert(context.signerCert.get(), nullptr));
|
||||||
|
if (!privKey) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECOidTag signatureAlgTag = SEC_GetSignatureAlgorithmOidTag(privKey->keyType,
|
||||||
|
SEC_OID_SHA1);
|
||||||
|
if (signatureAlgTag == SEC_OID_UNKNOWN) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* signatureAlgorithm = AlgorithmIdentifier(context.arena,
|
||||||
|
signatureAlgTag);
|
||||||
|
if (!signatureAlgorithm) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SEC_SignData doesn't take an arena parameter, so we have to manage
|
||||||
|
// the memory allocated in signature.
|
||||||
|
SECItem signature;
|
||||||
|
if (SEC_SignData(&signature, tbsResponseData->data, tbsResponseData->len,
|
||||||
|
privKey.get(), signatureAlgTag) != SECSuccess)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// We have to add a byte at the beginning indicating no unused bits.
|
||||||
|
// TODO: add ability to have signatures of bit length not divisible by 8,
|
||||||
|
// resulting in unused bits in the bitstring encoding
|
||||||
|
SECItem* prefixedSignature = SECITEM_AllocItem(context.arena, nullptr,
|
||||||
|
signature.len + 1);
|
||||||
|
if (!prefixedSignature) {
|
||||||
|
SECITEM_FreeItem(&signature, false);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
prefixedSignature->data[0] = 0;
|
||||||
|
memcpy(prefixedSignature->data + 1, signature.data, signature.len);
|
||||||
|
SECITEM_FreeItem(&signature, false);
|
||||||
|
if (context.badSignature) {
|
||||||
|
PR_ASSERT(prefixedSignature->len > 8);
|
||||||
|
prefixedSignature->data[8]++;
|
||||||
|
}
|
||||||
|
SECItem* signatureNested = EncodeNested(context.arena, der::BIT_STRING,
|
||||||
|
prefixedSignature);
|
||||||
|
if (!signatureNested) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bug 980538): certificates
|
||||||
|
Output output;
|
||||||
|
if (output.Add(tbsResponseData) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(signatureAlgorithm) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(signatureNested) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return output.Squash(context.arena, der::SEQUENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseData ::= SEQUENCE {
|
||||||
|
// version [0] EXPLICIT Version DEFAULT v1,
|
||||||
|
// responderID ResponderID,
|
||||||
|
// producedAt GeneralizedTime,
|
||||||
|
// responses SEQUENCE OF SingleResponse,
|
||||||
|
// responseExtensions [1] EXPLICIT Extensions OPTIONAL }
|
||||||
|
SECItem*
|
||||||
|
ResponseData(OCSPResponseContext& context)
|
||||||
|
{
|
||||||
|
SECItem* responderID = ResponderID(context);
|
||||||
|
if (!responderID) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* producedAtEncoded = PRTimeToEncodedTime(context.arena,
|
||||||
|
context.producedAt);
|
||||||
|
if (!producedAtEncoded) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* responses = SingleResponse(context);
|
||||||
|
if (!responses) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* responsesNested = EncodeNested(context.arena, der::SEQUENCE,
|
||||||
|
responses);
|
||||||
|
if (!responsesNested) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Output output;
|
||||||
|
if (output.Add(responderID) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(producedAtEncoded) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(responsesNested) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return output.Squash(context.arena, der::SEQUENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponderID ::= CHOICE {
|
||||||
|
// byName [1] Name,
|
||||||
|
// byKey [2] KeyHash }
|
||||||
|
// }
|
||||||
|
SECItem*
|
||||||
|
ResponderID(OCSPResponseContext& context)
|
||||||
|
{
|
||||||
|
SECItem* contents = nullptr;
|
||||||
|
if (context.responderIDType == OCSPResponseContext::ByName) {
|
||||||
|
contents = &context.signerCert->derSubject;
|
||||||
|
} else if (context.responderIDType == OCSPResponseContext::ByKeyHash) {
|
||||||
|
contents = KeyHash(context);
|
||||||
|
if (!contents) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EncodeNested(context.arena,
|
||||||
|
der::CONSTRUCTED |
|
||||||
|
der::CONTEXT_SPECIFIC |
|
||||||
|
context.responderIDType,
|
||||||
|
contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
|
||||||
|
// -- (i.e., the SHA-1 hash of the value of the
|
||||||
|
// -- BIT STRING subjectPublicKey [excluding
|
||||||
|
// -- the tag, length, and number of unused
|
||||||
|
// -- bits] in the responder's certificate)
|
||||||
|
SECItem*
|
||||||
|
KeyHash(OCSPResponseContext& context)
|
||||||
|
{
|
||||||
|
return KeyHashHelper(context.arena, context.signerCert.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// SingleResponse ::= SEQUENCE {
|
||||||
|
// certID CertID,
|
||||||
|
// certStatus CertStatus,
|
||||||
|
// thisUpdate GeneralizedTime,
|
||||||
|
// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
|
||||||
|
// singleExtensions [1] EXPLICIT Extensions OPTIONAL }
|
||||||
|
SECItem*
|
||||||
|
SingleResponse(OCSPResponseContext& context)
|
||||||
|
{
|
||||||
|
SECItem* certID = CertID(context);
|
||||||
|
if (!certID) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* certStatus = CertStatus(context);
|
||||||
|
if (!certStatus) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* thisUpdateEncoded = PRTimeToEncodedTime(context.arena,
|
||||||
|
context.thisUpdate);
|
||||||
|
if (!thisUpdateEncoded) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* nextUpdateEncodedNested = nullptr;
|
||||||
|
if (context.includeNextUpdate) {
|
||||||
|
SECItem* nextUpdateEncoded = PRTimeToEncodedTime(context.arena,
|
||||||
|
context.nextUpdate);
|
||||||
|
if (!nextUpdateEncoded) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
nextUpdateEncodedNested = EncodeNested(context.arena,
|
||||||
|
der::CONSTRUCTED |
|
||||||
|
der::CONTEXT_SPECIFIC |
|
||||||
|
0,
|
||||||
|
nextUpdateEncoded);
|
||||||
|
if (!nextUpdateEncodedNested) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Output output;
|
||||||
|
if (output.Add(certID) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(certStatus) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(thisUpdateEncoded) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (nextUpdateEncodedNested) {
|
||||||
|
if (output.Add(nextUpdateEncodedNested) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output.Squash(context.arena, der::SEQUENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertID ::= SEQUENCE {
|
||||||
|
// hashAlgorithm AlgorithmIdentifier,
|
||||||
|
// issuerNameHash OCTET STRING, -- Hash of issuer's DN
|
||||||
|
// issuerKeyHash OCTET STRING, -- Hash of issuer's public key
|
||||||
|
// serialNumber CertificateSerialNumber }
|
||||||
|
SECItem*
|
||||||
|
CertID(OCSPResponseContext& context)
|
||||||
|
{
|
||||||
|
SECItem* hashAlgorithm = AlgorithmIdentifier(context.arena,
|
||||||
|
context.certIDHashAlg);
|
||||||
|
if (!hashAlgorithm) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* issuerNameHash = HashedOctetString(context.arena,
|
||||||
|
&context.issuerCert->derSubject,
|
||||||
|
context.certIDHashAlg);
|
||||||
|
if (!issuerNameHash) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SECItem* issuerKeyHash = KeyHashHelper(context.arena,
|
||||||
|
context.issuerCert.get());
|
||||||
|
if (!issuerKeyHash) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
static const SEC_ASN1Template serialTemplate[] = {
|
||||||
|
{ SEC_ASN1_INTEGER, offsetof(CERTCertificate, serialNumber) },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
SECItem* serialNumber = SEC_ASN1EncodeItem(context.arena, nullptr,
|
||||||
|
context.cert.get(),
|
||||||
|
serialTemplate);
|
||||||
|
if (!serialNumber) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Output output;
|
||||||
|
if (output.Add(hashAlgorithm) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(issuerNameHash) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(issuerKeyHash) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (output.Add(serialNumber) != der::Success) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return output.Squash(context.arena, der::SEQUENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertStatus ::= CHOICE {
|
||||||
|
// good [0] IMPLICIT NULL,
|
||||||
|
// revoked [1] IMPLICIT RevokedInfo,
|
||||||
|
// unknown [2] IMPLICIT UnknownInfo }
|
||||||
|
//
|
||||||
|
// RevokedInfo ::= SEQUENCE {
|
||||||
|
// revocationTime GeneralizedTime,
|
||||||
|
// revocationReason [0] EXPLICIT CRLReason OPTIONAL }
|
||||||
|
//
|
||||||
|
// UnknownInfo ::= NULL
|
||||||
|
//
|
||||||
|
SECItem*
|
||||||
|
CertStatus(OCSPResponseContext& context)
|
||||||
|
{
|
||||||
|
switch (context.certStatus) {
|
||||||
|
// Both good and unknown are ultimately represented as NULL - the only
|
||||||
|
// difference is in the tag that identifies them.
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
SECItem* status = SECITEM_AllocItem(context.arena, nullptr, 2);
|
||||||
|
if (!status) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
status->data[0] = der::CONTEXT_SPECIFIC | context.certStatus;
|
||||||
|
status->data[1] = 0;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
SECItem* revocationTime = PRTimeToEncodedTime(context.arena,
|
||||||
|
context.revocationTime);
|
||||||
|
if (!revocationTime) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// TODO(bug 980536): add support for revocationReason
|
||||||
|
return EncodeNested(context.arena,
|
||||||
|
der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1,
|
||||||
|
revocationTime);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
PR_NOT_REACHED("CertStatus: bad context.certStatus");
|
||||||
|
PR_Abort();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} } // namespace insanity::test
|
|
@ -0,0 +1,65 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* Copyright 2013 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef insanity_test__pkixtestutils_h
|
||||||
|
#define insanity_test__pkixtestutils_h
|
||||||
|
|
||||||
|
#include "insanity/ScopedPtr.h"
|
||||||
|
#include "insanity/pkixtypes.h"
|
||||||
|
#include "seccomon.h"
|
||||||
|
|
||||||
|
namespace insanity { namespace test {
|
||||||
|
|
||||||
|
class OCSPResponseContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PLArenaPool* arena;
|
||||||
|
// TODO(bug 980538): add a way to specify what certificates are included.
|
||||||
|
pkix::ScopedCERTCertificate cert; // The subject of the OCSP response
|
||||||
|
pkix::ScopedCERTCertificate issuerCert; // The issuer of the subject
|
||||||
|
pkix::ScopedCERTCertificate signerCert; // This cert signs the response
|
||||||
|
uint8_t responseStatus; // See the OCSPResponseStatus enum in rfc 6960
|
||||||
|
// TODO(bug 979070): add ability to generate a response with no responseBytes
|
||||||
|
|
||||||
|
// The following fields are on a per-SingleResponse basis. In the future we
|
||||||
|
// may support including multiple SingleResponses per response.
|
||||||
|
PRTime producedAt;
|
||||||
|
PRTime thisUpdate;
|
||||||
|
PRTime nextUpdate;
|
||||||
|
bool includeNextUpdate;
|
||||||
|
SECOidTag certIDHashAlg;
|
||||||
|
uint8_t certStatus; // See the CertStatus choice in rfc 6960
|
||||||
|
PRTime revocationTime; // For certStatus == revoked
|
||||||
|
bool badSignature; // If true, alter the signature to fail verification
|
||||||
|
|
||||||
|
enum ResponderIDType {
|
||||||
|
ByName = 1,
|
||||||
|
ByKeyHash = 2
|
||||||
|
};
|
||||||
|
ResponderIDType responderIDType;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The return value, if non-null, is owned by the arena in the context
|
||||||
|
// and MUST NOT be freed.
|
||||||
|
// This function does its best to respect the NSPR error code convention
|
||||||
|
// (that is, if it returns null, calling PR_GetError() will return the
|
||||||
|
// error of the failed operation). However, this is not guaranteed.
|
||||||
|
SECItem* CreateEncodedOCSPResponse(OCSPResponseContext& context);
|
||||||
|
|
||||||
|
} } // namespace insanity::test
|
||||||
|
|
||||||
|
#endif // insanity_test__pkixtestutils_h
|
|
@ -44,6 +44,7 @@ const SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE = SEC_ERROR_BASE + 130;
|
||||||
const SEC_ERROR_OCSP_OLD_RESPONSE = SEC_ERROR_BASE + 132;
|
const SEC_ERROR_OCSP_OLD_RESPONSE = SEC_ERROR_BASE + 132;
|
||||||
const SEC_ERROR_OCSP_INVALID_SIGNING_CERT = SEC_ERROR_BASE + 144;
|
const SEC_ERROR_OCSP_INVALID_SIGNING_CERT = SEC_ERROR_BASE + 144;
|
||||||
const SEC_ERROR_POLICY_VALIDATION_FAILED = SEC_ERROR_BASE + 160; // -8032
|
const SEC_ERROR_POLICY_VALIDATION_FAILED = SEC_ERROR_BASE + 160; // -8032
|
||||||
|
const SEC_ERROR_OCSP_BAD_SIGNATURE = SEC_ERROR_BASE + 157;
|
||||||
const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176;
|
const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176;
|
||||||
|
|
||||||
const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12;
|
const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12;
|
||||||
|
|
|
@ -49,13 +49,9 @@ function add_tests_in_mode(useInsanity)
|
||||||
});
|
});
|
||||||
|
|
||||||
add_connection_test("ocsp-stapling-none.example.com",
|
add_connection_test("ocsp-stapling-none.example.com",
|
||||||
getXPCOMStatusFromNSS(SEC_ERROR_OCSP_INVALID_SIGNING_CERT));
|
getXPCOMStatusFromNSS(SEC_ERROR_OCSP_BAD_SIGNATURE));
|
||||||
// bug 964493 - using a cached OCSP response with a bad signature would cause
|
|
||||||
// the verification library to return a failure error code without calling
|
|
||||||
// PORT_SetError with the specific error, violating the expectations
|
|
||||||
// of the error handling code.
|
|
||||||
add_connection_test("ocsp-stapling-none.example.com",
|
add_connection_test("ocsp-stapling-none.example.com",
|
||||||
getXPCOMStatusFromNSS(SEC_ERROR_OCSP_INVALID_SIGNING_CERT));
|
getXPCOMStatusFromNSS(SEC_ERROR_OCSP_BAD_SIGNATURE));
|
||||||
add_test(function () {
|
add_test(function () {
|
||||||
// XXX(bug 915932): special case for insanity::pkix due to the temporary
|
// XXX(bug 915932): special case for insanity::pkix due to the temporary
|
||||||
// lack of an OCSP cache.
|
// lack of an OCSP cache.
|
||||||
|
|
|
@ -71,8 +71,10 @@ function add_tests_in_mode(useInsanity, certDB, otherTestCA) {
|
||||||
Ci.nsIX509CertDB.TRUSTED_SSL);
|
Ci.nsIX509CertDB.TRUSTED_SSL);
|
||||||
run_next_test();
|
run_next_test();
|
||||||
});
|
});
|
||||||
|
// TODO(bug 979055): When using ByName instead of ByKey, the error here is
|
||||||
|
// SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE. We should be testing both cases.
|
||||||
add_ocsp_test("ocsp-stapling-good-other-ca.example.com",
|
add_ocsp_test("ocsp-stapling-good-other-ca.example.com",
|
||||||
getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE),
|
getXPCOMStatusFromNSS(SEC_ERROR_OCSP_INVALID_SIGNING_CERT),
|
||||||
true);
|
true);
|
||||||
|
|
||||||
// TODO: Test the case where the signing cert can't be found at all, which
|
// TODO: Test the case where the signing cert can't be found at all, which
|
||||||
|
|
|
@ -162,5 +162,3 @@ main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ LIBS = \
|
||||||
$(NSPR_LIBS) \
|
$(NSPR_LIBS) \
|
||||||
$(NSS_LIBS) \
|
$(NSS_LIBS) \
|
||||||
$(MOZALLOC_LIB) \
|
$(MOZALLOC_LIB) \
|
||||||
|
../../../../../../certverifier/$(LIB_PREFIX)certverifier.$(LIB_SUFFIX) \
|
||||||
|
../../../../../../insanity/test/lib/$(LIB_PREFIX)pkixtestutil.$(LIB_SUFFIX) \
|
||||||
../lib/$(LIB_PREFIX)tlsserver.$(LIB_SUFFIX) \
|
../lib/$(LIB_PREFIX)tlsserver.$(LIB_SUFFIX) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "TLSServer.h"
|
|
||||||
#include "ScopedNSSTypes.h"
|
#include "ScopedNSSTypes.h"
|
||||||
|
#include "TLSServer.h"
|
||||||
|
#include "pkixtestutil.h"
|
||||||
#include "secerr.h"
|
#include "secerr.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
@ -26,181 +27,101 @@ GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aORT == ORTEmpty) {
|
||||||
|
SECItemArray* arr = SECITEM_AllocArray(aArena, nullptr, 1);
|
||||||
|
arr->items[0].data = nullptr;
|
||||||
|
arr->items[0].len = 0;
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
PRTime now = PR_Now();
|
PRTime now = PR_Now();
|
||||||
ScopedCERTOCSPCertID id(CERT_CreateOCSPCertID(aCert, now));
|
|
||||||
if (!id) {
|
|
||||||
PrintPRError("CERT_CreateOCSPCertID failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
PRTime nextUpdate = now + 10 * PR_USEC_PER_SEC;
|
|
||||||
PRTime oneDay = 60*60*24 * (PRTime)PR_USEC_PER_SEC;
|
PRTime oneDay = 60*60*24 * (PRTime)PR_USEC_PER_SEC;
|
||||||
PRTime expiredTime = now - oneDay;
|
|
||||||
PRTime oldNow = now - (8 * oneDay);
|
PRTime oldNow = now - (8 * oneDay);
|
||||||
PRTime oldNextUpdate = oldNow + 10 * PR_USEC_PER_SEC;
|
|
||||||
|
|
||||||
CERTOCSPSingleResponse *sr = nullptr;
|
insanity::test::OCSPResponseContext context;
|
||||||
switch (aORT) {
|
context.arena = aArena;
|
||||||
case ORTGood:
|
context.cert = CERT_DupCertificate(aCert);
|
||||||
case ORTGoodOtherCA:
|
context.issuerCert = nullptr;
|
||||||
case ORTBadSignature:
|
context.signerCert = nullptr;
|
||||||
sr = CERT_CreateOCSPSingleResponseGood(aArena, id, now, &nextUpdate);
|
context.responseStatus = 0;
|
||||||
if (!sr) {
|
|
||||||
PrintPRError("CERT_CreateOCSPSingleResponseGood failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
id.forget(); // owned by sr now
|
|
||||||
break;
|
|
||||||
case ORTRevoked:
|
|
||||||
sr = CERT_CreateOCSPSingleResponseRevoked(aArena, id, now, &nextUpdate,
|
|
||||||
expiredTime, nullptr);
|
|
||||||
if (!sr) {
|
|
||||||
PrintPRError("CERT_CreateOCSPSingleResponseRevoked failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
id.forget(); // owned by sr now
|
|
||||||
break;
|
|
||||||
case ORTUnknown:
|
|
||||||
sr = CERT_CreateOCSPSingleResponseUnknown(aArena, id, now, &nextUpdate);
|
|
||||||
if (!sr) {
|
|
||||||
PrintPRError("CERT_CreateOCSPSingleResponseUnknown failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
id.forget(); // owned by sr now
|
|
||||||
break;
|
|
||||||
case ORTExpired:
|
|
||||||
case ORTExpiredFreshCA:
|
|
||||||
sr = CERT_CreateOCSPSingleResponseGood(aArena, id, oldNow, &oldNextUpdate);
|
|
||||||
if (!sr) {
|
|
||||||
PrintPRError("CERT_CreateOCSPSingleResponseGood failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
id.forget(); // owned by sr now
|
|
||||||
break;
|
|
||||||
case ORTGoodOtherCert:
|
|
||||||
{
|
|
||||||
ScopedCERTCertificate otherCert(
|
|
||||||
PK11_FindCertFromNickname(aAdditionalCertName, nullptr));
|
|
||||||
if (!otherCert) {
|
|
||||||
PrintPRError("PK11_FindCertFromNickname failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedCERTOCSPCertID otherID(CERT_CreateOCSPCertID(otherCert, now));
|
context.producedAt = now;
|
||||||
if (!otherID) {
|
context.thisUpdate = now;
|
||||||
PrintPRError("CERT_CreateOCSPCertID failed");
|
context.nextUpdate = now + 10 * PR_USEC_PER_SEC;
|
||||||
return nullptr;
|
context.includeNextUpdate = true;
|
||||||
}
|
context.certIDHashAlg = SEC_OID_SHA1;
|
||||||
sr = CERT_CreateOCSPSingleResponseGood(aArena, otherID, now, &nextUpdate);
|
context.certStatus = 0;
|
||||||
if (!sr) {
|
context.revocationTime = 0;
|
||||||
PrintPRError("CERT_CreateOCSPSingleResponseGood failed");
|
context.badSignature = false;
|
||||||
return nullptr;
|
context.responderIDType = insanity::test::OCSPResponseContext::ByKeyHash;
|
||||||
}
|
|
||||||
otherID.forget(); // owned by sr now
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ORTEmpty:
|
|
||||||
case ORTMalformed:
|
|
||||||
case ORTSrverr:
|
|
||||||
case ORTTryLater:
|
|
||||||
case ORTNeedsSig:
|
|
||||||
case ORTUnauthorized:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (gDebugLevel >= DEBUG_ERRORS) {
|
|
||||||
fprintf(stderr, "bad ocsp response type: %d\n", aORT);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedCERTCertificate ca;
|
if (aORT == ORTGoodOtherCert) {
|
||||||
if (aORT == ORTGoodOtherCA) {
|
context.cert = PK11_FindCertFromNickname(aAdditionalCertName, nullptr);
|
||||||
ca = PK11_FindCertFromNickname(aAdditionalCertName, nullptr);
|
if (!context.cert) {
|
||||||
if (!ca) {
|
|
||||||
PrintPRError("PK11_FindCertFromNickname failed");
|
PrintPRError("PK11_FindCertFromNickname failed");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
} else if (aORT == ORTBadSignature) {
|
}
|
||||||
// passing in a null responderCert to CERT_CreateEncodedOCSPSuccessResponse
|
// XXX CERT_FindCertIssuer uses the old, deprecated path-building logic
|
||||||
// causes it to generate an invalid signature (by design, for testing).
|
context.issuerCert = CERT_FindCertIssuer(aCert, now, certUsageSSLCA);
|
||||||
ca = nullptr;
|
if (!context.issuerCert) {
|
||||||
} else {
|
PrintPRError("CERT_FindCertIssuer failed");
|
||||||
// XXX CERT_FindCertIssuer uses the old, deprecated path-building logic
|
return nullptr;
|
||||||
ca = CERT_FindCertIssuer(aCert, now, certUsageSSLCA);
|
}
|
||||||
if (!ca) {
|
if (aORT == ORTGoodOtherCA) {
|
||||||
PrintPRError("CERT_FindCertIssuer failed");
|
context.signerCert = PK11_FindCertFromNickname(aAdditionalCertName,
|
||||||
|
nullptr);
|
||||||
|
if (!context.signerCert) {
|
||||||
|
PrintPRError("PK11_FindCertFromNickname failed");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PRTime signTime = now;
|
|
||||||
if (aORT == ORTExpired) {
|
|
||||||
signTime = oldNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
CERTOCSPSingleResponse **responses;
|
|
||||||
SECItem *response = nullptr;
|
|
||||||
switch (aORT) {
|
switch (aORT) {
|
||||||
case ORTMalformed:
|
case ORTMalformed:
|
||||||
response = CERT_CreateEncodedOCSPErrorResponse(
|
context.responseStatus = 1;
|
||||||
aArena, SEC_ERROR_OCSP_MALFORMED_REQUEST);
|
|
||||||
if (!response) {
|
|
||||||
PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ORTSrverr:
|
case ORTSrverr:
|
||||||
response = CERT_CreateEncodedOCSPErrorResponse(
|
context.responseStatus = 2;
|
||||||
aArena, SEC_ERROR_OCSP_SERVER_ERROR);
|
|
||||||
if (!response) {
|
|
||||||
PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ORTTryLater:
|
case ORTTryLater:
|
||||||
response = CERT_CreateEncodedOCSPErrorResponse(
|
context.responseStatus = 3;
|
||||||
aArena, SEC_ERROR_OCSP_TRY_SERVER_LATER);
|
|
||||||
if (!response) {
|
|
||||||
PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ORTNeedsSig:
|
case ORTNeedsSig:
|
||||||
response = CERT_CreateEncodedOCSPErrorResponse(
|
context.responseStatus = 5;
|
||||||
aArena, SEC_ERROR_OCSP_REQUEST_NEEDS_SIG);
|
|
||||||
if (!response) {
|
|
||||||
PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ORTUnauthorized:
|
case ORTUnauthorized:
|
||||||
response = CERT_CreateEncodedOCSPErrorResponse(
|
context.responseStatus = 6;
|
||||||
aArena, SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST);
|
|
||||||
if (!response) {
|
|
||||||
PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ORTEmpty:
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// responses is contained in aArena and will be freed when aArena is
|
// context.responseStatus is 0 in all other cases, and it has
|
||||||
responses = PORT_ArenaNewArray(aArena, CERTOCSPSingleResponse *, 2);
|
// already been initialized, above.
|
||||||
if (!responses) {
|
|
||||||
PrintPRError("PORT_ArenaNewArray failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
responses[0] = sr;
|
|
||||||
responses[1] = nullptr;
|
|
||||||
response = CERT_CreateEncodedOCSPSuccessResponse(
|
|
||||||
aArena, ca, ocspResponderID_byName, signTime, responses, nullptr);
|
|
||||||
if (!response) {
|
|
||||||
PrintPRError("CERT_CreateEncodedOCSPSuccessResponse failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (aORT == ORTExpired || aORT == ORTExpiredFreshCA) {
|
||||||
|
context.thisUpdate = oldNow;
|
||||||
|
context.nextUpdate = oldNow + 10 * PR_USEC_PER_SEC;
|
||||||
|
}
|
||||||
|
if (aORT == ORTRevoked) {
|
||||||
|
context.certStatus = 1;
|
||||||
|
}
|
||||||
|
if (aORT == ORTUnknown) {
|
||||||
|
context.certStatus = 2;
|
||||||
|
}
|
||||||
|
if (aORT == ORTBadSignature) {
|
||||||
|
context.badSignature = true;
|
||||||
|
}
|
||||||
|
|
||||||
SECItemArray *arr = SECITEM_AllocArray(aArena, nullptr, 1);
|
if (!context.signerCert) {
|
||||||
|
context.signerCert = CERT_DupCertificate(context.issuerCert.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECItem* response = insanity::test::CreateEncodedOCSPResponse(context);
|
||||||
|
if (!response) {
|
||||||
|
PrintPRError("CreateEncodedOCSPResponse failed");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECItemArray* arr = SECITEM_AllocArray(aArena, nullptr, 1);
|
||||||
arr->items[0].data = response ? response->data : nullptr;
|
arr->items[0].data = response ? response->data : nullptr;
|
||||||
arr->items[0].len = response ? response->len : 0;
|
arr->items[0].len = response ? response->len : 0;
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,9 @@ UNIFIED_SOURCES += [
|
||||||
'TLSServer.cpp',
|
'TLSServer.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
LOCAL_INCLUDES += [
|
||||||
|
'../../../../../../insanity/include',
|
||||||
|
'../../../../../../insanity/test/lib',
|
||||||
|
]
|
||||||
|
|
||||||
LIBRARY_NAME = 'tlsserver'
|
LIBRARY_NAME = 'tlsserver'
|
||||||
|
|
Загрузка…
Ссылка в новой задаче