Создание пользовательских преобразований

Несмотря на то, что OS X обеспечивает, много коллективной безопасности преобразовывают, можно также создать собственное. Эта глава показывает, как создать и использовать пользовательское преобразование.

Как упомянуто в предыдущих главах, преобразовывает, основываются на блоках и Grand Central Dispatch (GCD). Расписания GCD, которые каждый преобразовывает независимо, разрешение, преобразовывают для выполнения параллельно. Несмотря на то, что Вы не должны понимать GCD для записи пользовательского преобразования, действительно необходимо понять блоки. Для узнавания больше о блоках и GCD считайте Блоки Представления и Центральную Отгрузку.

Преобразовывает основываются на Базовых объектах Основы. Этот документ принимает элементарные знания того, как работают эти объекты. Для узнавания больше считайте Базовые Концепции проекта Основы.

Демонстрационное пользовательское преобразование, описанное в этой главе, CaesarXform.c, преобразование, реализующее простого Цезаря (ROT-N) шифр. Полный исходный код для этого преобразования включен в конце этой главы в Полном Листинге кода.

Объявление имени преобразования

Все преобразовывает, нуждаются в уникальном имени для идентификации его при создании нового экземпляра. Как правило необходимо использовать именование обратного стиля DNS. Например, этот шифр вызывают com.apple.caesarcipher.

Во-первых, создайте исходный файл и включайте (по крайней мере), следующие заголовки:

#include <CoreFoundation/CoreFoundation.h>
#include <Security/SecCustomTransform.h>
#include <Security/SecTransform.h>

Затем объявите имя. Имя должно быть a CFStringRef экземпляр, и должен быть объявлен как глобальная переменная в заголовочном файле так, чтобы он мог использоваться другим кодом. Для этого шифра описание имени показано ниже:

const CFStringRef kCaesarCipher = CFSTR("com.apple.caesarcipher");

Пишущий функцию создания преобразования, первую часть

Преобразование может взять любое число параметров, но последним параметром должен всегда быть указатель на a CFErrorRef объект. Шифр Цезаря преобразовывает, берет только один параметр (ключ шифрования), который имеет тип CFIndex потому что ключ является небольшим числом. (Полезный диапазон совпадает с диапазоном uint8_t значение.) Таким образом функция объявляется следующим образом:

SecTransformRef CaesarTransformCreate(CFIndex k, CFErrorRef* error)

Эта функция регистрирует преобразование, затем создает экземпляр его.

Регистрация преобразования инициализирует преобразование и говорит коду платформы, что это существует. Поскольку эта инициализация должна быть выполнена точно один раз во время времени жизни программы, этот пример использует GCD для размещения блока инициализации в очередь отгрузки точно один раз, как показано ниже:

dispatch_once(&registeredOK, ^{block});

Для функции нужен флаг, чтобы видеть, было ли преобразование инициализировано и должно возвратить значение, чтобы указать, что было успешно зарегистрировано преобразование. Эти переменные определяются вне блока, следующим образом:

__block Boolean result = 1;
static dispatch_once_t registeredOK = 0;

Обратите внимание на то, что переменная результата, как объявляли, была блоком (__block) переменная так, чтобы это могло использоваться в блоке инициализации.

В блоке инициализации необходимо вызвать SecTransformRegister с тремя параметрами: имя преобразования, функция реализации, связывающая действия преобразования с фактическими блоками обработчика и указатель на хранение для a CFErrorRef объект. Например:

result = SecTransformRegister(kCaesarCipher, &CaesarImplementation, error);

Если регистрация перестала работать, SecTransformRegister возвращает булевскую переменную нам и изменяет CFErrorRef объект передал в вызывающей стороной.

Путем Вы реализуете само преобразование, описан в следующем разделе.

Запись функции реализации преобразования

После записи функции создания можно реализовать само преобразование. Вы делаете это путем записи функции реализации. Эта функция вызвана каждый раз, когда новый экземпляр Вашего преобразования создается. Это возвращает блок экземпляра, в котором преобразование выполняет свою работу.

