blob: 8aaaf3a09616718cf269381964e5dfc3348ab9a5 [file] [log] [blame]
/*
* Copyright (C) 2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import <Security/SecAsn1Coder.h>
#import <Security/SecAsn1Templates.h>
#import <WebCore/LocalizedStrings.h>
#import <WebCore/SSLKeyGenerator.h>
#import <wtf/MainThread.h>
#import <wtf/Scope.h>
#import <wtf/URL.h>
#import <wtf/spi/cocoa/SecuritySPI.h>
#import <wtf/text/Base64.h>
#if USE(APPLE_INTERNAL_SDK)
#import <Security/SecKeyPriv.h>
#else
extern const SecKeyAlgorithm kSecKeyAlgorithmRSASignatureMessagePKCS1v15MD5;
#endif
namespace TestWebKitAPI {
struct PublicKeyAndChallenge {
SecAsn1PubKeyInfo subjectPublicKeyInfo;
SecAsn1Item challenge;
};
struct SignedPublicKeyAndChallenge {
PublicKeyAndChallenge publicKeyAndChallenge;
SecAsn1AlgId algorithmIdentifier;
SecAsn1Item signature;
};
const SecAsn1Template publicKeyAndChallengeTemplate[] {
{ SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(PublicKeyAndChallenge) },
{ SEC_ASN1_INLINE, offsetof(PublicKeyAndChallenge, subjectPublicKeyInfo), kSecAsn1SubjectPublicKeyInfoTemplate, 0},
{ SEC_ASN1_INLINE, offsetof(PublicKeyAndChallenge, challenge), kSecAsn1IA5StringTemplate, 0 },
{ 0, 0, 0, 0}
};
const SecAsn1Template signedPublicKeyAndChallengeTemplate[] {
{ SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(SignedPublicKeyAndChallenge) },
{ SEC_ASN1_INLINE, offsetof(SignedPublicKeyAndChallenge, publicKeyAndChallenge), publicKeyAndChallengeTemplate, 0 },
{ SEC_ASN1_INLINE, offsetof(SignedPublicKeyAndChallenge, algorithmIdentifier), kSecAsn1AlgorithmIDTemplate, 0 },
{ SEC_ASN1_BIT_STRING, offsetof(SignedPublicKeyAndChallenge, signature), 0, 0 },
{ 0, 0, 0, 0 }
};
const URL url { "http://www.webkit.org/"_str };
class SSLKeyGeneratorTest : public testing::Test {
public:
virtual void SetUp()
{
WTF::initializeMainThread();
}
virtual void TearDown()
{
SecItemDelete((__bridge CFDictionaryRef) @{
(id)kSecClass: (id)kSecClassKey,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
(id)kSecAttrLabel: WebCore::keygenKeychainItemName(url.host().toString()),
});
SecItemDelete((__bridge CFDictionaryRef) @{
(id)kSecClass: (id)kSecClassKey,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPublic,
(id)kSecAttrLabel: WebCore::keygenKeychainItemName(url.host().toString()),
});
}
};
TEST_F(SSLKeyGeneratorTest, DefaultTest)
{
constexpr auto challenge = "0123456789"_s;
auto rawResult = WebCore::signedPublicKeyAndChallengeString(0, challenge, url);
ASSERT_FALSE(rawResult.isEmpty());
auto derResult = base64Decode(rawResult);
ASSERT_TRUE(derResult);
SecAsn1CoderRef coder = nullptr;
ASSERT_EQ(errSecSuccess, SecAsn1CoderCreate(&coder));
auto releaseCoder = makeScopeExit([&coder] {
SecAsn1CoderRelease(coder);
});
SignedPublicKeyAndChallenge decodedResult { };
SecAsn1Item derResultItem { derResult->size(), derResult->data() };
ASSERT_EQ(errSecSuccess, SecAsn1DecodeData(coder, &derResultItem, signedPublicKeyAndChallengeTemplate, &decodedResult));
// Check challenge
EXPECT_FALSE(memcmp(challenge, decodedResult.publicKeyAndChallenge.challenge.Data, sizeof(challenge)));
// Check signature
RetainPtr<SecKeyRef> publicKey = nullptr;
{
NSDictionary* options = @{
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPublic,
(id)kSecAttrKeySizeInBits: @2048,
};
CFErrorRef errorRef = nullptr;
publicKey = adoptCF(SecKeyCreateWithData(
adoptCF(CFDataCreate(NULL, decodedResult.publicKeyAndChallenge.subjectPublicKeyInfo.subjectPublicKey.Data, decodedResult.publicKeyAndChallenge.subjectPublicKeyInfo.subjectPublicKey.Length)).get(),
(__bridge CFDictionaryRef)options,
&errorRef
));
ASSERT_FALSE(errorRef);
}
SecAsn1Item dataToVerify { 0, nullptr };
ASSERT_EQ(errSecSuccess, SecAsn1EncodeItem(coder, &decodedResult.publicKeyAndChallenge, publicKeyAndChallengeTemplate, &dataToVerify));
// Signature's Length is in bits, we need it in bytes.
EXPECT_TRUE(SecKeyVerifySignature(publicKey.get(), kSecKeyAlgorithmRSASignatureMessagePKCS1v15MD5, adoptCF(CFDataCreate(NULL, dataToVerify.Data, dataToVerify.Length)).get(), adoptCF(CFDataCreate(NULL, decodedResult.signature.Data, decodedResult.signature.Length / 8)).get(), NULL));
// Check OIDs
EXPECT_FALSE(memcmp(oidMd5Rsa.data, decodedResult.algorithmIdentifier.algorithm.Data, oidMd5Rsa.length));
EXPECT_FALSE(memcmp(oidRsa.data, decodedResult.publicKeyAndChallenge.subjectPublicKeyInfo.algorithm.algorithm.Data, oidRsa.length));
}
} // namespace TestWebKitAPI