SecOTRPublicIdentity.c [plain text]
#include "SecOTR.h"
#include "SecOTRIdentityPriv.h"
#include <utilities/SecCFWrappers.h>
#include <AssertMacros.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFData.h>
#include <Security/SecKey.h>
#include <Security/SecKeyPriv.h>
#include <corecrypto/ccn.h>
#include <corecrypto/ccec.h>
#include <corecrypto/ccder.h>
#import <sys/syslog.h>
#include "SecOTRErrors.h"
#include <TargetConditionals.h>
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonDigestSPI.h>
enum {
kOTRPIDER_SigningID = 1,
kOTRPIDER_SupportsHashes =3,
};
CFGiblisFor(SecOTRPublicIdentity);
static bool sAdvertiseHashes = false;
static CF_RETURNS_RETAINED CFStringRef SecOTRPublicIdentityCopyDescription(CFTypeRef cf) {
SecOTRPublicIdentityRef requestor = (SecOTRPublicIdentityRef)cf;
return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTRPublicIdentity: %p %02x%02x%02x%02x%02x%02x%02x%02x>"),
requestor,
requestor->hash[0], requestor->hash[1],
requestor->hash[2], requestor->hash[3],
requestor->hash[4], requestor->hash[5],
requestor->hash[6], requestor->hash[7]);
}
static void SecOTRPublicIdentityDestroy(CFTypeRef cf) {
SecOTRPublicIdentityRef requestor = (SecOTRPublicIdentityRef)cf;
CFReleaseNull(requestor->publicSigningKey);
}
static bool SecKeyDigestAndVerifyWithError(
SecKeyRef key,
const SecAsn1AlgId *algId,
const uint8_t *dataToDigest,
size_t dataToDigestLen,
uint8_t *sig,
size_t sigLen,
CFErrorRef *error) {
OSStatus status = SecKeyDigestAndVerify(key, algId, dataToDigest, dataToDigestLen, sig, sigLen);
require_noerr(status, fail);
return true;
fail:
SecOTRCreateError(secOTRErrorOSError, status, CFSTR("Error verifying message. OSStatus in error code."), NULL, error);
return false;
}
static bool SecOTRPICacheHash(SecOTRPublicIdentityRef pubID, CFErrorRef *error)
{
bool result = false;
CFMutableDataRef stream = CFDataCreateMutable(NULL, 0);
require(SecOTRPIAppendSerialization(pubID, stream, error), fail);
CCDigest(kCCDigestSHA1, CFDataGetBytePtr(stream), (CC_LONG)CFDataGetLength(stream), pubID->hash);
result = true;
fail:
CFReleaseSafe(stream);
return result;
}
void SecOTRAdvertiseHashes(bool advertise) {
sAdvertiseHashes = advertise;
}
SecOTRPublicIdentityRef SecOTRPublicIdentityCopyFromPrivate(CFAllocatorRef allocator, SecOTRFullIdentityRef fullID, CFErrorRef *error)
{
SecOTRPublicIdentityRef result = CFTypeAllocate(SecOTRPublicIdentity, struct _SecOTRPublicIdentity, allocator);
EnsureOTRAlgIDInited();
result->publicSigningKey = fullID->publicSigningKey;
CFRetain(result->publicSigningKey);
require(SecOTRPICacheHash(result, error), fail);
return result;
fail:
CFReleaseSafe(result);
return NULL;
}
SecOTRPublicIdentityRef SecOTRPublicIdentityCreateFromSecKeyRef(CFAllocatorRef allocator, SecKeyRef publicKey,
CFErrorRef *error) {
SecOTRPublicIdentityRef result = CFTypeAllocate(SecOTRPublicIdentity, struct _SecOTRPublicIdentity, allocator);
result->publicSigningKey = publicKey;
CFRetain(result->publicSigningKey);
require(SecOTRPICacheHash(result, error), fail);
return result;
fail:
CFRelease(result->publicSigningKey);
CFReleaseSafe(result);
return NULL;
}
typedef SecKeyRef (*SecOTRPublicKeyCreateFunction)(CFAllocatorRef allocator, const uint8_t** data, size_t* limit);
static SecKeyRef SecOTRCreatePublicKeyFrom(const uint8_t* keyData, size_t keyDataSize, ccder_tag tagContainingKey, SecOTRPublicKeyCreateFunction createFunction)
{
SecKeyRef createdKey = NULL;
createdKey = createFunction(kCFAllocatorDefault, &keyData, &keyDataSize);
require(createdKey != NULL, fail);
require(keyDataSize == 0, fail);
return createdKey;
fail:
CFReleaseSafe(createdKey);
return NULL;
}
SecOTRPublicIdentityRef SecOTRPublicIdentityCreateFromBytes(CFAllocatorRef allocator, const uint8_t**bytes, size_t* size, CFErrorRef *error)
{
CFErrorRef stackedError = NULL;
SecOTRPublicIdentityRef newID = CFTypeAllocate(SecOTRPublicIdentity, struct _SecOTRPublicIdentity, allocator);
EnsureOTRAlgIDInited();
const uint8_t* fullSequenceEnd = *bytes + *size;
const uint8_t* keyData = ccder_decode_sequence_tl(&fullSequenceEnd, *bytes, fullSequenceEnd);
size_t fullSize = (size_t)(fullSequenceEnd - *bytes);
size_t keyDataSize;
keyData = ccder_decode_tl(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SigningID, &keyDataSize, keyData, fullSequenceEnd);
newID->publicSigningKey = SecOTRCreatePublicKeyFrom(keyData, keyDataSize, kOTRPIDER_SigningID, &CreateECPublicKeyFrom);
require(newID->publicSigningKey != NULL, fail);
keyData += keyDataSize;
newID->wantsHashes = (NULL != ccder_decode_tl(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SupportsHashes, &keyDataSize, keyData, fullSequenceEnd));
require(SecOTRPICacheHash(newID, &stackedError), fail);
*bytes += fullSize;
*size -= fullSize;
return newID;
fail:
SecOTRCreateError(secOTRErrorLocal, kSecOTRErrorCreatePublicIdentity, CFSTR("Error creating public identity from bytes"), stackedError, error);
CFReleaseSafe(newID);
return NULL;
}
SecOTRPublicIdentityRef SecOTRPublicIdentityCreateFromData(CFAllocatorRef allocator, CFDataRef serializedData, CFErrorRef *error)
{
if (serializedData == NULL)
return NULL;
size_t length = (size_t)CFDataGetLength(serializedData);
const uint8_t* bytes = CFDataGetBytePtr(serializedData);
return SecOTRPublicIdentityCreateFromBytes(allocator, &bytes, &length, error);
}
bool SecOTRPIEqualToBytes(SecOTRPublicIdentityRef id, const uint8_t*bytes, CFIndex size)
{
CFDataRef dataToMatch = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bytes, size, kCFAllocatorNull);
CFMutableDataRef idStreamed = CFDataCreateMutable(kCFAllocatorDefault, 0);
SecOTRPIAppendSerialization(id, idStreamed, NULL);
bool equal = CFEqualSafe(dataToMatch, idStreamed);
CFReleaseNull(dataToMatch);
CFReleaseNull(idStreamed);
return equal;
}
bool SecOTRPIEqual(SecOTRPublicIdentityRef left, SecOTRPublicIdentityRef right)
{
if (left == right)
return true;
CFMutableDataRef leftData = CFDataCreateMutable(kCFAllocatorDefault, 0);
CFMutableDataRef rightData = CFDataCreateMutable(kCFAllocatorDefault, 0);
SecOTRPIAppendSerialization(left, leftData, NULL);
SecOTRPIAppendSerialization(right, rightData, NULL);
bool match = CFEqualSafe(leftData, rightData);
CFReleaseNull(leftData);
CFReleaseNull(rightData);
return match;
}
size_t SecOTRPISignatureSize(SecOTRPublicIdentityRef publicID)
{
return SecKeyGetSize(publicID->publicSigningKey, kSecKeySignatureSize);
}
bool SecOTRPIAppendSerialization(SecOTRPublicIdentityRef publicID, CFMutableDataRef serializeInto, CFErrorRef *error)
{
CFIndex start = CFDataGetLength(serializeInto);
CFMutableDataRef signingKeySerialized = NULL;
signingKeySerialized = CFDataCreateMutable(kCFAllocatorDefault, 0);
uint8_t outputBuffer[16384];
uint8_t* outputBufferEnd = outputBuffer + sizeof(outputBuffer);
uint8_t sendHashes[1];
sendHashes[0] = 0xFF;
require_noerr(appendPublicOctetsAndSize(publicID->publicSigningKey, signingKeySerialized), fail);
uint8_t *result = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, outputBufferEnd, outputBuffer,
ccder_encode_implicit_raw_octet_string(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SigningID, (size_t)CFDataGetLength(signingKeySerialized), CFDataGetBytePtr(signingKeySerialized), outputBuffer,
sAdvertiseHashes ? ccder_encode_implicit_raw_octet_string(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SupportsHashes, sizeof(sendHashes), sendHashes, outputBuffer, outputBufferEnd) : outputBufferEnd));
CFDataAppendBytes(serializeInto, result, outputBufferEnd - result);
CFReleaseSafe(signingKeySerialized);
return true;
fail:
CFReleaseSafe(signingKeySerialized);
CFDataSetLength(serializeInto, start);
SecOTRCreateError(secOTRErrorLocal, kSecOTRErrorCreatePublicBytes, CFSTR("Unable to create public key bytes"), NULL, error);
return false;
}
static const uint8_t *mp_decode_forced_uint(cc_size n, cc_unit *r, const uint8_t *der, const uint8_t *der_end) {
size_t len;
der = ccder_decode_tl(CCDER_INTEGER, &len, der, der_end);
if (der && ccn_read_uint(n, r, len, der) >= 0)
return der + len;
return NULL;
}
static void SecOTRPIRecreateSignature(const uint8_t *oldSignature, size_t oldSignatureSize, uint8_t **newSignature, size_t *newSignatureSize)
{
cc_unit r[ccec_cp_n(ccec_cp_256())], s[ccec_cp_n(ccec_cp_256())];
cc_size n = ccec_cp_n(ccec_cp_256());
const uint8_t *oldSignatureEnd = oldSignature + oldSignatureSize;
oldSignature = ccder_decode_sequence_tl(&oldSignatureEnd, oldSignature, oldSignatureEnd);
oldSignature = mp_decode_forced_uint(n, r, oldSignature, oldSignatureEnd);
oldSignature = mp_decode_forced_uint(n, s, oldSignature, oldSignatureEnd);
const uint8_t *outputPointer = *newSignature;
uint8_t *outputEndPointer = *newSignature + *newSignatureSize;
*newSignature = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, outputEndPointer, outputPointer, ccder_encode_integer(n, r, outputPointer, ccder_encode_integer(n, s, outputPointer, outputEndPointer)));
long newSigSize = outputEndPointer - *newSignature;
*newSignatureSize = (newSigSize >= 0) ? (size_t)newSigSize : 0;
}
bool SecOTRPIVerifySignature(SecOTRPublicIdentityRef publicID,
const uint8_t *dataToHash, size_t amountToHash,
const uint8_t *signatureStart, size_t signatureSize, CFErrorRef *error)
{
require(signatureSize > 0, fail);
require(*signatureStart == signatureSize - 1, fail);
signatureSize -= 1;
signatureStart += 1;
require(SecKeyDigestAndVerifyWithError(publicID->publicSigningKey, kOTRSignatureAlgIDPtr,
dataToHash, amountToHash,
(uint8_t*)signatureStart, signatureSize, NULL), fail);
return true;
fail:
;
uint8_t replacementSignature[signatureSize + 3];
size_t replacementSignatureLen = sizeof(replacementSignature);
uint8_t *replacementSignaturePtr = replacementSignature;
SecOTRPIRecreateSignature(signatureStart, signatureSize, &replacementSignaturePtr, &replacementSignatureLen);
require_action(replacementSignaturePtr, fail2, SecOTRCreateError(secOTRErrorLocal, kSecOTRErrorSignatureDidNotRecreate, CFSTR("Unable to recreate signature blob."), NULL, error));
require(SecKeyDigestAndVerifyWithError(publicID->publicSigningKey, kOTRSignatureAlgIDPtr,
dataToHash, amountToHash,
replacementSignaturePtr, replacementSignatureLen, error), fail2);
return true;
fail2:
return false;
}
void SecOTRPICopyHash(SecOTRPublicIdentityRef publicID, uint8_t hash[kMPIDHashSize])
{
memcpy(hash, publicID->hash, kMPIDHashSize);
}
void SecOTRPIAppendHash(SecOTRPublicIdentityRef publicID, CFMutableDataRef appendTo)
{
CFDataAppendBytes(appendTo, publicID->hash, sizeof(publicID->hash));
}
bool SecOTRPICompareHash(SecOTRPublicIdentityRef publicID, const uint8_t hash[kMPIDHashSize])
{
return 0 == memcmp(hash, publicID->hash, kMPIDHashSize);
}