#include "cdbuilder.h"
#include <security_utilities/memutils.h>
#include <cmath>
using namespace UnixPlusPlus;
using LowLevelMemoryUtilities::alignUp;
namespace Security {
namespace CodeSigning {
CodeDirectory::Builder::Builder(HashAlgorithm digestAlgorithm)
: mFlags(0),
mHashType(digestAlgorithm),
mSpecialSlots(0),
mCodeSlots(0),
mScatter(NULL),
mScatterSize(0),
mDir(NULL)
{
mDigestLength = MakeHash<Builder>(this)->digestLength();
mSpecial = (unsigned char *)calloc(cdSlotMax, mDigestLength);
}
CodeDirectory::Builder::~Builder()
{
::free(mSpecial);
::free(mScatter);
}
void CodeDirectory::Builder::executable(string path,
size_t pagesize, size_t offset, size_t length)
{
mExec.close(); mExec.open(path);
mPageSize = pagesize;
mExecOffset = offset;
mExecLength = length;
}
void CodeDirectory::Builder::reopen(string path, size_t offset, size_t length)
{
assert(mExec); mExec.close();
mExec.open(path);
mExecOffset = offset;
mExecLength = length;
}
void CodeDirectory::Builder::specialSlot(SpecialSlot slot, CFDataRef data)
{
assert(slot <= cdSlotMax);
MakeHash<Builder> hash(this);
hash->update(CFDataGetBytePtr(data), CFDataGetLength(data));
hash->finish(specialSlot(slot));
if (slot >= mSpecialSlots)
mSpecialSlots = slot;
}
CodeDirectory::Scatter *CodeDirectory::Builder::scatter(unsigned count)
{
mScatterSize = (count + 1) * sizeof(Scatter);
if (!(mScatter = (Scatter *)::realloc(mScatter, mScatterSize)))
UnixError::throwMe(ENOMEM);
::memset(mScatter, 0, mScatterSize);
return mScatter;
}
size_t CodeDirectory::Builder::size()
{
assert(mExec); if (mExecLength == 0)
mExecLength = mExec.fileSize() - mExecOffset;
if (mPageSize == 0) { mCodeSlots = (mExecLength > 0);
} else { mCodeSlots = (mExecLength + mPageSize - 1) / mPageSize; }
size_t offset = sizeof(CodeDirectory);
offset += mScatterSize; offset += mIdentifier.size() + 1; offset += (mCodeSlots + mSpecialSlots) * mDigestLength; return offset;
}
CodeDirectory *CodeDirectory::Builder::build()
{
assert(mExec);
size_t identLength = mIdentifier.size() + 1;
size_t total = size();
if (!(mDir = (CodeDirectory *)calloc(1, total))) UnixError::throwMe(ENOMEM);
mDir->initialize(total);
mDir->version = currentVersion;
mDir->flags = mFlags;
mDir->nSpecialSlots = mSpecialSlots;
mDir->nCodeSlots = mCodeSlots;
mDir->codeLimit = mExecLength;
mDir->hashType = mHashType;
mDir->hashSize = mDigestLength;
if (mPageSize) {
int pglog;
assert(frexp(mPageSize, &pglog) == 0.5); frexp(mPageSize, &pglog);
assert(pglog < 256);
mDir->pageSize = pglog - 1;
} else
mDir->pageSize = 0;
size_t offset = sizeof(CodeDirectory);
if (mScatter) {
mDir->scatterOffset = offset;
memcpy(mDir->scatterVector(), mScatter, mScatterSize);
offset += mScatterSize;
}
mDir->identOffset = offset;
memcpy(mDir->identifier(), mIdentifier.c_str(), identLength);
offset += identLength;
mDir->hashOffset = offset + mSpecialSlots * mDigestLength;
offset += (mSpecialSlots + mCodeSlots) * mDigestLength;
assert(offset == total);
memset((*mDir)[-mSpecialSlots], 0, mDigestLength * mSpecialSlots);
for (size_t slot = 1; slot <= mSpecialSlots; ++slot)
memcpy((*mDir)[-slot], specialSlot(slot), mDigestLength);
mExec.seek(mExecOffset);
size_t remaining = mExecLength;
for (unsigned int slot = 0; slot < mCodeSlots; ++slot) {
size_t thisPage = min(mPageSize, remaining);
MakeHash<Builder> hasher(this);
generateHash(hasher, mExec, (*mDir)[slot], thisPage);
remaining -= thisPage;
}
return mDir;
}
} }