]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_utilities/lib/superblob.h
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_utilities / lib / superblob.h
diff --git a/Security/libsecurity_utilities/lib/superblob.h b/Security/libsecurity_utilities/lib/superblob.h
new file mode 100644 (file)
index 0000000..785f51c
--- /dev/null
@@ -0,0 +1,237 @@
+//
+// SuperBlob - a typed bag of Blobs
+//
+#ifndef _H_SUPERBLOB
+#define _H_SUPERBLOB
+
+#include <security_utilities/blob.h>
+#include <map>
+
+namespace Security {
+
+
+//
+// A SuperBlob is a Blob that contains multiple sub-Blobs of varying type.
+// The SuperBlob is contiguous and contains a directory of its sub-blobs.
+// A Maker is included.
+//
+// SuperBlobCore lets you define your own SuperBlob type. To just use a generic
+// SuperBlob, use SuperBlob<> below.
+//
+template <class _BlobType, uint32_t _magic, class _Type>
+class SuperBlobCore: public Blob<_BlobType, _magic> {
+public:
+       class Maker; friend class Maker;
+       
+       typedef _Type Type;
+       
+       // echoes from parent BlobCore (the C++ type system is too restrictive here)
+       typedef BlobCore::Offset Offset;
+       template <class BlobType> BlobType *at(Offset offset) { return BlobCore::at<BlobType>(offset); }
+       template <class BlobType> const BlobType *at(Offset offset) const { return BlobCore::at<BlobType>(offset); }
+       
+       void setup(size_t size, unsigned count)
+       { this->initialize(size); this->mCount = count; }
+
+       struct Index {
+               Endian<Type> type;                      // type of sub-Blob
+               Endian<Offset> offset;          // starting offset
+       };
+       
+       bool validateBlob(size_t maxSize = 0) const;
+       
+       unsigned count() const { return mCount; }
+
+       // access by index number
+       Type type(unsigned n) const { assert(n < mCount); return mIndex[n].type; }
+       const BlobCore *blob(unsigned n) const
+               { assert(n < mCount); return mIndex[n].offset ? at<const BlobCore>(mIndex[n].offset) : NULL; }
+       template <class BlobType>
+       const BlobType *blob(unsigned n) const { return BlobType::specific(blob(n)); }
+
+       // access by index type (assumes unique types)
+       const BlobCore *find(Type type) const;
+       template <class BlobType>
+       const BlobType *find(Type type) const { return BlobType::specific(find(type)); }
+       
+private:
+       Endian<uint32_t> mCount;                // number of sub-Blobs following
+       Index mIndex[0];                                // <count> IndexSlot structures
+       // followed by sub-Blobs, packed and ordered in an undefined way
+};
+
+
+template <class _BlobType, uint32_t _magic, class _Type>
+inline bool SuperBlobCore<_BlobType, _magic, _Type>::validateBlob(size_t maxSize /* = 0 */) const
+{
+       unsigned count = mCount;
+       size_t ixLimit = sizeof(SuperBlobCore) + count * sizeof(Index); // end of index vector
+       if (!BlobCore::validateBlob(_magic, ixLimit, maxSize))
+               return false;
+       for (const Index *ix = mIndex + count - 1; ix >= mIndex; ix--) {
+               Offset offset = ix->offset;
+               if (offset)                                                                                                                                             // if non-null
+                       if (offset < ixLimit                                                                                                            // offset not too small
+                               || offset + sizeof(BlobCore) > this->length()                                                   // fits Blob header (including length field)
+                               || offset + at<const BlobCore>(offset)->length() > this->length())      // fits entire blob
+                               return false;
+       }
+       return true;
+}
+
+
+//
+// A generic SuperBlob ready for use. You still need to specify a magic number.
+//
+template <uint32_t _magic, class _Type = uint32_t>
+class SuperBlob : public SuperBlobCore<SuperBlob<_magic, _Type>, _magic, _Type> {
+};
+
+
+template <class _BlobType, uint32_t _magic, class _Type>
+const BlobCore *SuperBlobCore<_BlobType, _magic, _Type>::find(Type type) const
+{
+       for (unsigned slot = 0; slot < mCount; slot++)
+               if (mIndex[slot].type == type)
+                       return mIndex[slot].offset ? at<const BlobCore>(mIndex[slot].offset) : NULL;
+       return NULL;    // not found
+}
+
+
+//
+// A SuperBlob::Maker simply assembles multiple Blobs into a single, indexed
+// super-blob. Just add() sub-Blobs by type and call make() to get
+// the result, malloc'ed. A Maker is not resettable.
+// Maker can repeatedly make SuperBlobs from the same (cached) inputs.
+// It can also tell you how big its output will be, given established contents
+// plus (optional) additional sizes of blobs yet to come.
+//
+template <class _BlobType, uint32_t _magic, class _Type>
+class SuperBlobCore<_BlobType, _magic, _Type>::Maker {
+public:
+       Maker() { }
+       
+       Maker(const Maker &src)
+       {
+               for (typename BlobMap::iterator it = mPieces.begin(); it != mPieces.end(); ++it)
+                       mPieces.insert(make_pair(it->first, it->second->clone()));
+       }
+
+       ~Maker()
+       {
+               for (typename BlobMap::iterator it = mPieces.begin(); it != mPieces.end(); ++it)
+                       ::free(it->second);
+       }
+       
+       void add(Type type, BlobCore *blob);            // takes ownership of blob
+       void add(const _BlobType *blobs);                       // copies all blobs
+       void add(const Maker &maker);                           // ditto
+       
+       bool contains(Type type) const                          // see if we have this type already
+               { return mPieces.find(type) != mPieces.end(); }
+       BlobCore *get(Type type) const
+               {
+                       typename BlobMap::const_iterator it = mPieces.find(type);
+                       return (it == mPieces.end()) ? NULL : it->second;
+               }
+       
+       size_t size(size_t size1 = 0, ...) const;       // size with optional additional blob sizes
+       _BlobType *make() const;                                        // create (malloc) and return SuperBlob
+       _BlobType *operator () () const { return make(); }
+
+private:
+       typedef std::map<Type, BlobCore *> BlobMap;
+       BlobMap mPieces;
+};
+
+
+//
+// Add a Blob to a SuperBlob::Maker.
+// This takes ownership of the blob, which must have been malloc'ed.
+// Any previous value set for this Type will be freed immediately.
+//
+template <class _BlobType, uint32_t _magic, class _Type>
+void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(Type type, BlobCore *blob)
+{
+       pair<typename BlobMap::iterator, bool> r = mPieces.insert(make_pair(type, blob));
+       if (!r.second) {        // already there
+               secdebug("superblob", "Maker %p replaces type=%d", this, type);
+               ::free(r.first->second);
+               r.first->second = blob;
+       }
+}
+
+template <class _BlobType, uint32_t _magic, class _Type>
+void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(const _BlobType *blobs)
+{
+       for (uint32_t ix = 0; ix < blobs->mCount; ix++)
+               this->add(blobs->mIndex[ix].type, blobs->blob(ix)->clone());
+}
+
+template <class _BlobType, uint32_t _magic, class _Type>
+void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(const Maker &maker)
+{
+       for (typename BlobMap::const_iterator it = maker.mPieces.begin(); it != maker.mPieces.end(); ++it)
+               this->add(it->first, it->second->clone());
+}
+
+
+//
+// Calculate the size the new SuperBlob would have, given the contents of the Maker
+// so far, plus additional blobs with the sizes given.
+//
+template <class _BlobType, uint32_t _magic, class _Type>
+size_t SuperBlobCore<_BlobType, _magic, _Type>::Maker::size(size_t size1, ...) const
+{
+       // count established blobs
+       size_t count = mPieces.size();
+       size_t total = 0;
+       for (typename BlobMap::const_iterator it = mPieces.begin(); it != mPieces.end(); ++it)
+               total += it->second->length();
+
+       // add preview blob sizes to calculation (if any)
+       if (size1) {
+               va_list args;
+               va_start(args, size1);
+               do {
+                       count++;
+                       total += size1;
+                       size1 = va_arg(args, size_t);
+               } while (size1);
+               va_end(args);
+       }
+
+       return sizeof(SuperBlobCore) + count * sizeof(Index) + total;
+}
+
+
+//
+// Finish SuperBlob construction and return the new, malloc'ed, SuperBlob.
+// This can be done repeatedly.
+//
+template <class _BlobType, uint32_t _magic, class _Type>
+_BlobType *SuperBlobCore<_BlobType, _magic, _Type>::Maker::make() const
+{
+       Offset pc = (Offset)(sizeof(SuperBlobCore) + mPieces.size() * sizeof(Index));
+       Offset total = (Offset)size();
+       _BlobType *result = (_BlobType *)malloc(total);
+       if (!result)
+               UnixError::throwMe(ENOMEM);
+       result->setup(total, (unsigned)mPieces.size());
+       unsigned n = 0;
+       for (typename BlobMap::const_iterator it = mPieces.begin(); it != mPieces.end(); ++it) {
+               result->mIndex[n].type = it->first;
+               result->mIndex[n].offset = pc;
+               memcpy(result->template at<unsigned char>(pc), it->second, it->second->length());
+               pc += it->second->length();
+               n++;
+       }
+       secdebug("superblob", "Maker %p assembles %ld blob(s) into %p (size=%d)",
+               this, mPieces.size(), result, total);
+       return result;
+}
+
+
+}      // Security
+
+#endif //_H_SUPERBLOB