В ядре этой функции следующие утверждения:

SecTransformInstanceBlock instanceBlock = ^{instance-block};
return Block_copy(instanceBlock);

В блоке экземпляра необходимо объявить любые переменные, в которых нуждается преобразование. Шифры могли бы использовать эти переменные для внутренних буферов, переменных состояния, и т.д. Шифр Цезаря преобразовывает, требует только одной переменной контекста, ключа:

__block int _key;

Обратите внимание на то, что переменная, как объявляют, __block, который заставляет его быть сохраненным в блоке. Группа преобразования может содержать многократные экземпляры преобразования, таким образом, все данные, определенные для экземпляра Вашего преобразования, должны инкапсулироваться в этом блоке экземпляра.

В дополнение к обеспечению хранения для значений, определенных для каждого экземпляра преобразования, блок экземпляра также устанавливает действие для каждого типа сообщения, который реализует преобразование. Шифр Цезаря преобразовывает поддержки только два действия:

Обратите внимание на то, что все подпрограммы действия вызывают с самым универсальным из всех Базовых объектов Основы, a CFTypeRef объект. Чтобы быть максимально гибкими, можно использовать isKindOfClass: метод для определения типа параметра.

Как исключение, код платформы фильтрует любые объекты CF, отправленные во входной атрибут, и передает только CFDataRef объекты к Вашему действию. Таким образом не необходимо использовать isKindOfClass: метод в Вашем ProcessData действие. Можно безопасно предположить, что Вы будете отправлены a CFDataRef объект (или NULL). Необходимо возвратить a CFDataRef объект, но необходимо бросить его к a CFTypeRef объект.

Ошибки из-за неправильного обращения

Преобразовывает использование CFErrorRef объекты сигнализировать и описать ошибки в выполнении. Любой блок или подпрограмма могут возвратить a CFErrorRef возразите для сигнализации ошибки. Это является самым полезным в a ProcessData действие. Самый прямой способ сигнализировать ошибку состоит в том, чтобы отправить a CFErrorRef возразите против атрибута аварийного прекращения работы (kSecTransformAbortAttributeName). Выполнение так закрывает цепочку преобразований.

Пишущий функцию создания преобразования, вторую часть

После диспетчеризации регистрации преобразования существует все еще несколько вещей, оставленных сделать в функции создания преобразования.

Во-первых, создайте экземпляр преобразования путем вызова SecTransformCreate. Этот вызов заставляет код платформы вызывать Вашу функцию реализации (тот, который Вы зарегистрировали в Функции Создания Преобразования, Первой части), который поочередно создает фактическое преобразование. Функция создания преобразования в конечном счете возвращает получающийся экземпляр преобразования вызывающей стороне.

В этом примере вызов создания похож на это:

caesarCipher = SecTransformCreate(kCaesarCipher, error);

Первый параметр является именем преобразования. Вторым является адрес a CFErrorRef переменная, взятая от последнего параметра функции создания.

После создания экземпляра преобразования, если Ваша функция создания преобразования берет дополнительные параметры для ключей шифрования или других настроек преобразования, она должна установить надлежащие атрибуты на экземпляре преобразования путем вызова SecTransformSetAttribute. (Пример шифра Цезаря не берет таких параметров.)

Наконец, функция создания преобразования возвращает экземпляр преобразования вызывающей стороне:

return caesarCipher;

Обработка изменений атрибута

Для изменения поведения преобразования Вы устанавливаете атрибуты объекта преобразования. Шифр Цезаря преобразовывает, имеет единственную функцию создания, не передающую параметров преобразованию. Однако возможно иметь многократные функции создания, берущие дополнительные параметры для установки определенных атрибутов. Значения атрибута могут также быть установлены после того, как объект преобразования создается и может даже быть установлен другими преобразованиями.

