cssmdata.cpp   [plain text]


/*
 * Copyright (c) 2000-2004,2006 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


//
// cssmdata.cpp -- Manager different CssmData types
//
#include <security_cdsa_utilities/cssmdata.h>
#include <security_utilities/utilities.h>
#include <cstring>
#include <algorithm>


namespace Security {


//
// Comparing raw CSSM_DATA things
//
bool operator == (const CSSM_DATA &d1, const CSSM_DATA &d2)
{
    if (&d1 == &d2)
        return true;	// identical
    if (d1.Length != d2.Length)
        return false;	// can't be
    if (d1.Data == d2.Data)
        return true;	// points to same data
    return !memcmp(d1.Data, d2.Data, d1.Length);
}


//
// CssmData out of line members
// 
string CssmData::toString() const 
{
    return data() ? 
        string(reinterpret_cast<const char *>(data()), length())
        :
        string();
}


//
// Conversion from/to hex digits.
// This could be separate functions, or Rep templates, but we just hang
// it onto generic CssmData.
//
string CssmData::toHex() const
{
	static const char digits[] = "0123456789abcdef";
	string result;
	unsigned char *p = Data;
	for (uint32 n = 0; n < length(); n++) {
		result.push_back(digits[p[n] >> 4]);
		result.push_back(digits[p[n] & 0xf]);
	}
	return result;
}

static unsigned char hexValue(char c)
{
	static const char digits[] = "0123456789abcdef";
	if (const char *p = strchr(digits, tolower(c)))
		return p - digits;
	else
		return 0;
}

void CssmData::fromHex(const char *hexDigits)
{
	size_t bytes = strlen(hexDigits) / 2;	// (discards malformed odd end)
	length(bytes);	// (will assert if we try to grow it)
	for (size_t n = 0; n < bytes; n++) {
		Data[n] = hexValue(hexDigits[2*n]) << 4 | hexValue(hexDigits[2*n+1]);
	}
}


//
// Conversion from/to OID strings.
// These are not strict; invalid inputs are not necessarily flagged as errors.
//
static unsigned long getOid(const CssmData &data, unsigned int &pos)
{
	unsigned long q = 0;
	do {
		q = q * 128 + (data.byte(pos) & ~0x80);
	} while (pos < data.length() && data.byte(pos++) & 0x80);
	return q;
}

string CssmData::toOid() const
{
	if (length() == 0)
		return "";

	unsigned int pos = 0;
		
	// first byte is composite (q1,q2)
	char buffer[10];
	unsigned long oid1 = getOid(*this, pos);
	unsigned int q1 = min(oid1 / 40, 2ul);
	snprintf(buffer, sizeof(buffer), "%u.%lu", q1, oid1 - q1 * 40);
	string s = buffer;

	// now for the rest
	while (pos < length()) {
		char buffer[20];
		snprintf(buffer, sizeof(buffer), ".%lu", getOid(*this, pos));
		s += buffer;
	}
	return s;
}

static void putOid(CssmOwnedData &data, unsigned long id)
{
	unsigned char buffer[sizeof(unsigned long) * 2];	// * (8/7) + 1, conservative
	unsigned char *p = buffer + sizeof(buffer);
	do {
		*--p = 0x80 | (id & 0x7F);		// last 7 bits, high bit set
	} while ((id >>= 7) > 0);
	buffer[sizeof(buffer) - 1] &= ~0x80;	// clear last high bit (end of number)
	data.append(p, buffer + sizeof(buffer) - p); // append generated byte string
}

//
// Convert OID string (1.2.3...) into CssmOid form.
// Allocates the data, replacing current contents.
// Will not process oid elements out of unsigned long range.
//
void CssmOwnedData::fromOid(const char *oid)
{
	this->length(0);		// make empty
	
	// first two elements get combined in weird&wacky ways
	unsigned long q1 = strtoul(oid, (char **)&oid, 10);
	if (*oid++ != '.')
		return;
	unsigned long q2 = strtoul(oid, (char **)&oid, 10);
	putOid(*this, 40 * q1 + q2);
	while (oid[0] == '.') {
		oid++;
		putOid(*this, strtoul(oid, (char **)&oid, 10));
	}
}


//
// Managed data objects
//
CssmManagedData::~CssmManagedData()
{ }


//
// CssmOwnedData
//
void CssmOwnedData::set(CssmManagedData &source)
{
    if (source.length() == 0) {					// source is empty
        reset();								// so just clear old data
    } else if (allocator == source.allocator) {	// compatible allocators
        if (referent.data() == source.data()) {	// same data *and* we own it?!
            assert(this == &source);			//  this better *be* me!
        } else {								// different data
            reset();							// give up our old data
            referent = source.release();		// take over source's data
        }
    } else {									// different allocators
        copy(source);							// make a copy with our allocator
        source.reset();							// release source's data
    }
}


//
// CssmAutoData
//
CssmData CssmAutoData::release()
{
    CssmData result = mData;
    mData.clear();
    return result;
}

void CssmAutoData::reset()
{
    allocator.free(mData);
    mData.clear();
}


//
// CssmRemoteData
//
CssmData CssmRemoteData::release()
{
    iOwnTheData = false;
    return referent;
}

void CssmRemoteData::reset()
{
    if (iOwnTheData)
        allocator.free(referent);
    referent.clear();
}


//
// Date stuff
//
CssmDateData::CssmDateData(const CSSM_DATE &date)
: CssmData(buffer, sizeof(buffer))
{
	memcpy(buffer, date.Year, 4);
	memcpy(buffer + 4, date.Month, 2);
	memcpy(buffer + 6, date.Day, 2);
}


CssmData& CssmOwnedData::get() const throw()
{
	return referent;
}

}	// end namespace Security