// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CRYPTO_MOCK_KEYCHAIN_MAC_H_
#define CRYPTO_MOCK_KEYCHAIN_MAC_H_
#include <stdint.h>
#include <map>
#include <set>
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "crypto/apple_keychain.h"
namespace crypto {
// Mock Keychain wrapper for testing code that interacts with the OS X
// Keychain. Implemented by storing SecKeychainAttributeList and
// KeychainPasswordData values in separate mutable containers and
// mapping them to integer keys.
//
// Note that "const" is pretty much meaningless for this class; the const-ness
// of AppleKeychain doesn't apply to the actual keychain data, so all of the
// Mock data is mutable; don't assume that it won't change over the life of
// tests.
class CRYPTO_EXPORT MockAppleKeychain : public AppleKeychain {
public:
MockAppleKeychain();
virtual ~MockAppleKeychain();
// AppleKeychain implementation.
virtual OSStatus FindGenericPassword(
CFTypeRef keychainOrArray,
UInt32 serviceNameLength,
const char* serviceName,
UInt32 accountNameLength,
const char* accountName,
UInt32* passwordLength,
void** passwordData,
SecKeychainItemRef* itemRef) const OVERRIDE;
virtual OSStatus ItemFreeContent(SecKeychainAttributeList* attrList,
void* data) const OVERRIDE;
virtual OSStatus AddGenericPassword(
SecKeychainRef keychain,
UInt32 serviceNameLength,
const char* serviceName,
UInt32 accountNameLength,
const char* accountName,
UInt32 passwordLength,
const void* passwordData,
SecKeychainItemRef* itemRef) const OVERRIDE;
#if !defined(OS_IOS)
virtual OSStatus ItemCopyAttributesAndData(
SecKeychainItemRef itemRef,
SecKeychainAttributeInfo* info,
SecItemClass* itemClass,
SecKeychainAttributeList** attrList,
UInt32* length,
void** outData) const OVERRIDE;
// Pass "fail_me" as the data to get errSecAuthFailed.
virtual OSStatus ItemModifyAttributesAndData(
SecKeychainItemRef itemRef,
const SecKeychainAttributeList* attrList,
UInt32 length,
const void* data) const OVERRIDE;
virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList* attrList,
void* data) const OVERRIDE;
virtual OSStatus ItemDelete(SecKeychainItemRef itemRef) const OVERRIDE;
virtual OSStatus SearchCreateFromAttributes(
CFTypeRef keychainOrArray,
SecItemClass itemClass,
const SecKeychainAttributeList* attrList,
SecKeychainSearchRef* searchRef) const OVERRIDE;
virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef,
SecKeychainItemRef* itemRef) const OVERRIDE;
// Pass "some.domain.com" as the serverName to get errSecDuplicateItem.
virtual OSStatus AddInternetPassword(
SecKeychainRef keychain,
UInt32 serverNameLength,
const char* serverName,
UInt32 securityDomainLength,
const char* securityDomain,
UInt32 accountNameLength,
const char* accountName,
UInt32 pathLength, const char* path,
UInt16 port, SecProtocolType protocol,
SecAuthenticationType authenticationType,
UInt32 passwordLength,
const void* passwordData,
SecKeychainItemRef* itemRef) const OVERRIDE;
virtual void Free(CFTypeRef ref) const OVERRIDE;
// Return the counts of objects returned by Create/Copy functions but never
// Free'd as they should have been.
int UnfreedSearchCount() const;
int UnfreedKeychainItemCount() const;
int UnfreedAttributeDataCount() const;
// Returns true if all items added with AddInternetPassword have a creator
// code set.
bool CreatorCodesSetForAddedItems() const;
struct KeychainTestData {
const SecAuthenticationType auth_type;
const char* server;
const SecProtocolType protocol;
const char* path;
const UInt32 port;
const char* security_domain;
const char* creation_date;
const char* username;
const char* password;
const bool negative_item;
};
// Adds a keychain item with the given info to the test set.
void AddTestItem(const KeychainTestData& item_data);
#endif // !defined(OS_IOS)
// |FindGenericPassword()| can return different results depending on user
// interaction with the system Keychain. For mocking purposes we allow the
// user of this class to specify the result code of the
// |FindGenericPassword()| call so we can simulate the result of different
// user interactions.
void set_find_generic_result(OSStatus result) {
find_generic_result_ = result;
}
// Returns the true if |AddGenericPassword()| was called.
bool called_add_generic() const { return called_add_generic_; }
// Returns the value of the password set when |AddGenericPassword()| was
// called.
std::string add_generic_password() const { return add_generic_password_; }
// Returns the number of allocations - deallocations for password data.
int password_data_count() const { return password_data_count_; }
private:
// Type used for the keys in the std::map(s) and MockAppleKeychain items.
typedef uintptr_t MockKeychainItemType;
// Type of the map holding the mock keychain attributes.
typedef std::map<MockKeychainItemType, SecKeychainAttributeList>
MockKeychainAttributesMap;
#if !defined(OS_IOS)
// Returns true if the keychain already contains a password that matches the
// attributes provided.
bool AlreadyContainsInternetPassword(
UInt32 serverNameLength,
const char* serverName,
UInt32 securityDomainLength,
const char* securityDomain,
UInt32 accountNameLength,
const char* accountName,
UInt32 pathLength,
const char* path,
UInt16 port,
SecProtocolType protocol,
SecAuthenticationType authenticationType) const;
// Initializes storage for keychain data at |key|.
void InitializeKeychainData(MockKeychainItemType key) const;
// Sets the data and length of |tag| in the item-th test item.
void SetTestDataBytes(
MockKeychainItemType item,
UInt32 tag,
const void* data,
size_t length);
// Sets the data and length of |tag| in the item-th test item based on
// |value|. The null-terminator will not be included; the Keychain Services
// docs don't indicate whether it is or not, so clients should not assume
// that it will be.
void SetTestDataString(MockKeychainItemType item,
UInt32 tag,
const char* value);
// Sets the data of the corresponding attribute of the item-th test item to
// |value|. Assumes that the space has alread been allocated, and the length
// set.
void SetTestDataPort(MockKeychainItemType item, UInt32 value);
void SetTestDataProtocol(MockKeychainItemType item, SecProtocolType value);
void SetTestDataAuthType(MockKeychainItemType item,
SecAuthenticationType value);
void SetTestDataNegativeItem(MockKeychainItemType item, Boolean value);
void SetTestDataCreator(MockKeychainItemType item, OSType value);
// Sets the password data and length for the item-th test item.
void SetTestDataPasswordBytes(MockKeychainItemType item,
const void* data,
size_t length);
// Sets the password for the item-th test item. As with SetTestDataString,
// the data will not be null-terminated.
void SetTestDataPasswordString(MockKeychainItemType item, const char* value);
// Returns the address of the attribute in attribute_list with tag |tag|.
static SecKeychainAttribute* AttributeWithTag(
const SecKeychainAttributeList& attribute_list,
UInt32 tag);
static const SecKeychainSearchRef kDummySearchRef;
typedef struct KeychainPasswordData {
KeychainPasswordData() : data(NULL), length(0) {}
void* data;
UInt32 length;
} KeychainPasswordData;
// Mutable because the MockAppleKeychain API requires its internal keychain
// storage to be modifiable by users of this class.
mutable MockKeychainAttributesMap keychain_attr_list_;
mutable std::map<MockKeychainItemType,
KeychainPasswordData> keychain_data_;
mutable MockKeychainItemType next_item_key_;
// Tracks the items that should be returned in subsequent calls to
// SearchCopyNext, based on the last call to SearchCreateFromAttributes.
// We can't handle multiple active searches, since we don't track the search
// ref we return, but we don't need to for our mocking.
mutable std::vector<MockKeychainItemType> remaining_search_results_;
// Track copies and releases to make sure they balance. Really these should
// be maps to track per item, but this should be good enough to catch
// real mistakes.
mutable int search_copy_count_;
mutable int keychain_item_copy_count_;
mutable int attribute_data_copy_count_;
// Tracks which items (by key) were added with AddInternetPassword.
mutable std::set<MockKeychainItemType> added_via_api_;
#endif // !defined(OS_IOS)
// Result code for the |FindGenericPassword()| method.
OSStatus find_generic_result_;
// Records whether |AddGenericPassword()| gets called.
mutable bool called_add_generic_;
// Tracks the allocations and frees of password data in |FindGenericPassword|
// and |ItemFreeContent|.
mutable int password_data_count_;
// Records the password being set when |AddGenericPassword()| gets called.
mutable std::string add_generic_password_;
};
} // namespace crypto
#endif // CRYPTO_MOCK_KEYCHAIN_MAC_H_