За исключением атрибутов ввода и вывода, все атрибуты равны. Их данные могут быть любым Базовым объектом Основы (хотя они обычно CFDataRef объекты). Платформа прозрачно обеспечивает управление потоком и полирующий для любых данных, отправленных в атрибут. Если входная очередь для атрибута достигает глубины десяти объектов, блоки отправителя (остановы), пока один или больше объектов не обрабатывается.

Входной атрибут является особенным тремя способами:

Выходной атрибут является столь же особенным:

Обработка данных

ProcessData действие выполняет большую часть работы в преобразовании.

Когда Вы обрабатываете данные, знать что CFDataRef ввод может иметь любой размер. Таким образом Ваше преобразование должно быть в состоянии обработать любой размер данных, включая единственный байт или даже строку нулевой длины (который отличен от получения a NULL указатель).

Если Ваш ProcessData действие получает исходные данные, который слишком короток для обработки, оно должно буферизовать данные в преобразовании и возвратить строку нулевой длины. Это может тогда обработать те данные вместе с любыми будущими данными, как только преобразование получило достаточно байтов.

Если Ваш ProcessData действие получает данные от любого атрибута (включая входной атрибут), но не готово обработать те данные из-за без вести пропавших входных данных от других атрибутов, это может вызвать SecTransformPushbackAttribute продвигать данные назад на переднюю сторону входной очереди. Можно пододвинуть только единственный элемент данных обратно на атрибут. Когда любой другой атрибут предоставит данные, Вам подарят пододвинутые обратно данные снова. Несмотря на то, что Вы обычно пододвигали бы элемент данных обратно, Вы просто получили, это не требование; можно пододвинуть любой объект обратно любого типа CF.

Полный листинг кода

Полный листинг кода для шифра Цезаря показан в Полном Листинге кода. Можно использовать это в качестве начальной точки для изучения, как создать собственные преобразования.

Перечисление 5-1  шифр Цезаря (полный список)

/*
 *  CaesarXform.c
 */
 
#include <Security/SecCustomTransform.h>
#include <Security/SecTransform.h>
 
// =========================================================================
//  Declaring the Transform Name
// =========================================================================
 
/* This is the unique name for the custom transform type. */
const CFStringRef kCaesarCipher = CFSTR("com.apple.caesarcipher");
 
/* Name of the "key" attribute. */
const CFStringRef kKeyAttributeName = CFSTR("key");
 
/* Helper that returns a CFError. */
CFErrorRef invalid_input_error(void)
{
    return CFErrorCreate(kCFAllocatorDefault, kSecTransformErrorDomain,
                         kSecTransformErrorInvalidInput, NULL);
}
 
// =========================================================================
//  The Transform Implementation Function
// =========================================================================
static SecTransformInstanceBlock CaesarImplementation(CFStringRef name,
                                            SecTransformRef newTransform,
                                            SecTransformImplementationRef ref)
{
 
    /* Instance Block:
 
       Every time a new instance of this custom transform class is
       created, this block is called. This behavior means that any
       block variables created in this block act like instance
       variables for the new custom transform instance.
     */
    SecTransformInstanceBlock instanceBlock =
    ^{
        CFErrorRef result = NULL;
 
        /* Data local to each instance: */
        __block int _key = 0;
 
        /****************************************
         *            Action Blocks:            *
         ****************************************/
 
        /* Key attribute action:
 
           This action is called when the key is set.
         */
        result = SecTransformSetAttributeAction(
                        ref,
                        kSecTransformActionAttributeNotification,
                        kKeyAttributeName,
                        ^(SecTransformAttributeRef name,
                        CFTypeRef d) {
                            CFNumberGetValue(
                                     (CFNumberRef)d,
                                     kCFNumberIntType,
                                     &_key
                            );
                            return d;
                        }
                    );
 
        if (result)
            return result;
 
        /* Input attribute action:
 
           This action is called for each piece of
           data posted on the input attribute.  It
           writes data to the output attribute.
         */
        result = SecTransformSetDataAction(
                    ref,
                    kSecTransformActionProcessData,
                    ^(CFTypeRef d) {
                        if (NULL == d)               // End of stream?
                            return (CFTypeRef) NULL; // Just return a null.
 
                        /* At this point, if desired, you
                           can check whether the key is available
                           and if not, you can call
                           SecTransformPushbackAttribute. */
 
                        char *dataPtr = (char *)CFDataGetBytePtr((CFDataRef)d);
 
                        CFIndex dataLength = CFDataGetLength((CFDataRef)d);
 
                        // Do the processing in a buffer generated by
                        // malloc for simplicity.
                        char *buffer = (char *)malloc(dataLength);
                        if (NULL == buffer) {
                            // Return a CFErrorRef
                            return (CFTypeRef) invalid_input_error();
                        }
 
                        // Do the work of the Caesar cipher (Rot(n))
 
                        CFIndex i;
                        for (i = 0; i < dataLength; i++)
                            buffer[i] = dataPtr[i] + _key;
 
                        return (CFTypeRef)CFDataCreateWithBytesNoCopy(
                                                NULL,
                                                (UInt8 *)buffer,
                                                dataLength,
                                                kCFAllocatorMalloc);
                    }
            );
        return result;
    };
 
    return Block_copy(instanceBlock);
}
 
// =========================================================================
//  The Transform Creation Function
// =========================================================================
SecTransformRef CaesarTransformCreate(CFIndex k, CFErrorRef* error)
{
    SecTransformRef caesarCipher;
    __block Boolean result = 1;
    static dispatch_once_t registeredOK = 0;
 
    dispatch_once(&registeredOK,
                  ^{
                     result = SecTransformRegister(
                                    kCaesarCipher,
                                    &CaesarImplementation,
                                    error);
                  });
 
    if (!result)
        return NULL;
 
    caesarCipher = SecTransformCreate(kCaesarCipher, error);
    if (NULL != caesarCipher)
    {
        CFNumberRef keyNumber =  CFNumberCreate(kCFAllocatorDefault,
                                                kCFNumberIntType, &k);
        SecTransformSetAttribute(caesarCipher, kKeyAttributeName,
                                 keyNumber, error);
        CFRelease(keyNumber);
    }
 
    return caesarCipher;
}
 
 
// The second function shows how to use custom transform defined in the
// previous function
 
// =========================================================================
//  Testing the transform
// =========================================================================
CFDataRef TestCaesar(CFDataRef theData, int rotNumber)
{
    CFDataRef result = NULL;
    CFErrorRef error = NULL;
 
    if (NULL == theData)
        return result;
 
    // Create an instance of the custom transform
    SecTransformRef caesarCipher = CaesarTransformCreate(rotNumber, &error);
    if (NULL == caesarCipher || NULL != error)
        return result;
 
    // Set the data to be transformed as the input to the custom transform
    SecTransformSetAttribute(caesarCipher,
                             kSecTransformInputAttributeName, theData, &error);
 
    if (NULL != error)
    {
        CFRelease(caesarCipher);
        return result;
    }
 
    // Execute the transform synchronously
    result = (CFDataRef)SecTransformExecute(caesarCipher, &error);
    CFRelease(caesarCipher);
 
    return result;
}
 
#include <CoreFoundation/CoreFoundation.h>
 
int main (int argc, const char *argv[])
{
    CFDataRef testData, testResult;
    UInt8 bytes[26];
    int i;
 
    // Create some test data, a string from A-Z
 
    for (i = 0; i < sizeof(bytes); i++)
        bytes[i] = 'A' + i;
 
    testData = CFDataCreate(kCFAllocatorDefault, bytes, sizeof(bytes));
    CFRetain(testData);
    CFShow(testData);
 
    // Encrypt the test data
    testResult = TestCaesar(testData, 3);
 
    CFShow(testResult);
    CFRelease(testData);
    CFRelease(testResult);
    return 0;
}