summaryrefslogtreecommitdiff
path: root/libqpdf
diff options
context:
space:
mode:
Diffstat (limited to 'libqpdf')
-rw-r--r--libqpdf/OffsetInputSource.cc61
-rw-r--r--libqpdf/Pl_AES_PDF.cc112
-rw-r--r--libqpdf/Pl_SHA2.cc164
-rw-r--r--libqpdf/QPDF.cc145
-rw-r--r--libqpdf/QPDFWriter.cc611
-rw-r--r--libqpdf/QPDF_Stream.cc250
-rw-r--r--libqpdf/QPDF_encryption.cc801
-rw-r--r--libqpdf/QPDF_optimization.cc101
-rw-r--r--libqpdf/QUtil.cc39
-rw-r--r--libqpdf/build.mk19
-rw-r--r--libqpdf/qpdf-c.cc48
-rw-r--r--libqpdf/qpdf/OffsetInputSource.hh29
-rw-r--r--libqpdf/qpdf/Pl_AES_PDF.hh28
-rw-r--r--libqpdf/qpdf/Pl_SHA2.hh50
-rw-r--r--libqpdf/qpdf/QPDF_Stream.hh3
-rw-r--r--libqpdf/sha2.c690
-rw-r--r--libqpdf/sha2big.c247
-rw-r--r--libqpdf/sph/md_helper.c346
-rw-r--r--libqpdf/sph/sph_sha2.h378
-rw-r--r--libqpdf/sph/sph_types.h1976
20 files changed, 5605 insertions, 493 deletions
diff --git a/libqpdf/OffsetInputSource.cc b/libqpdf/OffsetInputSource.cc
new file mode 100644
index 0000000..c1ec410
--- /dev/null
+++ b/libqpdf/OffsetInputSource.cc
@@ -0,0 +1,61 @@
+#include <qpdf/OffsetInputSource.hh>
+
+OffsetInputSource::OffsetInputSource(PointerHolder<InputSource> proxied,
+ qpdf_offset_t global_offset) :
+ proxied(proxied),
+ global_offset(global_offset)
+{
+}
+
+OffsetInputSource::~OffsetInputSource()
+{
+}
+
+qpdf_offset_t
+OffsetInputSource::findAndSkipNextEOL()
+{
+ return this->proxied->findAndSkipNextEOL() - this->global_offset;
+}
+
+std::string const&
+OffsetInputSource::getName() const
+{
+ return this->proxied->getName();
+}
+
+qpdf_offset_t
+OffsetInputSource::tell()
+{
+ return this->proxied->tell() - this->global_offset;
+}
+
+void
+OffsetInputSource::seek(qpdf_offset_t offset, int whence)
+{
+ if (whence == SEEK_SET)
+ {
+ this->proxied->seek(offset + global_offset, whence);
+ }
+ else
+ {
+ this->proxied->seek(offset, whence);
+ }
+}
+
+void
+OffsetInputSource::rewind()
+{
+ seek(0, SEEK_SET);
+}
+
+size_t
+OffsetInputSource::read(char* buffer, size_t length)
+{
+ return this->proxied->read(buffer, length);
+}
+
+void
+OffsetInputSource::unreadCh(char ch)
+{
+ this->proxied->unreadCh(ch);
+}
diff --git a/libqpdf/Pl_AES_PDF.cc b/libqpdf/Pl_AES_PDF.cc
index 0f73c09..5287610 100644
--- a/libqpdf/Pl_AES_PDF.cc
+++ b/libqpdf/Pl_AES_PDF.cc
@@ -6,28 +6,29 @@
#include <qpdf/rijndael.h>
#include <string>
#include <stdlib.h>
-#include <qpdf/qpdf-config.h>
-#ifndef HAVE_RANDOM
-# define random rand
-# define srandom srand
-#endif
bool Pl_AES_PDF::use_static_iv = false;
Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next,
- bool encrypt, unsigned char const key[key_size]) :
+ bool encrypt, unsigned char const* key,
+ unsigned int key_bytes) :
Pipeline(identifier, next),
encrypt(encrypt),
cbc_mode(true),
first(true),
offset(0),
- nrounds(0)
+ nrounds(0),
+ use_zero_iv(false),
+ use_specified_iv(false),
+ disable_padding(false)
{
- static int const keybits = 128;
- assert(key_size == KEYLENGTH(keybits));
- assert(sizeof(this->rk) / sizeof(uint32_t) == RKLENGTH(keybits));
- std::memcpy(this->key, key, key_size);
- std::memset(this->rk, 0, sizeof(this->rk));
+ unsigned int keybits = 8 * key_bytes;
+ assert(key_bytes == KEYLENGTH(keybits));
+ this->key = new unsigned char[key_bytes];
+ this->rk = new uint32_t[RKLENGTH(keybits)];
+ unsigned int rk_bytes = RKLENGTH(keybits) * sizeof(uint32_t);
+ std::memcpy(this->key, key, key_bytes);
+ std::memset(this->rk, 0, rk_bytes);
std::memset(this->inbuf, 0, this->buf_size);
std::memset(this->outbuf, 0, this->buf_size);
std::memset(this->cbc_block, 0, this->buf_size);
@@ -44,7 +45,33 @@ Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next,
Pl_AES_PDF::~Pl_AES_PDF()
{
- // nothing needed
+ delete [] this->key;
+ delete [] this->rk;
+}
+
+void
+Pl_AES_PDF::useZeroIV()
+{
+ this->use_zero_iv = true;
+}
+
+void
+Pl_AES_PDF::disablePadding()
+{
+ this->disable_padding = true;
+}
+
+void
+Pl_AES_PDF::setIV(unsigned char const* iv, size_t bytes)
+{
+ if (bytes != this->buf_size)
+ {
+ throw std::logic_error(
+ "Pl_AES_PDF: specified initialization vector"
+ " size in bytes must be " + QUtil::int_to_string(bytes));
+ }
+ this->use_specified_iv = true;
+ memcpy(this->specified_iv, iv, bytes);
}
void
@@ -90,13 +117,16 @@ Pl_AES_PDF::finish()
{
flush(false);
}
- // Pad as described in section 3.5.1 of version 1.7 of the PDF
- // specification, including providing an entire block of padding
- // if the input was a multiple of 16 bytes.
- unsigned char pad = (unsigned char) (this->buf_size - this->offset);
- memset(this->inbuf + this->offset, pad, pad);
- this->offset = this->buf_size;
- flush(false);
+ if (! this->disable_padding)
+ {
+ // Pad as described in section 3.5.1 of version 1.7 of the PDF
+ // specification, including providing an entire block of padding
+ // if the input was a multiple of 16 bytes.
+ unsigned char pad = (unsigned char) (this->buf_size - this->offset);
+ memset(this->inbuf + this->offset, pad, pad);
+ this->offset = this->buf_size;
+ flush(false);
+ }
}
else
{
@@ -112,7 +142,7 @@ Pl_AES_PDF::finish()
this->buf_size - this->offset);
this->offset = this->buf_size;
}
- flush(true);
+ flush(! this->disable_padding);
}
getNext()->finish();
}
@@ -120,16 +150,18 @@ Pl_AES_PDF::finish()
void
Pl_AES_PDF::initializeVector()
{
- static bool seeded_random = false;
- if (! seeded_random)
+ if (use_zero_iv)
{
- // Seed the random number generator with something simple, but
- // just to be interesting, don't use the unmodified current
- // time....
- srandom((int)QUtil::get_current_time() ^ 0xcccc);
- seeded_random = true;
+ for (unsigned int i = 0; i < this->buf_size; ++i)
+ {
+ this->cbc_block[i] = 0;
+ }
}
- if (use_static_iv)
+ else if (use_specified_iv)
+ {
+ std::memcpy(this->cbc_block, this->specified_iv, this->buf_size);
+ }
+ else if (use_static_iv)
{
for (unsigned int i = 0; i < this->buf_size; ++i)
{
@@ -138,10 +170,7 @@ Pl_AES_PDF::initializeVector()
}
else
{
- for (unsigned int i = 0; i < this->buf_size; ++i)
- {
- this->cbc_block[i] = (unsigned char)((random() & 0xff0) >> 4);
- }
+ QUtil::initializeWithRandomBytes(this->cbc_block, this->buf_size);
}
}
@@ -157,12 +186,21 @@ Pl_AES_PDF::flush(bool strip_padding)
{
if (encrypt)
{
- // Set cbc_block to a random initialization vector and
- // write it to the output stream
+ // Set cbc_block to the initialization vector, and if
+ // not zero, write it to the output stream.
initializeVector();
- getNext()->write(this->cbc_block, this->buf_size);
+ if (! (this->use_zero_iv || this->use_specified_iv))
+ {
+ getNext()->write(this->cbc_block, this->buf_size);
+ }
}
- else
+ else if (this->use_zero_iv || this->use_specified_iv)
+ {
+ // Initialize vector with zeroes; zero vector was not
+ // written to the beginning of the input file.
+ initializeVector();
+ }
+ else
{
// Take the first block of input as the initialization
// vector. There's nothing to write at this time.
diff --git a/libqpdf/Pl_SHA2.cc b/libqpdf/Pl_SHA2.cc
new file mode 100644
index 0000000..018f411
--- /dev/null
+++ b/libqpdf/Pl_SHA2.cc
@@ -0,0 +1,164 @@
+#include <qpdf/Pl_SHA2.hh>
+#include <stdexcept>
+#include <cstdio>
+#include <qpdf/PointerHolder.hh>
+
+Pl_SHA2::Pl_SHA2(int bits, Pipeline* next) :
+ Pipeline("sha2", next),
+ in_progress(false),
+ bits(0)
+{
+ if (bits)
+ {
+ resetBits(bits);
+ }
+}
+
+Pl_SHA2::~Pl_SHA2()
+{
+}
+
+void
+Pl_SHA2::badBits()
+{
+ throw std::logic_error("Pl_SHA2 has unexpected value for bits");
+}
+
+void
+Pl_SHA2::write(unsigned char* buf, size_t len)
+{
+ if (! this->in_progress)
+ {
+ switch (bits)
+ {
+ case 256:
+ sph_sha256_init(&this->ctx256);
+ break;
+ case 384:
+ sph_sha384_init(&this->ctx384);
+ break;
+ case 512:
+ sph_sha512_init(&this->ctx512);
+ break;
+ default:
+ badBits();
+ break;
+ }
+ this->in_progress = true;
+ }
+
+ // Write in chunks in case len is too big to fit in an int.
+ // Assume int is at least 32 bits.
+ static size_t const max_bytes = 1 << 30;
+ size_t bytes_left = len;
+ unsigned char* data = buf;
+ while (bytes_left > 0)
+ {
+ size_t bytes = (bytes_left >= max_bytes ? max_bytes : bytes_left);
+ switch (bits)
+ {
+ case 256:
+ sph_sha256(&this->ctx256, data, bytes);
+ break;
+ case 384:
+ sph_sha384(&this->ctx384, data, bytes);
+ break;
+ case 512:
+ sph_sha512(&this->ctx512, data, bytes);
+ break;
+ default:
+ badBits();
+ break;
+ }
+ bytes_left -= bytes;
+ data += bytes;
+ }
+
+ if (this->getNext(true))
+ {
+ this->getNext()->write(buf, len);
+ }
+}
+
+void
+Pl_SHA2::finish()
+{
+ if (this->getNext(true))
+ {
+ this->getNext()->finish();
+ }
+ switch (bits)
+ {
+ case 256:
+ sph_sha256_close(&this->ctx256, sha256sum);
+ break;
+ case 384:
+ sph_sha384_close(&this->ctx384, sha384sum);
+ break;
+ case 512:
+ sph_sha512_close(&this->ctx512, sha512sum);
+ break;
+ default:
+ badBits();
+ break;
+ }
+ this->in_progress = false;
+}
+
+void
+Pl_SHA2::resetBits(int bits)
+{
+ if (this->in_progress)
+ {
+ throw std::logic_error(
+ "bit reset requested for in-progress SHA2 Pipeline");
+ }
+ if (! ((bits == 256) || (bits == 384) || (bits == 512)))
+ {
+ throw std::logic_error("Pl_SHA2 called with bits != 256, 384, or 512");
+ }
+ this->bits = bits;
+}
+
+std::string
+Pl_SHA2::getRawDigest()
+{
+ std::string result;
+ switch (bits)
+ {
+ case 256:
+ result = std::string((char*)this->sha256sum, sizeof(this->sha256sum));
+ break;
+ case 384:
+ result = std::string((char*)this->sha384sum, sizeof(this->sha384sum));
+ break;
+ case 512:
+ result = std::string((char*)this->sha512sum, sizeof(this->sha512sum));
+ break;
+ default:
+ badBits();
+ break;
+ }
+ return result;
+}
+
+std::string
+Pl_SHA2::getHexDigest()
+{
+ if (this->in_progress)
+ {
+ throw std::logic_error(
+ "digest requested for in-progress SHA2 Pipeline");
+ }
+ std::string raw = getRawDigest();
+ size_t raw_size = raw.length();
+ size_t hex_size = 1 + (2 * raw_size);
+ PointerHolder<char> bufp(true, new char[hex_size]);
+ char* buf = bufp.getPointer();
+ buf[hex_size - 1] = '\0';
+ for (unsigned int i = 0; i < raw_size; ++i)
+ {
+ std::sprintf(buf + i * 2, "%02x", (unsigned char)raw[i]);
+ }
+ return buf;
+}
diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc
index efef337..a779c23 100644
--- a/libqpdf/QPDF.cc
+++ b/libqpdf/QPDF.cc
@@ -13,12 +13,13 @@
#include <qpdf/Pl_Discard.hh>
#include <qpdf/FileInputSource.hh>
#include <qpdf/BufferInputSource.hh>
+#include <qpdf/OffsetInputSource.hh>
#include <qpdf/QPDFExc.hh>
#include <qpdf/QPDF_Null.hh>
#include <qpdf/QPDF_Dictionary.hh>
-std::string QPDF::qpdf_version = "3.0.2";
+std::string QPDF::qpdf_version = "4.0.0";
static char const* EMPTY_PDF =
"%PDF-1.3\n"
@@ -96,6 +97,7 @@ QPDF::QPDF() :
err_stream(&std::cerr),
attempt_recovery(true),
encryption_V(0),
+ encryption_R(0),
encrypt_metadata(true),
cf_stream(e_none),
cf_string(e_none),
@@ -138,9 +140,8 @@ void
QPDF::processFile(char const* filename, char const* password)
{
FileInputSource* fi = new FileInputSource();
- this->file = fi;
fi->setFilename(filename);
- parse(password);
+ processInputSource(fi, password);
}
void
@@ -148,9 +149,8 @@ QPDF::processFile(char const* description, FILE* filep,
bool close_file, char const* password)
{
FileInputSource* fi = new FileInputSource();
- this->file = fi;
fi->setFile(description, filep, close_file);
- parse(password);
+ processInputSource(fi, password);
}
void
@@ -158,10 +158,18 @@ QPDF::processMemoryFile(char const* description,
char const* buf, size_t length,
char const* password)
{
- this->file =
+ processInputSource(
new BufferInputSource(description,
new Buffer((unsigned char*)buf, length),
- true);
+ true),
+ password);
+}
+
+void
+QPDF::processInputSource(PointerHolder<InputSource> source,
+ char const* password)
+{
+ this->file = source;
parse(password);
}
@@ -207,7 +215,7 @@ QPDF::getWarnings()
void
QPDF::parse(char const* password)
{
- PCRE header_re("^%PDF-(1.\\d+)\\b");
+ PCRE header_re("\\A((?s).*?)%PDF-(1.\\d+)\\b");
PCRE eof_re("(?s:startxref\\s+(\\d+)\\s+%%EOF\\b)");
if (password)
@@ -215,11 +223,26 @@ QPDF::parse(char const* password)
this->provided_password = password;
}
- std::string line = this->file->readLine(20);
+ // Find the header anywhere in the first 1024 bytes of the file,
+ // plus add a little extra space for the header itself.
+ char buffer[1045];
+ memset(buffer, '\0', sizeof(buffer));
+ this->file->read(buffer, sizeof(buffer) - 1);
+ std::string line(buffer);
PCRE::Match m1 = header_re.match(line.c_str());
if (m1)
{
- this->pdf_version = m1.getMatch(1);
+ size_t global_offset = m1.getMatch(1).length();
+ if (global_offset != 0)
+ {
+ // Empirical evidence strongly suggests that when there is
+ // leading material prior to the PDF header, all explicit
+ // offsets in the file are such that 0 points to the
+ // beginning of the header.
+ QTC::TC("qpdf", "QPDF global offset");
+ this->file = new OffsetInputSource(this->file, global_offset);
+ }
+ this->pdf_version = m1.getMatch(2);
if (atof(this->pdf_version.c_str()) < 1.2)
{
this->tokenizer.allowPoundAnywhereInName();
@@ -292,6 +315,7 @@ QPDF::parse(char const* password)
}
initializeEncryption();
+ findAttachmentStreams();
}
void
@@ -1247,6 +1271,21 @@ QPDF::readObjectAtOffset(bool try_recovery,
int& objid, int& generation)
{
setLastObjectDescription(description, exp_objid, exp_generation);
+
+ // Special case: if offset is 0, just return null. Some PDF
+ // writers, in particular "Mac OS X 10.7.5 Quartz PDFContext", may
+ // store deleted objects in the xref table as "0000000000 00000
+ // n", which is not correct, but it won't hurt anything for to
+ // ignore these.
+ if (offset == 0)
+ {
+ QTC::TC("qpdf", "QPDF bogus 0 offset", 0);
+ warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
+ this->last_object_description, 0,
+ "object has offset 0"));
+ return QPDFObjectHandle::newNull();
+ }
+
this->file->seek(offset, SEEK_SET);
QPDFTokenizer::Token tobjid = readToken(this->file);
@@ -1823,28 +1862,6 @@ QPDF::swapObjects(int objid1, int generation1, int objid2, int generation2)
this->obj_cache[og2] = t;
}
-void
-QPDF::trimTrailerForWrite()
-{
- // Note that removing the encryption dictionary does not interfere
- // with reading encrypted files. QPDF loads all the information
- // it needs from the encryption dictionary at the beginning and
- // never looks at it again.
- this->trailer.removeKey("/ID");
- this->trailer.removeKey("/Encrypt");
- this->trailer.removeKey("/Prev");
-
- // Remove all trailer keys that potentially come from a
- // cross-reference stream
- this->trailer.removeKey("/Index");
- this->trailer.removeKey("/W");
- this->trailer.removeKey("/Length");
- this->trailer.removeKey("/Filter");
- this->trailer.removeKey("/DecodeParms");
- this->trailer.removeKey("/Type");
- this->trailer.removeKey("/XRefStm");
-}
-
std::string
QPDF::getFilename() const
{
@@ -1857,6 +1874,30 @@ QPDF::getPDFVersion() const
return this->pdf_version;
}
+int
+QPDF::getExtensionLevel()
+{
+ int result = 0;
+ QPDFObjectHandle obj = getRoot();
+ if (obj.hasKey("/Extensions"))
+ {
+ obj = obj.getKey("/Extensions");
+ if (obj.isDictionary() && obj.hasKey("/ADBE"))
+ {
+ obj = obj.getKey("/ADBE");
+ if (obj.isDictionary() && obj.hasKey("/ExtensionLevel"))
+ {
+ obj = obj.getKey("/ExtensionLevel");
+ if (obj.isInteger())
+ {
+ result = obj.getIntValue();
+ }
+ }
+ }
+ }
+ return result;
+}
+
QPDFObjectHandle
QPDF::getTrailer()
{
@@ -2032,18 +2073,36 @@ QPDF::pipeStreamData(int objid, int generation,
}
void
-QPDF::decodeStreams()
+QPDF::findAttachmentStreams()
{
- for (std::map<ObjGen, QPDFXRefEntry>::iterator iter =
- this->xref_table.begin();
- iter != this->xref_table.end(); ++iter)
+ QPDFObjectHandle root = getRoot();
+ QPDFObjectHandle names = root.getKey("/Names");
+ if (! names.isDictionary())
{
- ObjGen const& og = (*iter).first;
- QPDFObjectHandle obj = getObjectByID(og.obj, og.gen);
- if (obj.isStream())
- {
- Pl_Discard pl;
- obj.pipeStreamData(&pl, true, false, false);
- }
+ return;
+ }
+ QPDFObjectHandle embeddedFiles = names.getKey("/EmbeddedFiles");
+ if (! embeddedFiles.isDictionary())
+ {
+ return;
+ }
+ names = embeddedFiles.getKey("/Names");
+ if (! names.isArray())
+ {
+ return;
+ }
+ for (int i = 0; i < names.getArrayNItems(); ++i)
+ {
+ QPDFObjectHandle item = names.getArrayItem(i);
+ if (item.isDictionary() &&
+ item.getKey("/Type").isName() &&
+ (item.getKey("/Type").getName() == "/Filespec") &&
+ item.getKey("/EF").isDictionary() &&
+ item.getKey("/EF").getKey("/F").isStream())
+ {
+ QPDFObjectHandle stream = item.getKey("/EF").getKey("/F");
+ this->attachment_streams.insert(
+ ObjGen(stream.getObjectID(), stream.getGeneration()));
+ }
}
}
diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc
index eb08488..a1949a9 100644
--- a/libqpdf/QPDFWriter.cc
+++ b/libqpdf/QPDFWriter.cc
@@ -64,6 +64,11 @@ QPDFWriter::init()
object_stream_mode = qpdf_o_preserve;
encrypt_metadata = true;
encrypt_use_aes = false;
+ min_extension_level = 0;
+ final_extension_level = 0;
+ forced_extension_level = 0;
+ encryption_V = 0;
+ encryption_R = 0;
encryption_dict_objid = 0;
next_objid = 1;
cur_stream_length_id = 0;
@@ -135,6 +140,13 @@ QPDFWriter::getBuffer()
}
void
+QPDFWriter::setOutputPipeline(Pipeline* p)
+{
+ this->filename = "custom pipeline";
+ initializePipelineStack(p);
+}
+
+void
QPDFWriter::setObjectStreamMode(qpdf_object_stream_e mode)
{
this->object_stream_mode = mode;
@@ -163,10 +175,19 @@ QPDFWriter::setQDFMode(bool val)
void
QPDFWriter::setMinimumPDFVersion(std::string const& version)
{
+ setMinimumPDFVersion(version, 0);
+}
+
+void
+QPDFWriter::setMinimumPDFVersion(std::string const& version,
+ int extension_level)
+{
bool set_version = false;
+ bool set_extension_level = false;
if (this->min_pdf_version.empty())
{
set_version = true;
+ set_extension_level = true;
}
else
{
@@ -176,10 +197,22 @@ QPDFWriter::setMinimumPDFVersion(std::string const& version)
int min_minor = 0;
parseVersion(version, old_major, old_minor);
parseVersion(this->min_pdf_version, min_major, min_minor);
- if (compareVersions(old_major, old_minor, min_major, min_minor) > 0)
+ int compare = compareVersions(
+ old_major, old_minor, min_major, min_minor);
+ if (compare > 0)
{
- QTC::TC("qpdf", "QPDFWriter increasing minimum version");
+ QTC::TC("qpdf", "QPDFWriter increasing minimum version",
+ extension_level == 0 ? 0 : 1);
set_version = true;
+ set_extension_level = true;
+ }
+ else if (compare == 0)
+ {
+ if (extension_level > this->min_extension_level)
+ {
+ QTC::TC("qpdf", "QPDFWriter increasing extension level");
+ set_extension_level = true;
+ }
}
}
@@ -187,12 +220,24 @@ QPDFWriter::setMinimumPDFVersion(std::string const& version)
{
this->min_pdf_version = version;
}
+ if (set_extension_level)
+ {
+ this->min_extension_level = extension_level;
+ }
}
void
QPDFWriter::forcePDFVersion(std::string const& version)
{
+ forcePDFVersion(version, 0);
+}
+
+void
+QPDFWriter::forcePDFVersion(std::string const& version,
+ int extension_level)
+{
this->forced_pdf_version = version;
+ this->forced_extension_level = extension_level;
}
void
@@ -301,6 +346,38 @@ QPDFWriter::setR4EncryptionParameters(
}
void
+QPDFWriter::setR5EncryptionParameters(
+ char const* user_password, char const* owner_password,
+ bool allow_accessibility, bool allow_extract,
+ qpdf_r3_print_e print, qpdf_r3_modify_e modify,
+ bool encrypt_metadata)
+{
+ std::set<int> clear;
+ interpretR3EncryptionParameters(
+ clear, user_password, owner_password,
+ allow_accessibility, allow_extract, print, modify);
+ this->encrypt_use_aes = true;
+ this->encrypt_metadata = encrypt_metadata;
+ setEncryptionParameters(user_password, owner_password, 5, 5, 32, clear);
+}
+
+void
+QPDFWriter::setR6EncryptionParameters(
+ char const* user_password, char const* owner_password,
+ bool allow_accessibility, bool allow_extract,
+ qpdf_r3_print_e print, qpdf_r3_modify_e modify,
+ bool encrypt_metadata)
+{
+ std::set<int> clear;
+ interpretR3EncryptionParameters(
+ clear, user_password, owner_password,
+ allow_accessibility, allow_extract, print, modify);
+ this->encrypt_use_aes = true;
+ this->encrypt_metadata = encrypt_metadata;
+ setEncryptionParameters(user_password, owner_password, 5, 6, 32, clear);
+}
+
+void
QPDFWriter::interpretR3EncryptionParameters(
std::set<int>& clear,
char const* user_password, char const* owner_password,
@@ -383,6 +460,12 @@ QPDFWriter::setEncryptionParameters(
bits_to_clear.insert(1);
bits_to_clear.insert(2);
+ if (R > 3)
+ {
+ // Bit 10 is deprecated and should always be set.
+ bits_to_clear.erase(10);
+ }
+
int P = 0;
// Create the complement of P, then invert.
for (std::set<int>::iterator iter = bits_to_clear.begin();
@@ -395,11 +478,26 @@ QPDFWriter::setEncryptionParameters(
generateID();
std::string O;
std::string U;
- QPDF::compute_encryption_O_U(
- user_password, owner_password, V, R, key_len, P,
- this->encrypt_metadata, this->id1, O, U);
+ std::string OE;
+ std::string UE;
+ std::string Perms;
+ std::string encryption_key;
+ if (V < 5)
+ {
+ QPDF::compute_encryption_O_U(
+ user_password, owner_password, V, R, key_len, P,
+ this->encrypt_metadata, this->id1, O, U);
+ }
+ else
+ {
+ QPDF::compute_encryption_parameters_V5(
+ user_password, owner_password, V, R, key_len, P,
+ this->encrypt_metadata, this->id1,
+ encryption_key, O, U, OE, UE, Perms);
+ }
setEncryptionParametersInternal(
- V, R, key_len, P, O, U, this->id1, user_password);
+ V, R, key_len, P, O, U, OE, UE, Perms,
+ this->id1, user_password, encryption_key);
}
void
@@ -427,32 +525,31 @@ QPDFWriter::copyEncryptionParameters(QPDF& qpdf)
}
if (V >= 4)
{
- if (encrypt.hasKey("/CF") &&
- encrypt.getKey("/CF").isDictionary() &&
- encrypt.hasKey("/StmF") &&
- encrypt.getKey("/StmF").isName())
- {
- // Determine whether to use AES from StmF. QPDFWriter
- // can't write files with different StrF and StmF.
- QPDFObjectHandle CF = encrypt.getKey("/CF");
- QPDFObjectHandle StmF = encrypt.getKey("/StmF");
- if (CF.hasKey(StmF.getName()) &&
- CF.getKey(StmF.getName()).isDictionary())
- {
- QPDFObjectHandle StmF_data = CF.getKey(StmF.getName());
- if (StmF_data.hasKey("/CFM") &&
- StmF_data.getKey("/CFM").isName() &&
- StmF_data.getKey("/CFM").getName() == "/AESV2")
- {
- this->encrypt_use_aes = true;
- }
- }
- }
+ // When copying encryption parameters, use AES even if the
+ // original file did not. Acrobat doesn't create files
+ // with V >= 4 that don't use AES, and the logic of
+ // figuring out whether AES is used or not is complicated
+ // with /StmF, /StrF, and /EFF all potentially having
+ // different values.
+ this->encrypt_use_aes = true;
}
QTC::TC("qpdf", "QPDFWriter copy encrypt metadata",
this->encrypt_metadata ? 0 : 1);
QTC::TC("qpdf", "QPDFWriter copy use_aes",
this->encrypt_use_aes ? 0 : 1);
+ std::string OE;
+ std::string UE;
+ std::string Perms;
+ std::string encryption_key;
+ if (V >= 5)
+ {
+ QTC::TC("qpdf", "QPDFWriter copy V5");
+ OE = encrypt.getKey("/OE").getStringValue();
+ UE = encrypt.getKey("/UE").getStringValue();
+ Perms = encrypt.getKey("/Perms").getStringValue();
+ encryption_key = qpdf.getEncryptionKey();
+ }
+
setEncryptionParametersInternal(
V,
encrypt.getKey("/R").getIntValue(),
@@ -460,13 +557,18 @@ QPDFWriter::copyEncryptionParameters(QPDF& qpdf)
encrypt.getKey("/P").getIntValue(),
encrypt.getKey("/O").getStringValue(),
encrypt.getKey("/U").getStringValue(),
+ OE,
+ UE,
+ Perms,
this->id1, // this->id1 == the other file's id1
- qpdf.getPaddedUserPassword());
+ qpdf.getPaddedUserPassword(),
+ encryption_key);
}
}
void
-QPDFWriter::disableIncompatibleEncryption(int major, int minor)
+QPDFWriter::disableIncompatibleEncryption(int major, int minor,
+ int extension_level)
{
if (! this->encrypted)
{
@@ -503,6 +605,15 @@ QPDFWriter::disableIncompatibleEncryption(int major, int minor)
disable = true;
}
}
+ else if ((compareVersions(major, minor, 1, 7) < 0) ||
+ ((compareVersions(major, minor, 1, 7) == 0) &&
+ extension_level < 3))
+ {
+ if ((V >= 5) || (R >= 5))
+ {
+ disable = true;
+ }
+ }
}
if (disable)
{
@@ -562,8 +673,12 @@ void
QPDFWriter::setEncryptionParametersInternal(
int V, int R, int key_len, long P,
std::string const& O, std::string const& U,
- std::string const& id1, std::string const& user_password)
+ std::string const& OE, std::string const& UE, std::string const& Perms,
+ std::string const& id1, std::string const& user_password,
+ std::string const& encryption_key)
{
+ this->encryption_V = V;
+ this->encryption_R = R;
encryption_dictionary["/Filter"] = "/Standard";
encryption_dictionary["/V"] = QUtil::int_to_string(V);
encryption_dictionary["/Length"] = QUtil::int_to_string(key_len * 8);
@@ -571,44 +686,71 @@ QPDFWriter::setEncryptionParametersInternal(
encryption_dictionary["/P"] = QUtil::int_to_string(P);
encryption_dictionary["/O"] = QPDF_String(O).unparse(true);
encryption_dictionary["/U"] = QPDF_String(U).unparse(true);
- setMinimumPDFVersion("1.3");
- if (R == 3)
+ if (V >= 5)
{
- setMinimumPDFVersion("1.4");
+ encryption_dictionary["/OE"] = QPDF_String(OE).unparse(true);
+ encryption_dictionary["/UE"] = QPDF_String(UE).unparse(true);
+ encryption_dictionary["/Perms"] = QPDF_String(Perms).unparse(true);
+ }
+ if (R >= 6)
+ {
+ setMinimumPDFVersion("1.7", 8);
+ }
+ else if (R == 5)
+ {
+ setMinimumPDFVersion("1.7", 3);
}
- else if (R >= 4)
+ else if (R == 4)
{
setMinimumPDFVersion(this->encrypt_use_aes ? "1.6" : "1.5");
}
+ else if (R == 3)
+ {
+ setMinimumPDFVersion("1.4");
+ }
+ else
+ {
+ setMinimumPDFVersion("1.3");
+ }
if ((R >= 4) && (! encrypt_metadata))
{
encryption_dictionary["/EncryptMetadata"] = "false";
}
- if (V == 4)
+ if ((V == 4) || (V == 5))
{
// The spec says the value for the crypt filter key can be
// anything, and xpdf seems to agree. However, Adobe Reader
// won't open our files unless we use /StdCF.
encryption_dictionary["/StmF"] = "/StdCF";
encryption_dictionary["/StrF"] = "/StdCF";
- std::string method = (this->encrypt_use_aes ? "/AESV2" : "/V2");
+ std::string method = (this->encrypt_use_aes
+ ? ((V < 5) ? "/AESV2" : "/AESV3")
+ : "/V2");
encryption_dictionary["/CF"] =
"<< /StdCF << /AuthEvent /DocOpen /CFM " + method + " >> >>";
}
this->encrypted = true;
QPDF::EncryptionData encryption_data(
- V, R, key_len, P, O, U, id1, this->encrypt_metadata);
- this->encryption_key = QPDF::compute_encryption_key(
- user_password, encryption_data);
+ V, R, key_len, P, O, U, OE, UE, Perms, id1, this->encrypt_metadata);
+ if (V < 5)
+ {
+ this->encryption_key = QPDF::compute_encryption_key(
+ user_password, encryption_data);
+ }
+ else
+ {
+ this->encryption_key = encryption_key;
+ }
}
void
QPDFWriter::setDataKey(int objid)
{
this->cur_data_key = QPDF::compute_data_key(
- this->encryption_key, objid, 0, this->encrypt_use_aes);
+ this->encryption_key, objid, 0,
+ this->encrypt_use_aes, this->encryption_V, this->encryption_R);
}
int
@@ -745,13 +887,14 @@ QPDFWriter::pushEncryptionFilter()
{
p = new Pl_AES_PDF(
"aes stream encryption", this->pipeline, true,
- (unsigned char*) this->cur_data_key.c_str());
+ (unsigned char*) this->cur_data_key.c_str(),
+ (unsigned int)this->cur_data_key.length());
}
else
{
p = new Pl_RC4("rc4 stream encryption", this->pipeline,
(unsigned char*) this->cur_data_key.c_str(),
- (int)this->cur_data_key.length());
+ (unsigned int)this->cur_data_key.length());
}
pushPipeline(p);
}
@@ -827,16 +970,6 @@ QPDFWriter::enqueueObject(QPDFObjectHandle object)
{
// This is a place-holder object for an object stream
}
- else if (object.isScalar())
- {
- // flattenScalarReferences is supposed to have removed all
- // indirect scalars.
- throw std::logic_error(
- "INTERNAL ERROR: QPDFWriter::enqueueObject: indirect scalar: " +
- std::string(this->filename) + " " +
- QUtil::int_to_string(object.getObjectID()) + " " +
- QUtil::int_to_string(object.getGeneration()));
- }
int objid = object.getObjectID();
if (obj_renumber.count(objid) == 0)
@@ -909,15 +1042,6 @@ QPDFWriter::unparseChild(QPDFObjectHandle child, int level, int flags)
}
if (child.isIndirect())
{
- if (child.isScalar())
- {
- // flattenScalarReferences is supposed to have removed all
- // indirect scalars.
- throw std::logic_error(
- "INTERNAL ERROR: QPDFWriter::unparseChild: indirect scalar: " +
- QUtil::int_to_string(child.getObjectID()) + " " +
- QUtil::int_to_string(child.getGeneration()));
- }
int old_id = child.getObjectID();
int new_id = obj_renumber[old_id];
writeString(QUtil::int_to_string(new_id));
@@ -933,7 +1057,7 @@ void
QPDFWriter::writeTrailer(trailer_e which, int size, bool xref_stream,
qpdf_offset_t prev)
{
- QPDFObjectHandle trailer = pdf.getTrailer();
+ QPDFObjectHandle trailer = getTrimmedTrailer();
if (! xref_stream)
{
writeString("trailer <<");
@@ -1011,6 +1135,7 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
unsigned int flags, size_t stream_length,
bool compress)
{
+ int old_id = object.getObjectID();
unsigned int child_flags = flags & ~f_stream;
std::string indent;
@@ -1043,32 +1168,199 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
}
else if (object.isDictionary())
{
+ // Make a shallow copy of this object so we can modify it
+ // safely without affecting the original. This code makes
+ // assumptions about things that are made true in
+ // prepareFileForWrite, such as that certain things are direct
+ // objects so that replacing them doesn't leave unreferenced
+ // objects in the output.
+ object = object.shallowCopy();
+
+ // Handle special cases for specific dictionaries.
+
+ // Extensions dictionaries.
+
+ // We have one of several cases:
+ //
+ // * We need ADBE
+ // - We already have Extensions
+ // - If it has the right ADBE, preserve it
+ // - Otherwise, replace ADBE
+ // - We don't have Extensions: create one from scratch
+ // * We don't want ADBE
+ // - We already have Extensions
+ // - If it only has ADBE, remove it
+ // - If it has other things, keep those and remove ADBE
+ // - We have no extensions: no action required
+ //
+ // Before writing, we guarantee that /Extensions, if present,
+ // is direct through the ADBE dictionary, so we can modify in
+ // place.
+
+ bool is_root = false;
+ bool have_extensions_other = false;
+ bool have_extensions_adbe = false;
+
+ QPDFObjectHandle extensions;
+ if (old_id == pdf.getRoot().getObjectID())
+ {
+ is_root = true;
+ if (object.hasKey("/Extensions") &&
+ object.getKey("/Extensions").isDictionary())
+ {
+ extensions = object.getKey("/Extensions");
+ }
+ }
+
+ if (extensions.isInitialized())
+ {
+ std::set<std::string> keys = extensions.getKeys();
+ if (keys.count("/ADBE") > 0)
+ {
+ have_extensions_adbe = true;
+ keys.erase("/ADBE");
+ }
+ if (keys.size() > 0)
+ {
+ have_extensions_other = true;
+ }
+ }
+
+ bool need_extensions_adbe = (this->final_extension_level > 0);
+
+ if (is_root)
+ {
+ if (need_extensions_adbe)
+ {
+ if (! (have_extensions_other || have_extensions_adbe))
+ {
+ // We need Extensions and don't have it. Create
+ // it here.
+ QTC::TC("qpdf", "QPDFWriter create Extensions",
+ this->qdf_mode ? 0 : 1);
+ extensions = QPDFObjectHandle::newDictionary();
+ object.replaceKey("/Extensions", extensions);
+ }
+ }
+ else if (! have_extensions_other)
+ {
+ // We have Extensions dictionary and don't want one.
+ if (have_extensions_adbe)
+ {
+ QTC::TC("qpdf", "QPDFWriter remove existing Extensions");
+ object.removeKey("/Extensions");
+ extensions = QPDFObjectHandle(); // uninitialized
+ }
+ }
+ }
+
+ if (extensions.isInitialized())
+ {
+ QTC::TC("qpdf", "QPDFWriter preserve Extensions");
+ QPDFObjectHandle adbe = extensions.getKey("/ADBE");
+ if (adbe.isDictionary() &&
+ adbe.hasKey("/BaseVersion") &&
+ adbe.getKey("/BaseVersion").isName() &&
+ (adbe.getKey("/BaseVersion").getName() ==
+ "/" + this->final_pdf_version) &&
+ adbe.hasKey("/ExtensionLevel") &&
+ adbe.getKey("/ExtensionLevel").isInteger() &&
+ (adbe.getKey("/ExtensionLevel").getIntValue() ==
+ this->final_extension_level))
+ {
+ QTC::TC("qpdf", "QPDFWriter preserve ADBE");
+ }
+ else
+ {
+ if (need_extensions_adbe)
+ {
+ extensions.replaceKey(
+ "/ADBE",
+ QPDFObjectHandle::parse(
+ "<< /BaseVersion /" + this->final_pdf_version +
+ " /ExtensionLevel " +
+ QUtil::int_to_string(this->final_extension_level) +
+ " >>"));
+ }
+ else
+ {
+ QTC::TC("qpdf", "QPDFWriter remove ADBE");
+ extensions.removeKey("/ADBE");
+ }
+ }
+ }
+
+ // Stream dictionaries.
+
+ if (flags & f_stream)
+ {
+ // Suppress /Length since we will write it manually
+ object.removeKey("/Length");
+
+ if (flags & f_filtered)
+ {
+ // We will supply our own filter and decode
+ // parameters.
+ object.removeKey("/Filter");
+ object.removeKey("/DecodeParms");
+ }
+ else
+ {
+ // Make sure, no matter what else we have, that we
+ // don't have /Crypt in the output filters.
+ QPDFObjectHandle filter = object.getKey("/Filter");
+ QPDFObjectHandle decode_parms = object.getKey("/DecodeParms");
+ if (filter.isOrHasName("/Crypt"))
+ {
+ if (filter.isName())
+ {
+ object.removeKey("/Filter");
+ object.removeKey("/DecodeParms");
+ }
+ else
+ {
+ int idx = -1;
+ for (int i = 0; i < filter.getArrayNItems(); ++i)
+ {
+ QPDFObjectHandle item = filter.getArrayItem(i);
+ if (item.isName() && item.getName() == "/Crypt")
+ {
+ idx = i;
+ break;
+ }
+ }
+ if (idx >= 0)
+ {
+ // If filter is an array, then the code in
+ // QPDF_Stream has already verified that
+ // DecodeParms and Filters are arrays of
+ // the same length, but if they weren't
+ // for some reason, eraseItem does type
+ // and bounds checking.
+ QTC::TC("qpdf", "QPDFWriter remove Crypt");
+ filter.eraseItem(idx);
+ decode_parms.eraseItem(idx);
+ }
+ }
+ }
+ }
+ }
+
writeString("<<");
writeStringQDF("\n");
+
std::set<std::string> keys = object.getKeys();
for (std::set<std::string>::iterator iter = keys.begin();
iter != keys.end(); ++iter)
{
- // I'm not fully clear on /Crypt keys in /DecodeParms. If
- // one is found, we refuse to filter, so we should be
- // safe.
std::string const& key = *iter;
- if ((flags & f_filtered) &&
- ((key == "/Filter") ||
- (key == "/DecodeParms")))
- {
- continue;
- }
- if ((flags & f_stream) && (key == "/Length"))
- {
- continue;
- }
+
writeStringQDF(indent);
writeStringQDF(" ");
writeStringNoQDF(" ");
writeString(QPDF_Name::normalizeName(key));
writeString(" ");
- unparseChild(object.getKey(key), level + 1, child_flags);
+ unparseChild(object.getKey(key), level + 1, child_flags);
writeStringQDF("\n");
}
@@ -1105,7 +1397,6 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
else if (object.isStream())
{
// Write stream data to a buffer.
- int old_id = object.getObjectID();
int new_id = obj_renumber[old_id];
if (! this->direct_stream_lengths)
{
@@ -1214,7 +1505,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
{
Pl_Buffer bufpl("encrypted string");
Pl_AES_PDF pl("aes encrypt string", &bufpl, true,
- (unsigned char const*)this->cur_data_key.c_str());
+ (unsigned char const*)this->cur_data_key.c_str(),
+ (unsigned int)this->cur_data_key.length());
pl.write((unsigned char*) val.c_str(), val.length());
pl.finish();
Buffer* buf = bufpl.getBuffer();
@@ -1640,6 +1932,151 @@ QPDFWriter::generateObjectStreams()
}
}
+QPDFObjectHandle
+QPDFWriter::getTrimmedTrailer()
+{
+ // Remove keys from the trailer that necessarily have to be
+ // replaced when writing the file.
+
+ QPDFObjectHandle trailer = pdf.getTrailer().shallowCopy();
+
+ // Remove encryption keys
+ trailer.removeKey("/ID");
+ trailer.removeKey("/Encrypt");
+
+ // Remove modification information
+ trailer.removeKey("/Prev");
+
+ // Remove all trailer keys that potentially come from a
+ // cross-reference stream
+ trailer.removeKey("/Index");
+ trailer.removeKey("/W");
+ trailer.removeKey("/Length");
+ trailer.removeKey("/Filter");
+ trailer.removeKey("/DecodeParms");
+ trailer.removeKey("/Type");
+ trailer.removeKey("/XRefStm");
+
+ return trailer;
+}
+
+void
+QPDFWriter::prepareFileForWrite()
+{
+ // Do a traversal of the entire PDF file structure replacing all
+ // indirect objects that QPDFWriter wants to be direct. This
+ // includes stream lengths, stream filtering parameters, and
+ // document extension level information. Also replace all
+ // indirect null references with direct nulls. This way, the only
+ // indirect nulls queued for output will be object stream place
+ // holders.
+
+ std::list<QPDFObjectHandle> queue;
+ queue.push_back(getTrimmedTrailer());
+ std::set<int> visited;
+
+ while (! queue.empty())
+ {
+ QPDFObjectHandle node = queue.front();
+ queue.pop_front();
+ if (node.isIndirect())
+ {
+ if (visited.count(node.getObjectID()) > 0)
+ {
+ continue;
+ }
+ visited.insert(node.getObjectID());
+ }
+
+ if (node.isArray())
+ {
+ int nitems = node.getArrayNItems();
+ for (int i = 0; i < nitems; ++i)
+ {
+ QPDFObjectHandle oh = node.getArrayItem(i);
+ if (oh.isIndirect() && oh.isNull())
+ {
+ QTC::TC("qpdf", "QPDFWriter flatten array null");
+ oh.makeDirect();
+ node.setArrayItem(i, oh);
+ }
+ else if (! oh.isScalar())
+ {
+ queue.push_back(oh);
+ }
+ }
+ }
+ else if (node.isDictionary() || node.isStream())
+ {
+ bool is_stream = false;
+ bool is_root = false;
+ QPDFObjectHandle dict = node;
+ if (node.isStream())
+ {
+ is_stream = true;
+ dict = node.getDict();
+ }
+ else if (pdf.getRoot().getObjectID() == node.getObjectID())
+ {
+ is_root = true;
+ }
+
+ std::set<std::string> keys = dict.getKeys();
+ for (std::set<std::string>::iterator iter = keys.begin();
+ iter != keys.end(); ++iter)
+ {
+ std::string const& key = *iter;
+ QPDFObjectHandle oh = dict.getKey(key);
+ bool add_to_queue = true;
+ if (is_stream)
+ {
+ if (oh.isIndirect() &&
+ ((key == "/Length") ||
+ (key == "/Filter") ||
+ (key == "/DecodeParms")))
+ {
+ QTC::TC("qpdf", "QPDFWriter make stream key direct");
+ add_to_queue = false;
+ oh.makeDirect();
+ dict.replaceKey(key, oh);
+ }
+ }
+ else if (is_root)
+ {
+ if ((key == "/Extensions") && (oh.isDictionary()))
+ {
+ bool extensions_indirect = false;
+ if (oh.isIndirect())
+ {
+ QTC::TC("qpdf", "QPDFWriter make Extensions direct");
+ extensions_indirect = true;
+ add_to_queue = false;
+ oh = oh.shallowCopy();
+ dict.replaceKey(key, oh);
+ }
+ if (oh.hasKey("/ADBE"))
+ {
+ QPDFObjectHandle adbe = oh.getKey("/ADBE");
+ if (adbe.isIndirect())
+ {
+ QTC::TC("qpdf", "QPDFWriter make ADBE direct",
+ extensions_indirect ? 0 : 1);
+ adbe.makeDirect();
+ oh.replaceKey("/ADBE", adbe);
+ }
+ }
+ }
+ }
+
+ if (add_to_queue)
+ {
+ queue.push_back(oh);
+ }
+ }
+ }
+ }
+}
+
void
QPDFWriter::write()
{
@@ -1686,7 +2123,8 @@ QPDFWriter::write()
int major = 0;
int minor = 0;
parseVersion(this->forced_pdf_version, major, minor);
- disableIncompatibleEncryption(major, minor);
+ disableIncompatibleEncryption(major, minor,
+ this->forced_extension_level);
if (compareVersions(major, minor, 1, 5) < 0)
{
QTC::TC("qpdf", "QPDFWriter forcing object stream disable");
@@ -1778,8 +2216,7 @@ QPDFWriter::write()
generateID();
- pdf.trimTrailerForWrite();
- pdf.flattenScalarReferences();
+ prepareFileForWrite();
if (this->linearized)
{
@@ -1834,16 +2271,18 @@ QPDFWriter::writeEncryptionDictionary()
void
QPDFWriter::writeHeader()
{
- setMinimumPDFVersion(pdf.getPDFVersion());
- std::string version = this->min_pdf_version;
+ setMinimumPDFVersion(pdf.getPDFVersion(), pdf.getExtensionLevel());
+ this->final_pdf_version = this->min_pdf_version;
+ this->final_extension_level = this->min_extension_level;
if (! this->forced_pdf_version.empty())
{
QTC::TC("qpdf", "QPDFWriter using forced PDF version");
- version = this->forced_pdf_version;
+ this->final_pdf_version = this->forced_pdf_version;
+ this->final_extension_level = this->forced_extension_level;
}
writeString("%PDF-");
- writeString(version);
+ writeString(this->final_pdf_version);
// This string of binary characters would not be valid UTF-8, so
// it really should be treated as binary.
writeString("\n%\xbf\xf7\xa2\xfe\n");
@@ -2427,7 +2866,7 @@ QPDFWriter::writeStandard()
writeString(this->extra_header_text);
// Put root first on queue.
- QPDFObjectHandle trailer = pdf.getTrailer();
+ QPDFObjectHandle trailer = getTrimmedTrailer();
enqueueObject(trailer.getKey("/Root"));
// Next place any other objects referenced from the trailer
diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc
index 970ee58..88b8e8f 100644
--- a/libqpdf/QPDF_Stream.cc
+++ b/libqpdf/QPDF_Stream.cc
@@ -91,6 +91,80 @@ QPDF_Stream::getRawStreamData()
}
bool
+QPDF_Stream::understandDecodeParams(
+ std::string const& filter, QPDFObjectHandle decode_obj,
+ int& predictor, int& columns, bool& early_code_change)
+{
+ bool filterable = true;
+ std::set<std::string> keys = decode_obj.getKeys();
+ for (std::set<std::string>::iterator iter = keys.begin();
+ iter != keys.end(); ++iter)
+ {
+ std::string const& key = *iter;
+ if ((filter == "/FlateDecode") && (key == "/Predictor"))
+ {
+ QPDFObjectHandle predictor_obj = decode_obj.getKey(key);
+ if (predictor_obj.isInteger())
+ {
+ predictor = predictor_obj.getIntValue();
+ if (! ((predictor == 1) || (predictor == 12)))
+ {
+ filterable = false;
+ }
+ }
+ else
+ {
+ filterable = false;
+ }
+ }
+ else if ((filter == "/LZWDecode") && (key == "/EarlyChange"))
+ {
+ QPDFObjectHandle earlychange_obj = decode_obj.getKey(key);
+ if (earlychange_obj.isInteger())
+ {
+ int earlychange = earlychange_obj.getIntValue();
+ early_code_change = (earlychange == 1);
+ if (! ((earlychange == 0) || (earlychange == 1)))
+ {
+ filterable = false;
+ }
+ }
+ else
+ {
+ filterable = false;
+ }
+ }
+ else if (key == "/Columns")
+ {
+ QPDFObjectHandle columns_obj = decode_obj.getKey(key);
+ if (columns_obj.isInteger())
+ {
+ columns = columns_obj.getIntValue();
+ }
+ else
+ {
+ filterable = false;
+ }
+ }
+ else if ((filter == "/Crypt") &&
+ (((key == "/Type") || (key == "/Name")) &&
+ (decode_obj.getKey("/Type").isNull() ||
+ (decode_obj.getKey("/Type").isName() &&
+ (decode_obj.getKey("/Type").getName() ==
+ "/CryptFilterDecodeParms")))))
+ {
+ // we handle this in decryptStream
+ }
+ else
+ {
+ filterable = false;
+ }
+ }
+
+ return filterable;
+}
+
+bool
QPDF_Stream::filterable(std::vector<std::string>& filters,
int& predictor, int& columns,
bool& early_code_change)
@@ -110,106 +184,6 @@ QPDF_Stream::filterable(std::vector<std::string>& filters,
filter_abbreviations["/DCT"] = "/DCTDecode";
}
- // Initialize values to their defaults as per the PDF spec
- predictor = 1;
- columns = 0;
- early_code_change = true;
-
- bool filterable = true;
-
- // See if we can support any decode parameters that are specified.
-
- QPDFObjectHandle decode_obj =
- this->stream_dict.getKey("/DecodeParms");
- if (decode_obj.isNull())
- {
- // no problem
- }
- else if (decode_obj.isDictionary())
- {
- std::set<std::string> keys = decode_obj.getKeys();
- for (std::set<std::string>::iterator iter = keys.begin();
- iter != keys.end(); ++iter)
- {
- std::string const& key = *iter;
- if (key == "/Predictor")
- {
- QPDFObjectHandle predictor_obj = decode_obj.getKey(key);
- if (predictor_obj.isInteger())
- {
- predictor = predictor_obj.getIntValue();
- if (! ((predictor == 1) || (predictor == 12)))
- {
- filterable = false;
- }
- }
- else
- {
- filterable = false;
- }
- }
- else if (key == "/EarlyChange")
- {
- QPDFObjectHandle earlychange_obj = decode_obj.getKey(key);
- if (earlychange_obj.isInteger())
- {
- int earlychange = earlychange_obj.getIntValue();
- early_code_change = (earlychange == 1);
- if (! ((earlychange == 0) || (earlychange == 1)))
- {
- filterable = false;
- }
- }
- else
- {
- filterable = false;
- }
- }
- else if (key == "/Columns")
- {
- QPDFObjectHandle columns_obj = decode_obj.getKey(key);
- if (columns_obj.isInteger())
- {
- columns = columns_obj.getIntValue();
- }
- else
- {
- filterable = false;
- }
- }
- else if (((key == "/Type") || (key == "/Name")) &&
- decode_obj.getKey("/Type").isName() &&
- (decode_obj.getKey("/Type").getName() ==
- "/CryptFilterDecodeParms"))
- {
- // we handle this in decryptStream
- }
- else
- {
- filterable = false;
- }
- }
- }
- else
- {
- // Ignore for now -- some filter types, like CCITTFaxDecode,
- // use types other than dictionary for this.
- QTC::TC("qpdf", "QPDF_Stream ignore non-dictionary DecodeParms");
-
- filterable = false;
- }
-
- if ((predictor > 1) && (columns == 0))
- {
- // invalid
- filterable = false;
- }
-
- if (! filterable)
- {
- return false;
- }
-
// Check filters
QPDFObjectHandle filter_obj = this->stream_dict.getKey("/Filter");
@@ -254,8 +228,7 @@ QPDF_Stream::filterable(std::vector<std::string>& filters,
"stream filter type is not name or array");
}
- // `filters' now contains a list of filters to be applied in
- // order. See which ones we can support.
+ bool filterable = true;
for (std::vector<std::string>::iterator iter = filters.begin();
iter != filters.end(); ++iter)
@@ -278,6 +251,79 @@ QPDF_Stream::filterable(std::vector<std::string>& filters,
}
}
+ if (! filterable)
+ {
+ return false;
+ }
+
+ // `filters' now contains a list of filters to be applied in
+ // order. See which ones we can support.
+
+ // Initialize values to their defaults as per the PDF spec
+ predictor = 1;
+ columns = 0;
+ early_code_change = true;
+
+ // See if we can support any decode parameters that are specified.
+
+ QPDFObjectHandle decode_obj = this->stream_dict.getKey("/DecodeParms");
+ std::vector<QPDFObjectHandle> decode_parms;
+ if (decode_obj.isArray())
+ {
+ for (int i = 0; i < decode_obj.getArrayNItems(); ++i)
+ {
+ decode_parms.push_back(decode_obj.getArrayItem(i));
+ }
+ }
+ else
+ {
+ for (unsigned int i = 0; i < filters.size(); ++i)
+ {
+ decode_parms.push_back(decode_obj);
+ }
+ }
+
+ if (decode_parms.size() != filters.size())
+ {
+ throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(),
+ "", this->offset,
+ "stream /DecodeParms length is"
+ " inconsistent with filters");
+ }
+
+ for (unsigned int i = 0; i < filters.size(); ++i)
+ {
+ QPDFObjectHandle decode_item = decode_parms[i];
+ if (decode_item.isNull())
+ {
+ // okay
+ }
+ else if (decode_item.isDictionary())
+ {
+ if (! understandDecodeParams(
+ filters[i], decode_item,
+ predictor, columns, early_code_change))
+ {
+ filterable = false;
+ }
+ }
+ else
+ {
+ filterable = false;
+ }
+ }
+
+ if ((predictor > 1) && (columns == 0))
+ {
+ // invalid
+ filterable = false;
+ }
+
+ if (! filterable)
+ {
+ return false;
+ }
+
return filterable;
}
diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc
index c73a47b..60d54b7 100644
--- a/libqpdf/QPDF_encryption.cc
+++ b/libqpdf/QPDF_encryption.cc
@@ -10,6 +10,7 @@
#include <qpdf/Pl_RC4.hh>
#include <qpdf/Pl_AES_PDF.hh>
#include <qpdf/Pl_Buffer.hh>
+#include <qpdf/Pl_SHA2.hh>
#include <qpdf/RC4.hh>
#include <qpdf/MD5.hh>
@@ -23,11 +24,110 @@ static unsigned char const padding_string[] = {
0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a
};
-static unsigned int const O_key_bytes = sizeof(MD5::Digest);
static unsigned int const key_bytes = 32;
+// V4 key lengths apply to V <= 4
+static unsigned int const OU_key_bytes_V4 = sizeof(MD5::Digest);
+
+static unsigned int const OU_key_bytes_V5 = 48;
+static unsigned int const OUE_key_bytes_V5 = 32;
+static unsigned int const Perms_key_bytes_V5 = 16;
+
+int
+QPDF::EncryptionData::getV() const
+{
+ return this->V;
+}
+
+int
+QPDF::EncryptionData::getR() const
+{
+ return this->R;
+}
+
+int
+QPDF::EncryptionData::getLengthBytes() const
+{
+ return this->Length_bytes;
+}
+
+int
+QPDF::EncryptionData::getP() const
+{
+ return this->P;
+}
+
+std::string const&
+QPDF::EncryptionData::getO() const
+{
+ return this->O;
+}
+
+std::string const&
+QPDF::EncryptionData::getU() const
+{
+ return this->U;
+}
+
+std::string const&
+QPDF::EncryptionData::getOE() const
+{
+ return this->OE;
+}
+
+std::string const&
+QPDF::EncryptionData::getUE() const
+{
+ return this->UE;
+}
+
+std::string const&
+QPDF::EncryptionData::getPerms() const
+{
+ return this->Perms;
+}
+
+std::string const&
+QPDF::EncryptionData::getId1() const
+{
+ return this->id1;
+}
+
+bool
+QPDF::EncryptionData::getEncryptMetadata() const
+{
+ return this->encrypt_metadata;
+}
+
void
-pad_or_truncate_password(std::string const& password, char k1[key_bytes])
+QPDF::EncryptionData::setO(std::string const& O)
+{
+ this->O = O;
+}
+
+void
+QPDF::EncryptionData::setU(std::string const& U)
+{
+ this->U = U;
+}
+
+void
+QPDF::EncryptionData::setV5EncryptionParameters(
+ std::string const& O,
+ std::string const& OE,
+ std::string const& U,
+ std::string const& UE,
+ std::string const& Perms)
+{
+ this->O = O;
+ this->OE = OE;
+ this->U = U;
+ this->UE = UE;
+ this->Perms = Perms;
+}
+
+static void
+pad_or_truncate_password_V4(std::string const& password, char k1[key_bytes])
{
int password_bytes = std::min(key_bytes, (unsigned int)password.length());
int pad_bytes = key_bytes - password_bytes;
@@ -48,25 +148,37 @@ QPDF::trim_user_password(std::string& user_password)
return;
}
- char const* p = 0;
- while ((p = strchr(cstr, '\x28')) != 0)
+ char const* p1 = cstr;
+ char const* p2 = 0;
+ while ((p2 = strchr(p1, '\x28')) != 0)
{
- if (memcmp(p, padding_string, len - (p - cstr)) == 0)
+ if (memcmp(p2, padding_string, len - (p2 - cstr)) == 0)
{
- user_password = user_password.substr(0, p - cstr);
+ user_password = user_password.substr(0, p2 - cstr);
return;
}
+ else
+ {
+ QTC::TC("qpdf", "QPDF_encryption skip 0x28");
+ p1 = p2 + 1;
+ }
}
}
static std::string
-pad_or_truncate_password(std::string const& password)
+pad_or_truncate_password_V4(std::string const& password)
{
char k1[key_bytes];
- pad_or_truncate_password(password, k1);
+ pad_or_truncate_password_V4(password, k1);
return std::string(k1, key_bytes);
}
+static std::string
+truncate_password_V5(std::string const& password)
+{
+ return password.substr(0, std::min((size_t)127, password.length()));
+}
+
static void
iterate_md5_digest(MD5& md5, MD5::Digest& digest, int iterations)
{
@@ -100,15 +212,146 @@ iterate_rc4(unsigned char* data, int data_len,
delete [] key;
}
+static std::string
+process_with_aes(std::string const& key,
+ bool encrypt,
+ std::string const& data,
+ size_t outlength = 0,
+ unsigned int repetitions = 1,
+ unsigned char const* iv = 0,
+ size_t iv_length = 0)
+{
+ Pl_Buffer buffer("buffer");
+ Pl_AES_PDF aes("aes", &buffer, encrypt,
+ (unsigned char const*)key.c_str(),
+ (unsigned int)key.length());
+ if (iv)
+ {
+ aes.setIV(iv, iv_length);
+ }
+ else
+ {
+ aes.useZeroIV();
+ }
+ aes.disablePadding();
+ for (unsigned int i = 0; i < repetitions; ++i)
+ {
+ aes.write((unsigned char*)data.c_str(), data.length());
+ }
+ aes.finish();
+ PointerHolder<Buffer> bufp = buffer.getBuffer();
+ if (outlength == 0)
+ {
+ outlength = bufp->getSize();
+ }
+ else
+ {
+ outlength = std::min(outlength, bufp->getSize());
+ }
+ return std::string((char const*)bufp->getBuffer(), outlength);
+}
+
+static std::string
+hash_V5(std::string const& password,
+ std::string const& salt,
+ std::string const& udata,
+ QPDF::EncryptionData const& data)
+{
+ Pl_SHA2 hash(256);
+ hash.write((unsigned char*)password.c_str(), password.length());
+ hash.write((unsigned char*)salt.c_str(), salt.length());
+ hash.write((unsigned char*)udata.c_str(), udata.length());
+ hash.finish();
+ std::string K = hash.getRawDigest();
+
+ std::string result;
+ if (data.getR() < 6)
+ {
+ result = K;
+ }
+ else
+ {
+ // Algorithm 2.B from ISO 32000-1 chapter 7: Computing a hash
+
+ int round_number = 0;
+ bool done = false;
+ while (! done)
+ {
+ // The hash algorithm has us setting K initially to the R5
+ // value and then repeating a series of steps 64 times
+ // before starting with the termination case testing. The
+ // wording of the specification is very unclear as to the
+ // exact number of times it should be run since the
+ // wording about whether the initial setup counts as round
+ // 0 or not is ambiguous. This code counts the initial
+ // setup (R5) value as round 0, which appears to be
+ // correct. This was determined to be correct by
+ // increasing or decreasing the number of rounds by 1 or 2
+ // from this value and generating 20 test files. In this
+ // interpretation, all the test files worked with Adobe
+ // Reader X. In the other configurations, many of the
+ // files did not work, and we were accurately able to
+ // predict which files didn't work by looking at the
+ // conditions under which we terminated repetition.
+
+ ++round_number;
+ std::string K1 = password + K + udata;
+ assert(K.length() >= 32);
+ std::string E = process_with_aes(
+ K.substr(0, 16), true, K1, 0, 64,
+ (unsigned char*)K.substr(16, 16).c_str(), 16);
+
+ // E_mod_3 is supposed to be mod 3 of the first 16 bytes
+ // of E taken as as a (128-bit) big-endian number. Since
+ // (xy mod n) is equal to ((x mod n) + (y mod n)) mod n
+ // and since 256 mod n is 1, we can just take the sums of
+ // the the mod 3s of each byte to get the same result.
+ int E_mod_3 = 0;
+ for (unsigned int i = 0; i < 16; ++i)
+ {
+ E_mod_3 += (unsigned char)E[i];
+ }
+ E_mod_3 %= 3;
+ int next_hash = ((E_mod_3 == 0) ? 256 :
+ (E_mod_3 == 1) ? 384 :
+ 512);
+ Pl_SHA2 hash(next_hash);
+ hash.write((unsigned char*)E.c_str(), E.length());
+ hash.finish();
+ K = hash.getRawDigest();
+
+ if (round_number >= 64)
+ {
+ unsigned int ch = (unsigned int)((unsigned char) *(E.rbegin()));
+
+ if (ch <= (unsigned int)(round_number - 32))
+ {
+ done = true;
+ }
+ }
+ }
+ result = K.substr(0, 32);
+ }
+
+ return result;
+}
+
std::string
QPDF::compute_data_key(std::string const& encryption_key,
- int objid, int generation,
- bool use_aes)
+ int objid, int generation, bool use_aes,
+ int encryption_V, int encryption_R)
{
// Algorithm 3.1 from the PDF 1.7 Reference Manual
std::string result = encryption_key;
+ if (encryption_V >= 5)
+ {
+ // Algorithm 3.1a (PDF 1.7 extension level 3): just use
+ // encryption key straight.
+ return result;
+ }
+
// Append low three bytes of object ID and low two bytes of generation
result += (char) (objid & 0xff);
result += (char) ((objid >> 8) & 0xff);
@@ -132,36 +375,69 @@ std::string
QPDF::compute_encryption_key(
std::string const& password, EncryptionData const& data)
{
+ if (data.getV() >= 5)
+ {
+ // For V >= 5, the encryption key is generated and stored in
+ // the file, encrypted separately with both user and owner
+ // passwords.
+ return recover_encryption_key_with_password(password, data);
+ }
+ else
+ {
+ // For V < 5, the encryption key is derived from the user
+ // password.
+ return compute_encryption_key_from_password(password, data);
+ }
+}
+
+std::string
+QPDF::compute_encryption_key_from_password(
+ std::string const& password, EncryptionData const& data)
+{
// Algorithm 3.2 from the PDF 1.7 Reference Manual
+ // This code does not properly handle Unicode passwords.
+ // Passwords are supposed to be converted from OS codepage
+ // characters to PDFDocEncoding. Unicode passwords are supposed
+ // to be converted to OS codepage before converting to
+ // PDFDocEncoding. We instead require the password to be
+ // presented in its final form.
+
MD5 md5;
md5.encodeDataIncrementally(
- pad_or_truncate_password(password).c_str(), key_bytes);
- md5.encodeDataIncrementally(data.O.c_str(), key_bytes);
+ pad_or_truncate_password_V4(password).c_str(), key_bytes);
+ md5.encodeDataIncrementally(data.getO().c_str(), key_bytes);
char pbytes[4];
- pbytes[0] = (char) (data.P & 0xff);
- pbytes[1] = (char) ((data.P >> 8) & 0xff);
- pbytes[2] = (char) ((data.P >> 16) & 0xff);
- pbytes[3] = (char) ((data.P >> 24) & 0xff);
+ int P = data.getP();
+ pbytes[0] = (char) (P & 0xff);
+ pbytes[1] = (char) ((P >> 8) & 0xff);
+ pbytes[2] = (char) ((P >> 16) & 0xff);
+ pbytes[3] = (char) ((P >> 24) & 0xff);
md5.encodeDataIncrementally(pbytes, 4);
- md5.encodeDataIncrementally(data.id1.c_str(), (int)data.id1.length());
- if ((data.R >= 4) && (! data.encrypt_metadata))
+ md5.encodeDataIncrementally(data.getId1().c_str(),
+ (int)data.getId1().length());
+ if ((data.getR() >= 4) && (! data.getEncryptMetadata()))
{
char bytes[4];
memset(bytes, 0xff, 4);
md5.encodeDataIncrementally(bytes, 4);
}
MD5::Digest digest;
- iterate_md5_digest(md5, digest, ((data.R >= 3) ? 50 : 0));
- return std::string((char*)digest, data.Length_bytes);
+ iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0));
+ return std::string((char*)digest, data.getLengthBytes());
}
static void
compute_O_rc4_key(std::string const& user_password,
std::string const& owner_password,
QPDF::EncryptionData const& data,
- unsigned char key[O_key_bytes])
+ unsigned char key[OU_key_bytes_V4])
{
+ if (data.getV() >= 5)
+ {
+ throw std::logic_error(
+ "compute_O_rc4_key called for file with V >= 5");
+ }
std::string password = owner_password;
if (password.empty())
{
@@ -169,10 +445,10 @@ compute_O_rc4_key(std::string const& user_password,
}
MD5 md5;
md5.encodeDataIncrementally(
- pad_or_truncate_password(password).c_str(), key_bytes);
+ pad_or_truncate_password_V4(password).c_str(), key_bytes);
MD5::Digest digest;
- iterate_md5_digest(md5, digest, ((data.R >= 3) ? 50 : 0));
- memcpy(key, digest, O_key_bytes);
+ iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0));
+ memcpy(key, digest, OU_key_bytes_V4);
}
static std::string
@@ -182,13 +458,14 @@ compute_O_value(std::string const& user_password,
{
// Algorithm 3.3 from the PDF 1.7 Reference Manual
- unsigned char O_key[O_key_bytes];
+ unsigned char O_key[OU_key_bytes_V4];
compute_O_rc4_key(user_password, owner_password, data, O_key);
char upass[key_bytes];
- pad_or_truncate_password(user_password, upass);
+ pad_or_truncate_password_V4(user_password, upass);
iterate_rc4((unsigned char*) upass, key_bytes,
- O_key, data.Length_bytes, (data.R >= 3) ? 20 : 1, false);
+ O_key, data.getLengthBytes(),
+ (data.getR() >= 3) ? 20 : 1, false);
return std::string(upass, key_bytes);
}
@@ -201,9 +478,9 @@ compute_U_value_R2(std::string const& user_password,
std::string k1 = QPDF::compute_encryption_key(user_password, data);
char udata[key_bytes];
- pad_or_truncate_password("", udata);
+ pad_or_truncate_password_V4("", udata);
iterate_rc4((unsigned char*) udata, key_bytes,
- (unsigned char*)k1.c_str(), data.Length_bytes, 1, false);
+ (unsigned char*)k1.c_str(), data.getLengthBytes(), 1, false);
return std::string(udata, key_bytes);
}
@@ -217,12 +494,13 @@ compute_U_value_R3(std::string const& user_password,
std::string k1 = QPDF::compute_encryption_key(user_password, data);
MD5 md5;
md5.encodeDataIncrementally(
- pad_or_truncate_password("").c_str(), key_bytes);
- md5.encodeDataIncrementally(data.id1.c_str(), (int)data.id1.length());
+ pad_or_truncate_password_V4("").c_str(), key_bytes);
+ md5.encodeDataIncrementally(data.getId1().c_str(),
+ (int)data.getId1().length());
MD5::Digest digest;
md5.digest(digest);
iterate_rc4(digest, sizeof(MD5::Digest),
- (unsigned char*) k1.c_str(), data.Length_bytes, 20, false);
+ (unsigned char*) k1.c_str(), data.getLengthBytes(), 20, false);
char result[key_bytes];
memcpy(result, digest, sizeof(MD5::Digest));
// pad with arbitrary data -- make it consistent for the sake of
@@ -238,7 +516,7 @@ static std::string
compute_U_value(std::string const& user_password,
QPDF::EncryptionData const& data)
{
- if (data.R >= 3)
+ if (data.getR() >= 3)
{
return compute_U_value_R3(user_password, data);
}
@@ -247,40 +525,214 @@ compute_U_value(std::string const& user_password,
}
static bool
-check_user_password(std::string const& user_password,
- QPDF::EncryptionData const& data)
+check_user_password_V4(std::string const& user_password,
+ QPDF::EncryptionData const& data)
{
// Algorithm 3.6 from the PDF 1.7 Reference Manual
std::string u_value = compute_U_value(user_password, data);
- int to_compare = ((data.R >= 3) ? sizeof(MD5::Digest) : key_bytes);
- return (memcmp(data.U.c_str(), u_value.c_str(), to_compare) == 0);
+ int to_compare = ((data.getR() >= 3) ? sizeof(MD5::Digest)
+ : key_bytes);
+ return (memcmp(data.getU().c_str(), u_value.c_str(), to_compare) == 0);
}
static bool
-check_owner_password(std::string& user_password,
- std::string const& owner_password,
- QPDF::EncryptionData const& data)
+check_user_password_V5(std::string const& user_password,
+ QPDF::EncryptionData const& data)
+{
+ // Algorithm 3.11 from the PDF 1.7 extension level 3
+
+ std::string user_data = data.getU().substr(0, 32);
+ std::string validation_salt = data.getU().substr(32, 8);
+ std::string password = truncate_password_V5(user_password);
+ return (hash_V5(password, validation_salt, "", data) == user_data);
+}
+
+static bool
+check_user_password(std::string const& user_password,
+ QPDF::EncryptionData const& data)
+{
+ if (data.getV() < 5)
+ {
+ return check_user_password_V4(user_password, data);
+ }
+ else
+ {
+ return check_user_password_V5(user_password, data);
+ }
+}
+
+static bool
+check_owner_password_V4(std::string& user_password,
+ std::string const& owner_password,
+ QPDF::EncryptionData const& data)
{
// Algorithm 3.7 from the PDF 1.7 Reference Manual
- unsigned char key[O_key_bytes];
+ unsigned char key[OU_key_bytes_V4];
compute_O_rc4_key(user_password, owner_password, data, key);
unsigned char O_data[key_bytes];
- memcpy(O_data, (unsigned char*) data.O.c_str(), key_bytes);
- iterate_rc4(O_data, key_bytes, key, data.Length_bytes,
- (data.R >= 3) ? 20 : 1, true);
+ memcpy(O_data, (unsigned char*) data.getO().c_str(), key_bytes);
+ iterate_rc4(O_data, key_bytes, key, data.getLengthBytes(),
+ (data.getR() >= 3) ? 20 : 1, true);
std::string new_user_password =
- std::string((char*)O_data, key_bytes);
+ std::string((char*)O_data, key_bytes);
bool result = false;
if (check_user_password(new_user_password, data))
{
- result = true;
- user_password = new_user_password;
+ result = true;
+ user_password = new_user_password;
}
return result;
}
+static bool
+check_owner_password_V5(std::string const& owner_password,
+ QPDF::EncryptionData const& data)
+{
+ // Algorithm 3.12 from the PDF 1.7 extension level 3
+
+ std::string user_data = data.getU().substr(0, 48);
+ std::string owner_data = data.getO().substr(0, 32);
+ std::string validation_salt = data.getO().substr(32, 8);
+ std::string password = truncate_password_V5(owner_password);
+ return (hash_V5(password, validation_salt, user_data,
+ data) == owner_data);
+}
+
+static bool
+check_owner_password(std::string& user_password,
+ std::string const& owner_password,
+ QPDF::EncryptionData const& data)
+{
+ if (data.getV() < 5)
+ {
+ return check_owner_password_V4(user_password, owner_password, data);
+ }
+ else
+ {
+ return check_owner_password_V5(owner_password, data);
+ }
+}
+
+std::string
+QPDF::recover_encryption_key_with_password(
+ std::string const& password, EncryptionData const& data)
+{
+ // Disregard whether Perms is valid.
+ bool disregard;
+ return recover_encryption_key_with_password(password, data, disregard);
+}
+
+static void
+compute_U_UE_value_V5(std::string const& user_password,
+ std::string const& encryption_key,
+ QPDF::EncryptionData const& data,
+ std::string& U, std::string& UE)
+{
+ // Algorithm 3.8 from the PDF 1.7 extension level 3
+ char k[16];
+ QUtil::initializeWithRandomBytes((unsigned char*) k, sizeof(k));
+ std::string validation_salt(k, 8);
+ std::string key_salt(k + 8, 8);
+ U = hash_V5(user_password, validation_salt, "", data) +
+ validation_salt + key_salt;
+ std::string intermediate_key = hash_V5(user_password, key_salt, "", data);
+ UE = process_with_aes(intermediate_key, true, encryption_key);
+}
+
+static void
+compute_O_OE_value_V5(std::string const& owner_password,
+ std::string const& encryption_key,
+ QPDF::EncryptionData const& data,
+ std::string const& U,
+ std::string& O, std::string& OE)
+{
+ // Algorithm 3.9 from the PDF 1.7 extension level 3
+ char k[16];
+ QUtil::initializeWithRandomBytes((unsigned char*) k, sizeof(k));
+ std::string validation_salt(k, 8);
+ std::string key_salt(k + 8, 8);
+ O = hash_V5(owner_password, validation_salt, U, data) +
+ validation_salt + key_salt;
+ std::string intermediate_key = hash_V5(owner_password, key_salt, U, data);
+ OE = process_with_aes(intermediate_key, true, encryption_key);
+}
+
+void
+compute_Perms_value_V5_clear(std::string const& encryption_key,
+ QPDF::EncryptionData const& data,
+ unsigned char k[16])
+{
+ // From algorithm 3.10 from the PDF 1.7 extension level 3
+ unsigned long long extended_perms = 0xffffffff00000000LL | data.getP();
+ for (int i = 0; i < 8; ++i)
+ {
+ k[i] = (unsigned char) (extended_perms & 0xff);
+ extended_perms >>= 8;
+ }
+ k[8] = data.getEncryptMetadata() ? 'T' : 'F';
+ k[9] = 'a';
+ k[10] = 'd';
+ k[11] = 'b';
+ QUtil::initializeWithRandomBytes(k + 12, 4);
+}
+
+static std::string
+compute_Perms_value_V5(std::string const& encryption_key,
+ QPDF::EncryptionData const& data)
+{
+ // Algorithm 3.10 from the PDF 1.7 extension level 3
+ unsigned char k[16];
+ compute_Perms_value_V5_clear(encryption_key, data, k);
+ return process_with_aes(encryption_key, true,
+ std::string((char const*) k, sizeof(k)));
+}
+
+std::string
+QPDF::recover_encryption_key_with_password(
+ std::string const& password, EncryptionData const& data,
+ bool& perms_valid)
+{
+ // Algorithm 3.2a from the PDF 1.7 extension level 3
+
+ // This code does not handle Unicode passwords correctly.
+ // Empirical evidence suggests that most viewers don't. We are
+ // supposed to process the input string with the SASLprep (RFC
+ // 4013) profile of stringprep (RFC 3454) and then convert the
+ // result to UTF-8.
+
+ perms_valid = false;
+ std::string key_password = truncate_password_V5(password);
+ std::string key_salt;
+ std::string user_data;
+ std::string encrypted_file_key;
+ if (check_owner_password_V5(key_password, data))
+ {
+ key_salt = data.getO().substr(40, 8);
+ user_data = data.getU().substr(0, 48);
+ encrypted_file_key = data.getOE().substr(0, 32);
+ }
+ else if (check_user_password_V5(key_password, data))
+ {
+ key_salt = data.getU().substr(40, 8);
+ encrypted_file_key = data.getUE().substr(0, 32);
+ }
+ std::string intermediate_key =
+ hash_V5(key_password, key_salt, user_data, data);
+ std::string file_key =
+ process_with_aes(intermediate_key, false, encrypted_file_key);
+
+ // Decrypt Perms and check against expected value
+ std::string perms_check =
+ process_with_aes(file_key, false, data.getPerms(), 12);
+ unsigned char k[16];
+ compute_Perms_value_V5_clear(file_key, data, k);
+ perms_valid = (memcmp(perms_check.c_str(), k, 12) == 0);
+
+ return file_key;
+}
+
QPDF::encryption_method_e
QPDF::interpretCF(QPDFObjectHandle cf)
{
@@ -384,29 +836,70 @@ QPDF::initializeEncryption()
std::string U = encryption_dict.getKey("/U").getStringValue();
unsigned int P = (unsigned int) encryption_dict.getKey("/P").getIntValue();
- if (! (((R == 2) || (R == 3) || (R == 4)) &&
- ((V == 1) || (V == 2) || (V == 4))))
+ // If supporting new encryption R/V values, remember to update
+ // error message inside this if statement.
+ if (! (((R >= 2) && (R <= 6)) &&
+ ((V == 1) || (V == 2) || (V == 4) || (V == 5))))
{
throw QPDFExc(qpdf_e_unsupported, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
- "Unsupported /R or /V in encryption dictionary");
+ "Unsupported /R or /V in encryption dictionary; R = " +
+ QUtil::int_to_string(R) + " (max 6), V = " +
+ QUtil::int_to_string(V) + " (max 5)");
}
this->encryption_V = V;
+ this->encryption_R = R;
- if (! ((O.length() == key_bytes) && (U.length() == key_bytes)))
+ // OE, UE, and Perms are only present if V >= 5.
+ std::string OE;
+ std::string UE;
+ std::string Perms;
+
+ if (V < 5)
{
- throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
- "encryption dictionary", this->file->getLastOffset(),
- "incorrect length for /O and/or /P in "
- "encryption dictionary");
+ if (! ((O.length() == key_bytes) && (U.length() == key_bytes)))
+ {
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
+ "encryption dictionary", this->file->getLastOffset(),
+ "incorrect length for /O and/or /U in "
+ "encryption dictionary");
+ }
+ }
+ else
+ {
+ if (! (encryption_dict.getKey("/OE").isString() &&
+ encryption_dict.getKey("/UE").isString() &&
+ encryption_dict.getKey("/Perms").isString()))
+ {
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
+ "encryption dictionary", this->file->getLastOffset(),
+ "some V=5 encryption dictionary parameters are "
+ "missing or the wrong type");
+ }
+ OE = encryption_dict.getKey("/OE").getStringValue();
+ UE = encryption_dict.getKey("/UE").getStringValue();
+ Perms = encryption_dict.getKey("/Perms").getStringValue();
+
+ if ((O.length() < OU_key_bytes_V5) ||
+ (U.length() < OU_key_bytes_V5) ||
+ (OE.length() < OUE_key_bytes_V5) ||
+ (UE.length() < OUE_key_bytes_V5) ||
+ (Perms.length() < Perms_key_bytes_V5))
+ {
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
+ "encryption dictionary", this->file->getLastOffset(),
+ "incorrect length for some of"
+ " /O, /U, /OE, /UE, or /Perms in"
+ " encryption dictionary");
+ }
}
int Length = 40;
if (encryption_dict.getKey("/Length").isInteger())
{
Length = encryption_dict.getKey("/Length").getIntValue();
- if ((Length % 8) || (Length < 40) || (Length > 128))
+ if ((Length % 8) || (Length < 40) || (Length > 256))
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
@@ -421,7 +914,7 @@ QPDF::initializeEncryption()
encryption_dict.getKey("/EncryptMetadata").getBoolValue();
}
- if (V == 4)
+ if ((V == 4) || (V == 5))
{
QPDFObjectHandle CF = encryption_dict.getKey("/CF");
std::set<std::string> keys = CF.getKeys();
@@ -446,6 +939,11 @@ QPDF::initializeEncryption()
QTC::TC("qpdf", "QPDF_encryption CFM AESV2");
method = e_aes;
}
+ else if (method_name == "/AESV3")
+ {
+ QTC::TC("qpdf", "QPDF_encryption CFM AESV3");
+ method = e_aesv3;
+ }
else
{
// Don't complain now -- maybe we won't need
@@ -470,35 +968,15 @@ QPDF::initializeEncryption()
{
this->cf_file = this->cf_stream;
}
- if (this->cf_file != this->cf_stream)
- {
- // The issue for qpdf is that it can't tell the difference
- // between an embedded file stream and a regular stream.
- // Search for a comment containing cf_file. To fix this,
- // we need files with encrypted embedded files and
- // non-encrypted native streams and vice versa. Also if
- // it is possible for them to be encrypted in different
- // ways, we should have some of those too. In cases where
- // we can detect whether a stream is encrypted or not, we
- // might want to try to detecet that automatically in
- // defense of possible logic errors surrounding detection
- // of embedded file streams, unless that's really clear
- // from the specification.
- throw QPDFExc(qpdf_e_unsupported, this->file->getName(),
- "encryption dictionary", this->file->getLastOffset(),
- "This document has embedded files that are"
- " encrypted differently from the rest of the file."
- " qpdf does not presently support this due to"
- " lack of test data; if possible, please submit"
- " a bug report that includes this file.");
- }
}
- EncryptionData data(V, R, Length / 8, P, O, U, id1, this->encrypt_metadata);
+
+ EncryptionData data(V, R, Length / 8, P, O, U, OE, UE, Perms,
+ id1, this->encrypt_metadata);
if (check_owner_password(
this->user_password, this->provided_password, data))
{
// password supplied was owner password; user_password has
- // been initialized
+ // been initialized for V < 5
}
else if (check_user_password(this->provided_password, data))
{
@@ -510,7 +988,30 @@ QPDF::initializeEncryption()
"", 0, "invalid password");
}
- this->encryption_key = compute_encryption_key(this->user_password, data);
+ if (V < 5)
+ {
+ // For V < 5, the user password is encrypted with the owner
+ // password, and the user password is always used for
+ // computing the encryption key.
+ this->encryption_key = compute_encryption_key(
+ this->user_password, data);
+ }
+ else
+ {
+ // For V >= 5, either password can be used independently to
+ // compute the encryption key, and neither password can be
+ // used to recover the other.
+ bool perms_valid;
+ this->encryption_key = recover_encryption_key_with_password(
+ this->provided_password, data, perms_valid);
+ if (! perms_valid)
+ {
+ warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
+ "encryption dictionary", this->file->getLastOffset(),
+ "/Perms field in encryption dictionary"
+ " doesn't match expected value"));
+ }
+ }
}
std::string
@@ -526,7 +1027,8 @@ QPDF::getKeyForObject(int objid, int generation, bool use_aes)
(generation == this->cached_key_generation)))
{
this->cached_object_encryption_key =
- compute_data_key(this->encryption_key, objid, generation, use_aes);
+ compute_data_key(this->encryption_key, objid, generation,
+ use_aes, this->encryption_V, this->encryption_R);
this->cached_key_objid = objid;
this->cached_key_generation = generation;
}
@@ -542,7 +1044,7 @@ QPDF::decryptString(std::string& str, int objid, int generation)
return;
}
bool use_aes = false;
- if (this->encryption_V == 4)
+ if (this->encryption_V >= 4)
{
switch (this->cf_string)
{
@@ -553,6 +1055,10 @@ QPDF::decryptString(std::string& str, int objid, int generation)
use_aes = true;
break;
+ case e_aesv3:
+ use_aes = true;
+ break;
+
case e_rc4:
break;
@@ -576,10 +1082,10 @@ QPDF::decryptString(std::string& str, int objid, int generation)
if (use_aes)
{
QTC::TC("qpdf", "QPDF_encryption aes decode string");
- assert(key.length() == Pl_AES_PDF::key_size);
Pl_Buffer bufpl("decrypted string");
Pl_AES_PDF pl("aes decrypt string", &bufpl, false,
- (unsigned char const*)key.c_str());
+ (unsigned char const*)key.c_str(),
+ (unsigned int)key.length());
pl.write((unsigned char*)str.c_str(), str.length());
pl.finish();
PointerHolder<Buffer> buf = bufpl.getBuffer();
@@ -628,23 +1134,53 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
return;
}
bool use_aes = false;
- if (this->encryption_V == 4)
+ if (this->encryption_V >= 4)
{
encryption_method_e method = e_unknown;
std::string method_source = "/StmF from /Encrypt dictionary";
- if (stream_dict.getKey("/Filter").isOrHasName("/Crypt") &&
- stream_dict.getKey("/DecodeParms").isDictionary())
- {
- QPDFObjectHandle decode_parms = stream_dict.getKey("/DecodeParms");
- if (decode_parms.getKey("/Type").isName() &&
- (decode_parms.getKey("/Type").getName() ==
- "/CryptFilterDecodeParms"))
- {
- QTC::TC("qpdf", "QPDF_encryption stream crypt filter");
- method = interpretCF(decode_parms.getKey("/Name"));
- method_source = "stream's Crypt decode parameters";
- }
+ if (stream_dict.getKey("/Filter").isOrHasName("/Crypt"))
+ {
+ if (stream_dict.getKey("/DecodeParms").isDictionary())
+ {
+ QPDFObjectHandle decode_parms =
+ stream_dict.getKey("/DecodeParms");
+ if (decode_parms.getKey("/Type").isName() &&
+ (decode_parms.getKey("/Type").getName() ==
+ "/CryptFilterDecodeParms"))
+ {
+ QTC::TC("qpdf", "QPDF_encryption stream crypt filter");
+ method = interpretCF(decode_parms.getKey("/Name"));
+ method_source = "stream's Crypt decode parameters";
+ }
+ }
+ else if (stream_dict.getKey("/DecodeParms").isArray() &&
+ stream_dict.getKey("/Filter").isArray())
+ {
+ QPDFObjectHandle filter = stream_dict.getKey("/Filter");
+ QPDFObjectHandle decode = stream_dict.getKey("/DecodeParms");
+ if (filter.getArrayNItems() == decode.getArrayNItems())
+ {
+ for (int i = 0; i < filter.getArrayNItems(); ++i)
+ {
+ if (filter.getArrayItem(i).isName() &&
+ (filter.getArrayItem(i).getName() == "/Crypt"))
+ {
+ QPDFObjectHandle crypt_params =
+ decode.getArrayItem(i);
+ if (crypt_params.isDictionary() &&
+ crypt_params.getKey("/Name").isName())
+ {
+ QTC::TC("qpdf", "QPDF_encrypt crypt array");
+ method = interpretCF(
+ crypt_params.getKey("/Name"));
+ method_source = "stream's Crypt "
+ "decode parameters (array)";
+ }
+ }
+ }
+ }
+ }
}
if (method == e_unknown)
@@ -656,12 +1192,15 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
}
else
{
- // NOTE: We should should use cf_file if this is an
- // embedded file, but we can't yet detect embedded
- // file streams as such. When fixing, search for all
- // occurrences of cf_file to find a reference to this
- // comment.
- method = this->cf_stream;
+ if (this->attachment_streams.count(
+ ObjGen(objid, generation)) > 0)
+ {
+ method = this->cf_file;
+ }
+ else
+ {
+ method = this->cf_stream;
+ }
}
}
use_aes = false;
@@ -675,6 +1214,10 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
use_aes = true;
break;
+ case e_aesv3:
+ use_aes = true;
+ break;
+
case e_rc4:
break;
@@ -696,15 +1239,16 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
if (use_aes)
{
QTC::TC("qpdf", "QPDF_encryption aes decode stream");
- assert(key.length() == Pl_AES_PDF::key_size);
pipeline = new Pl_AES_PDF("AES stream decryption", pipeline,
- false, (unsigned char*) key.c_str());
+ false, (unsigned char*) key.c_str(),
+ (unsigned int) key.length());
}
else
{
QTC::TC("qpdf", "QPDF_encryption rc4 decode stream");
pipeline = new Pl_RC4("RC4 stream decryption", pipeline,
- (unsigned char*) key.c_str(), (int)key.length());
+ (unsigned char*) key.c_str(),
+ (unsigned int) key.length());
}
heap.push_back(pipeline);
}
@@ -715,10 +1259,37 @@ QPDF::compute_encryption_O_U(
int V, int R, int key_len, int P, bool encrypt_metadata,
std::string const& id1, std::string& O, std::string& U)
{
- EncryptionData data(V, R, key_len, P, "", "", id1, encrypt_metadata);
- data.O = compute_O_value(user_password, owner_password, data);
- O = data.O;
- U = compute_U_value(user_password, data);
+ if (V >= 5)
+ {
+ throw std::logic_error(
+ "compute_encryption_O_U called for file with V >= 5");
+ }
+ EncryptionData data(V, R, key_len, P, "", "", "", "", "",
+ id1, encrypt_metadata);
+ data.setO(compute_O_value(user_password, owner_password, data));
+ O = data.getO();
+ data.setU(compute_U_value(user_password, data));
+ U = data.getU();
+}
+
+void
+QPDF::compute_encryption_parameters_V5(
+ char const* user_password, char const* owner_password,
+ int V, int R, int key_len, int P, bool encrypt_metadata,
+ std::string const& id1,
+ std::string& encryption_key,
+ std::string& O, std::string& U,
+ std::string& OE, std::string& UE, std::string& Perms)
+{
+ EncryptionData data(V, R, key_len, P, "", "", "", "", "",
+ id1, encrypt_metadata);
+ unsigned char k[key_bytes];
+ QUtil::initializeWithRandomBytes(k, key_bytes);
+ encryption_key = std::string((char const*)k, key_bytes);
+ compute_U_UE_value_V5(user_password, encryption_key, data, U, UE);
+ compute_O_OE_value_V5(owner_password, encryption_key, data, U, O, OE);
+ Perms = compute_Perms_value_V5(encryption_key, data);
+ data.setV5EncryptionParameters(O, OE, U, UE, Perms);
}
std::string const&
@@ -735,6 +1306,12 @@ QPDF::getTrimmedUserPassword() const
return result;
}
+std::string
+QPDF::getEncryptionKey() const
+{
+ return this->encryption_key;
+}
+
bool
QPDF::isEncrypted() const
{
diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc
index 67f147f..f832a88 100644
--- a/libqpdf/QPDF_optimization.cc
+++ b/libqpdf/QPDF_optimization.cc
@@ -59,103 +59,6 @@ QPDF::ObjUser::operator<(ObjUser const& rhs) const
}
void
-QPDF::flattenScalarReferences()
-{
- // Do a traversal of the entire PDF file structure replacing all
- // indirect objects that are not arrays, streams, or dictionaries
- // with direct objects.
-
- std::list<QPDFObjectHandle> queue;
- queue.push_back(this->trailer);
- std::set<ObjGen> visited;
-
- // Add every object in the xref table to the queue. This ensures
- // that we flatten scalar references in unreferenced objects.
- // This becomes important if we are preserving object streams in a
- // file that has unreferenced objects in its object streams. (See
- // QPDF bug 2974522 at SourceForge.)
- for (std::map<ObjGen, QPDFXRefEntry>::iterator iter =
- this->xref_table.begin();
- iter != this->xref_table.end(); ++iter)
- {
- ObjGen const& og = (*iter).first;
- queue.push_back(getObjectByID(og.obj, og.gen));
- }
-
- while (! queue.empty())
- {
- QPDFObjectHandle node = queue.front();
- queue.pop_front();
- if (node.isIndirect())
- {
- ObjGen og(node.getObjectID(), node.getGeneration());
- if (visited.count(og) > 0)
- {
- continue;
- }
- visited.insert(og);
- }
-
- if (node.isArray())
- {
- int nitems = node.getArrayNItems();
- for (int i = 0; i < nitems; ++i)
- {
- QPDFObjectHandle oh = node.getArrayItem(i);
- if (oh.isScalar())
- {
- if (oh.isIndirect())
- {
- QTC::TC("qpdf", "QPDF opt flatten array scalar");
- oh.makeDirect();
- node.setArrayItem(i, oh);
- }
- }
- else
- {
- queue.push_back(oh);
- }
- }
- }
- else if (node.isDictionary() || node.isStream())
- {
- QPDFObjectHandle dict = node;
- if (node.isStream())
- {
- dict = node.getDict();
- }
- std::set<std::string> keys = dict.getKeys();
- for (std::set<std::string>::iterator iter = keys.begin();
- iter != keys.end(); ++iter)
- {
- std::string const& key = *iter;
- QPDFObjectHandle oh = dict.getKey(key);
- if (oh.isNull())
- {
- // QPDF_Dictionary.getKeys() never returns null
- // keys.
- throw std::logic_error(
- "INTERNAL ERROR: dictionary with null key found");
- }
- else if (oh.isScalar())
- {
- if (oh.isIndirect())
- {
- QTC::TC("qpdf", "QPDF opt flatten dict scalar");
- oh.makeDirect();
- dict.replaceKey(key, oh);
- }
- }
- else
- {
- queue.push_back(oh);
- }
- }
- }
- }
-}
-
-void
QPDF::optimize(std::map<int, int> const& object_stream_data,
bool allow_changes)
{
@@ -304,9 +207,7 @@ QPDF::pushInheritedAttributesToPageInternal(
}
else
{
- // Don't defeat flattenScalarReferences which
- // would have already been called by this
- // time.
+ // It's okay to copy scalars.
QTC::TC("qpdf", "QPDF opt inherited scalar");
}
}
diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc
index 2014308..3cdfdc4 100644
--- a/libqpdf/QUtil.cc
+++ b/libqpdf/QUtil.cc
@@ -333,3 +333,42 @@ QUtil::toUTF8(unsigned long uval)
return result;
}
+
+long
+QUtil::random()
+{
+ static bool seeded_random = false;
+ if (! seeded_random)
+ {
+ // Seed the random number generator with something simple, but
+ // just to be interesting, don't use the unmodified current
+ // time....
+ QUtil::srandom((int)QUtil::get_current_time() ^ 0xcccc);
+ seeded_random = true;
+ }
+
+#ifdef HAVE_RANDOM
+ return ::random();
+#else
+ return rand();
+#endif
+}
+
+void
+QUtil::srandom(unsigned int seed)
+{
+#ifdef HAVE_RANDOM
+ ::srandom(seed);
+#else
+ srand(seed);
+#endif
+}
+
+void
+QUtil::initializeWithRandomBytes(unsigned char* data, size_t len)
+{
+ for (size_t i = 0; i < len; ++i)
+ {
+ data[i] = (unsigned char)((QUtil::random() & 0xff0) >> 4);
+ }
+}
diff --git a/libqpdf/build.mk b/libqpdf/build.mk
index 6debf10..d64fde5 100644
--- a/libqpdf/build.mk
+++ b/libqpdf/build.mk
@@ -12,6 +12,7 @@ SRCS_libqpdf = \
libqpdf/FileInputSource.cc \
libqpdf/InputSource.cc \
libqpdf/MD5.cc \
+ libqpdf/OffsetInputSource.cc \
libqpdf/PCRE.cc \
libqpdf/Pipeline.cc \
libqpdf/Pl_AES_PDF.cc \
@@ -27,6 +28,7 @@ SRCS_libqpdf = \
libqpdf/Pl_PNGFilter.cc \
libqpdf/Pl_QPDFTokenizer.cc \
libqpdf/Pl_RC4.cc \
+ libqpdf/Pl_SHA2.cc \
libqpdf/Pl_StdioFile.cc \
libqpdf/QPDF.cc \
libqpdf/QPDFExc.cc \
@@ -53,18 +55,27 @@ SRCS_libqpdf = \
libqpdf/QUtil.cc \
libqpdf/RC4.cc \
libqpdf/qpdf-c.cc \
- libqpdf/rijndael.cc
+ libqpdf/rijndael.cc \
+ libqpdf/sha2.c \
+ libqpdf/sha2big.c
# -----
-OBJS_libqpdf = $(call src_to_lobj,$(SRCS_libqpdf))
+CCSRCS_libqpdf = $(filter %.cc,$(SRCS_libqpdf))
+CSRCS_libqpdf = $(filter %.c,$(SRCS_libqpdf))
+
+CCOBJS_libqpdf = $(call src_to_lobj,$(CCSRCS_libqpdf))
+COBJS_libqpdf = $(call c_src_to_lobj,$(CSRCS_libqpdf))
+OBJS_libqpdf = $(CCOBJS_libqpdf) $(COBJS_libqpdf)
ifeq ($(GENDEPS),1)
-include $(call lobj_to_dep,$(OBJS_libqpdf))
endif
-$(OBJS_libqpdf): libqpdf/$(OUTPUT_DIR)/%.$(LOBJ): libqpdf/%.cc
+$(CCOBJS_libqpdf): libqpdf/$(OUTPUT_DIR)/%.$(LOBJ): libqpdf/%.cc
$(call libcompile,$<,$(INCLUDES_libqpdf))
+$(COBJS_libqpdf): libqpdf/$(OUTPUT_DIR)/%.$(LOBJ): libqpdf/%.c
+ $(call c_libcompile,$<,$(INCLUDES_libqpdf))
# Last three arguments to makelib are CURRENT,REVISION,AGE.
#
@@ -80,4 +91,4 @@ $(OBJS_libqpdf): libqpdf/$(OUTPUT_DIR)/%.$(LOBJ): libqpdf/%.cc
# * Otherwise, increment REVISION
$(TARGETS_libqpdf): $(OBJS_libqpdf)
- $(call makelib,$(OBJS_libqpdf),$@,$(LDFLAGS),$(LIBS),9,0,1)
+ $(call makelib,$(OBJS_libqpdf),$@,$(LDFLAGS),$(LIBS),10,0,0)
diff --git a/libqpdf/qpdf-c.cc b/libqpdf/qpdf-c.cc
index 0312ae5..a46df63 100644
--- a/libqpdf/qpdf-c.cc
+++ b/libqpdf/qpdf-c.cc
@@ -288,6 +288,12 @@ char const* qpdf_get_pdf_version(qpdf_data qpdf)
return qpdf->tmp_string.c_str();
}
+int qpdf_get_pdf_extension_level(qpdf_data qpdf)
+{
+ QTC::TC("qpdf", "qpdf-c called qpdf_get_pdf_extension_level");
+ return qpdf->qpdf->getExtensionLevel();
+}
+
char const* qpdf_get_user_password(qpdf_data qpdf)
{
QTC::TC("qpdf", "qpdf-c called qpdf_get_user_password");
@@ -566,6 +572,32 @@ void qpdf_set_r4_encryption_parameters(
encrypt_metadata, use_aes);
}
+void qpdf_set_r5_encryption_parameters(
+ qpdf_data qpdf, char const* user_password, char const* owner_password,
+ QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract,
+ qpdf_r3_print_e print, qpdf_r3_modify_e modify,
+ QPDF_BOOL encrypt_metadata)
+{
+ QTC::TC("qpdf", "qpdf-c called qpdf_set_r5_encryption_parameters");
+ qpdf->qpdf_writer->setR5EncryptionParameters(
+ user_password, owner_password,
+ allow_accessibility, allow_extract, print, modify,
+ encrypt_metadata);
+}
+
+void qpdf_set_r6_encryption_parameters(
+ qpdf_data qpdf, char const* user_password, char const* owner_password,
+ QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract,
+ qpdf_r3_print_e print, qpdf_r3_modify_e modify,
+ QPDF_BOOL encrypt_metadata)
+{
+ QTC::TC("qpdf", "qpdf-c called qpdf_set_r6_encryption_parameters");
+ qpdf->qpdf_writer->setR6EncryptionParameters(
+ user_password, owner_password,
+ allow_accessibility, allow_extract, print, modify,
+ encrypt_metadata);
+}
+
void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value)
{
QTC::TC("qpdf", "qpdf-c called qpdf_set_linearization");
@@ -574,14 +606,26 @@ void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value)
void qpdf_set_minimum_pdf_version(qpdf_data qpdf, char const* version)
{
+ qpdf_set_minimum_pdf_version_and_extension(qpdf, version, 0);
+}
+
+void qpdf_set_minimum_pdf_version_and_extension(
+ qpdf_data qpdf, char const* version, int extension_level)
+{
QTC::TC("qpdf", "qpdf-c called qpdf_set_minimum_pdf_version");
- qpdf->qpdf_writer->setMinimumPDFVersion(version);
+ qpdf->qpdf_writer->setMinimumPDFVersion(version, extension_level);
}
void qpdf_force_pdf_version(qpdf_data qpdf, char const* version)
{
+ qpdf_force_pdf_version_and_extension(qpdf, version, 0);
+}
+
+void qpdf_force_pdf_version_and_extension(
+ qpdf_data qpdf, char const* version, int extension_level)
+{
QTC::TC("qpdf", "qpdf-c called qpdf_force_pdf_version");
- qpdf->qpdf_writer->forcePDFVersion(version);
+ qpdf->qpdf_writer->forcePDFVersion(version, extension_level);
}
QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf)
diff --git a/libqpdf/qpdf/OffsetInputSource.hh b/libqpdf/qpdf/OffsetInputSource.hh
new file mode 100644
index 0000000..aedc574
--- /dev/null
+++ b/libqpdf/qpdf/OffsetInputSource.hh
@@ -0,0 +1,29 @@
+#ifndef __QPDF_OFFSETINPUTSOURCE_HH__
+#define __QPDF_OFFSETINPUTSOURCE_HH__
+
+// This class implements an InputSource that proxies for an underlying
+// input source but offset a specific number of bytes.
+
+#include <qpdf/InputSource.hh>
+#include <qpdf/PointerHolder.hh>
+
+class OffsetInputSource: public InputSource
+{
+ public:
+ OffsetInputSource(PointerHolder<InputSource>, qpdf_offset_t global_offset);
+ virtual ~OffsetInputSource();
+
+ virtual qpdf_offset_t findAndSkipNextEOL();
+ virtual std::string const& getName() const;
+ virtual qpdf_offset_t tell();
+ virtual void seek(qpdf_offset_t offset, int whence);
+ virtual void rewind();
+ virtual size_t read(char* buffer, size_t length);
+ virtual void unreadCh(char ch);
+
+ private:
+ PointerHolder<InputSource> proxied;
+ qpdf_offset_t global_offset;
+};
+
+#endif // __QPDF_OFFSETINPUTSOURCE_HH__
diff --git a/libqpdf/qpdf/Pl_AES_PDF.hh b/libqpdf/qpdf/Pl_AES_PDF.hh
index 3947506..9aa73ad 100644
--- a/libqpdf/qpdf/Pl_AES_PDF.hh
+++ b/libqpdf/qpdf/Pl_AES_PDF.hh
@@ -7,17 +7,16 @@
# include <stdint.h>
#endif
-// This pipeline implements AES-128 with CBC and block padding as
-// specified in the PDF specification.
+// This pipeline implements AES-128 and AES-256 with CBC and block
+// padding as specified in the PDF specification.
class Pl_AES_PDF: public Pipeline
{
public:
- // key_data should be a pointer to key_size bytes of data
- static unsigned int const key_size = 16;
QPDF_DLL
+ // key should be a pointer to key_bytes bytes of data
Pl_AES_PDF(char const* identifier, Pipeline* next,
- bool encrypt, unsigned char const key[key_size]);
+ bool encrypt, unsigned char const* key, unsigned int key_bytes);
QPDF_DLL
virtual ~Pl_AES_PDF();
@@ -26,6 +25,17 @@ class Pl_AES_PDF: public Pipeline
QPDF_DLL
virtual void finish();
+ // Use zero initialization vector; needed for AESV3
+ QPDF_DLL
+ void useZeroIV();
+ // Disable padding; needed for AESV3
+ QPDF_DLL
+ void disablePadding();
+ // Specify an initialization vector, which will not be included in
+ // the output.
+ QPDF_DLL
+ void setIV(unsigned char const* iv, size_t bytes);
+
// For testing only; PDF always uses CBC
QPDF_DLL
void disableCBC();
@@ -44,12 +54,16 @@ class Pl_AES_PDF: public Pipeline
bool cbc_mode;
bool first;
size_t offset; // offset into memory buffer
- unsigned char key[key_size];
- uint32_t rk[key_size + 28];
+ unsigned char* key;
+ uint32_t* rk;
unsigned char inbuf[buf_size];
unsigned char outbuf[buf_size];
unsigned char cbc_block[buf_size];
+ unsigned char specified_iv[buf_size];
unsigned int nrounds;
+ bool use_zero_iv;
+ bool use_specified_iv;
+ bool disable_padding;
};
#endif // __PL_AES_PDF_HH__
diff --git a/libqpdf/qpdf/Pl_SHA2.hh b/libqpdf/qpdf/Pl_SHA2.hh
new file mode 100644
index 0000000..8ff4723
--- /dev/null
+++ b/libqpdf/qpdf/Pl_SHA2.hh
@@ -0,0 +1,50 @@
+#ifndef __PL_SHA2_HH__
+#define __PL_SHA2_HH__
+
+// Bits must be a supported number of bits, currently only 256, 384,
+// or 512. Passing 0 as bits leaves the pipeline uncommitted, in
+// which case resetBits must be called before the pipeline is used.
+// If a next is provided, this pipeline sends its output to its
+// successor unmodified. After calling finish, the SHA2 checksum of
+// the data that passed through the pipeline is available.
+
+// This pipeline is reusable; i.e., it is safe to call write() after
+// calling finish(). The first call to write() after a call to
+// finish() initializes a new SHA2 object. resetBits may also be
+// called between finish and the next call to write.
+
+#include <qpdf/Pipeline.hh>
+#include <sph/sph_sha2.h>
+
+class Pl_SHA2: public Pipeline
+{
+ public:
+ QPDF_DLL
+ Pl_SHA2(int bits = 0, Pipeline* next = 0);
+ QPDF_DLL
+ virtual ~Pl_SHA2();
+ QPDF_DLL
+ virtual void write(unsigned char*, size_t);
+ QPDF_DLL
+ virtual void finish();
+ QPDF_DLL
+ void resetBits(int bits);
+ QPDF_DLL
+ std::string getHexDigest();
+ QPDF_DLL
+ std::string getRawDigest();
+
+ private:
+ void badBits();
+
+ bool in_progress;
+ int bits;
+ sph_sha256_context ctx256;
+ sph_sha384_context ctx384;
+ sph_sha512_context ctx512;
+ unsigned char sha256sum[32];
+ unsigned char sha384sum[48];
+ unsigned char sha512sum[64];
+};
+
+#endif // __PL_SHA2_HH__
diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh
index 34eacee..6e5dacf 100644
--- a/libqpdf/qpdf/QPDF_Stream.hh
+++ b/libqpdf/qpdf/QPDF_Stream.hh
@@ -45,6 +45,9 @@ class QPDF_Stream: public QPDFObject
void replaceFilterData(QPDFObjectHandle const& filter,
QPDFObjectHandle const& decode_parms,
size_t length);
+ bool understandDecodeParams(
+ std::string const& filter, QPDFObjectHandle decode_params,
+ int& predictor, int& columns, bool& early_code_change);
bool filterable(std::vector<std::string>& filters,
int& predictor, int& columns, bool& early_code_change);
diff --git a/libqpdf/sha2.c b/libqpdf/sha2.c
new file mode 100644
index 0000000..45fdd7e
--- /dev/null
+++ b/libqpdf/sha2.c
@@ -0,0 +1,690 @@
+/* $Id: sha2.c 227 2010-06-16 17:28:38Z tp $ */
+/*
+ * SHA-224 / SHA-256 implementation.
+ *
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010 Projet RNRT SAPHIR
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ *
+ * @author Thomas Pornin <thomas.pornin@cryptolog.com>
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "sph/sph_sha2.h"
+
+#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_SHA2
+#define SPH_SMALL_FOOTPRINT_SHA2 1
+#endif
+
+#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z))
+#define MAJ(X, Y, Z) (((Y) & (Z)) | (((Y) | (Z)) & (X)))
+
+#define ROTR SPH_ROTR32
+
+#define BSG2_0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define BSG2_1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define SSG2_0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SPH_T32((x) >> 3))
+#define SSG2_1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SPH_T32((x) >> 10))
+
+static const sph_u32 H224[8] = {
+ SPH_C32(0xC1059ED8), SPH_C32(0x367CD507), SPH_C32(0x3070DD17),
+ SPH_C32(0xF70E5939), SPH_C32(0xFFC00B31), SPH_C32(0x68581511),
+ SPH_C32(0x64F98FA7), SPH_C32(0xBEFA4FA4)
+};
+
+static const sph_u32 H256[8] = {
+ SPH_C32(0x6A09E667), SPH_C32(0xBB67AE85), SPH_C32(0x3C6EF372),
+ SPH_C32(0xA54FF53A), SPH_C32(0x510E527F), SPH_C32(0x9B05688C),
+ SPH_C32(0x1F83D9AB), SPH_C32(0x5BE0CD19)
+};
+
+/*
+ * The SHA2_ROUND_BODY defines the body for a SHA-224 / SHA-256
+ * compression function implementation. The "in" parameter should
+ * evaluate, when applied to a numerical input parameter from 0 to 15,
+ * to an expression which yields the corresponding input block. The "r"
+ * parameter should evaluate to an array or pointer expression
+ * designating the array of 8 words which contains the input and output
+ * of the compression function.
+ */
+
+#if SPH_SMALL_FOOTPRINT_SHA2
+
+static const sph_u32 K[64] = {
+ SPH_C32(0x428A2F98), SPH_C32(0x71374491),
+ SPH_C32(0xB5C0FBCF), SPH_C32(0xE9B5DBA5),
+ SPH_C32(0x3956C25B), SPH_C32(0x59F111F1),
+ SPH_C32(0x923F82A4), SPH_C32(0xAB1C5ED5),
+ SPH_C32(0xD807AA98), SPH_C32(0x12835B01),
+ SPH_C32(0x243185BE), SPH_C32(0x550C7DC3),
+ SPH_C32(0x72BE5D74), SPH_C32(0x80DEB1FE),
+ SPH_C32(0x9BDC06A7), SPH_C32(0xC19BF174),
+ SPH_C32(0xE49B69C1), SPH_C32(0xEFBE4786),
+ SPH_C32(0x0FC19DC6), SPH_C32(0x240CA1CC),
+ SPH_C32(0x2DE92C6F), SPH_C32(0x4A7484AA),
+ SPH_C32(0x5CB0A9DC), SPH_C32(0x76F988DA),
+ SPH_C32(0x983E5152), SPH_C32(0xA831C66D),
+ SPH_C32(0xB00327C8), SPH_C32(0xBF597FC7),
+ SPH_C32(0xC6E00BF3), SPH_C32(0xD5A79147),
+ SPH_C32(0x06CA6351), SPH_C32(0x14292967),
+ SPH_C32(0x27B70A85), SPH_C32(0x2E1B2138),
+ SPH_C32(0x4D2C6DFC), SPH_C32(0x53380D13),
+ SPH_C32(0x650A7354), SPH_C32(0x766A0ABB),
+ SPH_C32(0x81C2C92E), SPH_C32(0x92722C85),
+ SPH_C32(0xA2BFE8A1), SPH_C32(0xA81A664B),
+ SPH_C32(0xC24B8B70), SPH_C32(0xC76C51A3),
+ SPH_C32(0xD192E819), SPH_C32(0xD6990624),
+ SPH_C32(0xF40E3585), SPH_C32(0x106AA070),
+ SPH_C32(0x19A4C116), SPH_C32(0x1E376C08),
+ SPH_C32(0x2748774C), SPH_C32(0x34B0BCB5),
+ SPH_C32(0x391C0CB3), SPH_C32(0x4ED8AA4A),
+ SPH_C32(0x5B9CCA4F), SPH_C32(0x682E6FF3),
+ SPH_C32(0x748F82EE), SPH_C32(0x78A5636F),
+ SPH_C32(0x84C87814), SPH_C32(0x8CC70208),
+ SPH_C32(0x90BEFFFA), SPH_C32(0xA4506CEB),
+ SPH_C32(0xBEF9A3F7), SPH_C32(0xC67178F2)
+};
+
+#define SHA2_MEXP1(in, pc) do { \
+ W[pc] = in(pc); \
+ } while (0)
+
+#define SHA2_MEXP2(in, pc) do { \
+ W[(pc) & 0x0F] = SPH_T32(SSG2_1(W[((pc) - 2) & 0x0F]) \
+ + W[((pc) - 7) & 0x0F] \
+ + SSG2_0(W[((pc) - 15) & 0x0F]) + W[(pc) & 0x0F]); \
+ } while (0)
+
+#define SHA2_STEPn(n, a, b, c, d, e, f, g, h, in, pc) do { \
+ sph_u32 t1, t2; \
+ SHA2_MEXP ## n(in, pc); \
+ t1 = SPH_T32(h + BSG2_1(e) + CH(e, f, g) \
+ + K[pcount + (pc)] + W[(pc) & 0x0F]); \
+ t2 = SPH_T32(BSG2_0(a) + MAJ(a, b, c)); \
+ d = SPH_T32(d + t1); \
+ h = SPH_T32(t1 + t2); \
+ } while (0)
+
+#define SHA2_STEP1(a, b, c, d, e, f, g, h, in, pc) \
+ SHA2_STEPn(1, a, b, c, d, e, f, g, h, in, pc)
+#define SHA2_STEP2(a, b, c, d, e, f, g, h, in, pc) \
+ SHA2_STEPn(2, a, b, c, d, e, f, g, h, in, pc)
+
+#define SHA2_ROUND_BODY(in, r) do { \
+ sph_u32 A, B, C, D, E, F, G, H; \
+ sph_u32 W[16]; \
+ unsigned pcount; \
+ \
+ A = (r)[0]; \
+ B = (r)[1]; \
+ C = (r)[2]; \
+ D = (r)[3]; \
+ E = (r)[4]; \
+ F = (r)[5]; \
+ G = (r)[6]; \
+ H = (r)[7]; \
+ pcount = 0; \
+ SHA2_STEP1(A, B, C, D, E, F, G, H, in, 0); \
+ SHA2_STEP1(H, A, B, C, D, E, F, G, in, 1); \
+ SHA2_STEP1(G, H, A, B, C, D, E, F, in, 2); \
+ SHA2_STEP1(F, G, H, A, B, C, D, E, in, 3); \
+ SHA2_STEP1(E, F, G, H, A, B, C, D, in, 4); \
+ SHA2_STEP1(D, E, F, G, H, A, B, C, in, 5); \
+ SHA2_STEP1(C, D, E, F, G, H, A, B, in, 6); \
+ SHA2_STEP1(B, C, D, E, F, G, H, A, in, 7); \
+ SHA2_STEP1(A, B, C, D, E, F, G, H, in, 8); \
+ SHA2_STEP1(H, A, B, C, D, E, F, G, in, 9); \
+ SHA2_STEP1(G, H, A, B, C, D, E, F, in, 10); \
+ SHA2_STEP1(F, G, H, A, B, C, D, E, in, 11); \
+ SHA2_STEP1(E, F, G, H, A, B, C, D, in, 12); \
+ SHA2_STEP1(D, E, F, G, H, A, B, C, in, 13); \
+ SHA2_STEP1(C, D, E, F, G, H, A, B, in, 14); \
+ SHA2_STEP1(B, C, D, E, F, G, H, A, in, 15); \
+ for (pcount = 16; pcount < 64; pcount += 16) { \
+ SHA2_STEP2(A, B, C, D, E, F, G, H, in, 0); \
+ SHA2_STEP2(H, A, B, C, D, E, F, G, in, 1); \
+ SHA2_STEP2(G, H, A, B, C, D, E, F, in, 2); \
+ SHA2_STEP2(F, G, H, A, B, C, D, E, in, 3); \
+ SHA2_STEP2(E, F, G, H, A, B, C, D, in, 4); \
+ SHA2_STEP2(D, E, F, G, H, A, B, C, in, 5); \
+ SHA2_STEP2(C, D, E, F, G, H, A, B, in, 6); \
+ SHA2_STEP2(B, C, D, E, F, G, H, A, in, 7); \
+ SHA2_STEP2(A, B, C, D, E, F, G, H, in, 8); \
+ SHA2_STEP2(H, A, B, C, D, E, F, G, in, 9); \
+ SHA2_STEP2(G, H, A, B, C, D, E, F, in, 10); \
+ SHA2_STEP2(F, G, H, A, B, C, D, E, in, 11); \
+ SHA2_STEP2(E, F, G, H, A, B, C, D, in, 12); \
+ SHA2_STEP2(D, E, F, G, H, A, B, C, in, 13); \
+ SHA2_STEP2(C, D, E, F, G, H, A, B, in, 14); \
+ SHA2_STEP2(B, C, D, E, F, G, H, A, in, 15); \
+ } \
+ (r)[0] = SPH_T32((r)[0] + A); \
+ (r)[1] = SPH_T32((r)[1] + B); \
+ (r)[2] = SPH_T32((r)[2] + C); \
+ (r)[3] = SPH_T32((r)[3] + D); \
+ (r)[4] = SPH_T32((r)[4] + E); \
+ (r)[5] = SPH_T32((r)[5] + F); \
+ (r)[6] = SPH_T32((r)[6] + G); \
+ (r)[7] = SPH_T32((r)[7] + H); \
+ } while (0)
+
+#else
+
+#define SHA2_ROUND_BODY(in, r) do { \
+ sph_u32 A, B, C, D, E, F, G, H, T1, T2; \
+ sph_u32 W00, W01, W02, W03, W04, W05, W06, W07; \
+ sph_u32 W08, W09, W10, W11, W12, W13, W14, W15; \
+ \
+ A = (r)[0]; \
+ B = (r)[1]; \
+ C = (r)[2]; \
+ D = (r)[3]; \
+ E = (r)[4]; \
+ F = (r)[5]; \
+ G = (r)[6]; \
+ H = (r)[7]; \
+ W00 = in(0); \
+ T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \
+ + SPH_C32(0x428A2F98) + W00); \
+ T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \
+ D = SPH_T32(D + T1); \
+ H = SPH_T32(T1 + T2); \
+ W01 = in(1); \
+ T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \
+ + SPH_C32(0x71374491) + W01); \
+ T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \
+ C = SPH_T32(C + T1); \
+ G = SPH_T32(T1 + T2); \
+ W02 = in(2); \
+ T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \
+ + SPH_C32(0xB5C0FBCF) + W02); \
+ T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \
+ B = SPH_T32(B + T1); \
+ F = SPH_T32(T1 + T2); \
+ W03 = in(3); \
+ T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \
+ + SPH_C32(0xE9B5DBA5) + W03); \
+ T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \
+ A = SPH_T32(A + T1); \
+ E = SPH_T32(T1 + T2); \
+ W04 = in(4); \
+ T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \
+ + SPH_C32(0x3956C25B) + W04); \
+ T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \
+ H = SPH_T32(H + T1); \
+ D = SPH_T32(T1 + T2); \
+ W05 = in(5); \
+ T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \
+ + SPH_C32(0x59F111F1) + W05); \
+ T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \
+ G = SPH_T32(G + T1); \
+ C = SPH_T32(T1 + T2); \
+ W06 = in(6); \
+ T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \
+ + SPH_C32(0x923F82A4) + W06); \
+ T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \
+ F = SPH_T32(F + T1); \
+ B = SPH_T32(T1 + T2); \
+ W07 = in(7); \
+ T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \
+ + SPH_C32(0xAB1C5ED5) + W07); \
+ T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \
+ E = SPH_T32(E + T1); \
+ A = SPH_T32(T1 + T2); \
+ W08 = in(8); \
+ T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \
+ + SPH_C32(0xD807AA98) + W08); \
+ T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \
+ D = SPH_T32(D + T1); \
+ H = SPH_T32(T1 + T2); \
+ W09 = in(9); \
+ T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \
+ + SPH_C32(0x12835B01) + W09); \
+ T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \
+ C = SPH_T32(C + T1); \
+ G = SPH_T32(T1 + T2); \
+ W10 = in(10); \
+ T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \
+ + SPH_C32(0x243185BE) + W10); \
+ T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \
+ B = SPH_T32(B + T1); \
+ F = SPH_T32(T1 + T2); \
+ W11 = in(11); \
+ T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \
+ + SPH_C32(0x550C7DC3) + W11); \
+ T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \
+ A = SPH_T32(A + T1); \
+ E = SPH_T32(T1 + T2); \
+ W12 = in(12); \
+ T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \
+ + SPH_C32(0x72BE5D74) + W12); \
+ T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \
+ H = SPH_T32(H + T1); \
+ D = SPH_T32(T1 + T2); \
+ W13 = in(13); \
+ T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \
+ + SPH_C32(0x80DEB1FE) + W13); \
+ T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \
+ G = SPH_T32(G + T1); \
+ C = SPH_T32(T1 + T2); \
+ W14 = in(14); \
+ T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \
+ + SPH_C32(0x9BDC06A7) + W14); \
+ T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \
+ F = SPH_T32(F + T1); \
+ B = SPH_T32(T1 + T2); \
+ W15 = in(15); \
+ T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \
+ + SPH_C32(0xC19BF174) + W15); \
+ T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \
+ E = SPH_T32(E + T1); \
+ A = SPH_T32(T1 + T2); \
+ W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \
+ T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \
+ + SPH_C32(0xE49B69C1) + W00); \
+ T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \
+ D = SPH_T32(D + T1); \
+ H = SPH_T32(T1 + T2); \
+ W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \
+ T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \
+ + SPH_C32(0xEFBE4786) + W01); \
+ T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \
+ C = SPH_T32(C + T1); \
+ G = SPH_T32(T1 + T2); \
+ W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \
+ T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \
+ + SPH_C32(0x0FC19DC6) + W02); \
+ T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \
+ B = SPH_T32(B + T1); \
+ F = SPH_T32(T1 + T2); \
+ W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \
+ T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \
+ + SPH_C32(0x240CA1CC) + W03); \
+ T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \
+ A = SPH_T32(A + T1); \
+ E = SPH_T32(T1 + T2); \
+ W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \
+ T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \
+ + SPH_C32(0x2DE92C6F) + W04); \
+ T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \
+ H = SPH_T32(H + T1); \
+ D = SPH_T32(T1 + T2); \
+ W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \
+ T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \
+ + SPH_C32(0x4A7484AA) + W05); \
+ T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \
+ G = SPH_T32(G + T1); \
+ C = SPH_T32(T1 + T2); \
+ W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \
+ T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \
+ + SPH_C32(0x5CB0A9DC) + W06); \
+ T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \
+ F = SPH_T32(F + T1); \
+ B = SPH_T32(T1 + T2); \
+ W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \
+ T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \
+ + SPH_C32(0x76F988DA) + W07); \
+ T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \
+ E = SPH_T32(E + T1); \
+ A = SPH_T32(T1 + T2); \
+ W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \
+ T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \
+ + SPH_C32(0x983E5152) + W08); \
+ T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \
+ D = SPH_T32(D + T1); \
+ H = SPH_T32(T1 + T2); \
+ W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \
+ T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \
+ + SPH_C32(0xA831C66D) + W09); \
+ T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \
+ C = SPH_T32(C + T1); \
+ G = SPH_T32(T1 + T2); \
+ W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \
+ T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \
+ + SPH_C32(0xB00327C8) + W10); \
+ T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \
+ B = SPH_T32(B + T1); \
+ F = SPH_T32(T1 + T2); \
+ W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \
+ T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \
+ + SPH_C32(0xBF597FC7) + W11); \
+ T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \
+ A = SPH_T32(A + T1); \
+ E = SPH_T32(T1 + T2); \
+ W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \
+ T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \
+ + SPH_C32(0xC6E00BF3) + W12); \
+ T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \
+ H = SPH_T32(H + T1); \
+ D = SPH_T32(T1 + T2); \
+ W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \
+ T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \
+ + SPH_C32(0xD5A79147) + W13); \
+ T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \
+ G = SPH_T32(G + T1); \
+ C = SPH_T32(T1 + T2); \
+ W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \
+ T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \
+ + SPH_C32(0x06CA6351) + W14); \
+ T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \
+ F = SPH_T32(F + T1); \
+ B = SPH_T32(T1 + T2); \
+ W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \
+ T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \
+ + SPH_C32(0x14292967) + W15); \
+ T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \
+ E = SPH_T32(E + T1); \
+ A = SPH_T32(T1 + T2); \
+ W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \
+ T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \
+ + SPH_C32(0x27B70A85) + W00); \
+ T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \
+ D = SPH_T32(D + T1); \
+ H = SPH_T32(T1 + T2); \
+ W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \
+ T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \
+ + SPH_C32(0x2E1B2138) + W01); \
+ T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \
+ C = SPH_T32(C + T1); \
+ G = SPH_T32(T1 + T2); \
+ W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \
+ T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \
+ + SPH_C32(0x4D2C6DFC) + W02); \
+ T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \
+ B = SPH_T32(B + T1); \
+ F = SPH_T32(T1 + T2); \
+ W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \
+ T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \
+ + SPH_C32(0x53380D13) + W03); \
+ T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \
+ A = SPH_T32(A + T1); \
+ E = SPH_T32(T1 + T2); \
+ W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \
+ T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \
+ + SPH_C32(0x650A7354) + W04); \
+ T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \
+ H = SPH_T32(H + T1); \
+ D = SPH_T32(T1 + T2); \
+ W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \
+ T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \
+ + SPH_C32(0x766A0ABB) + W05); \
+ T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \
+ G = SPH_T32(G + T1); \
+ C = SPH_T32(T1 + T2); \
+ W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \
+ T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \
+ + SPH_C32(0x81C2C92E) + W06); \
+ T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \
+ F = SPH_T32(F + T1); \
+ B = SPH_T32(T1 + T2); \
+ W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \
+ T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \
+ + SPH_C32(0x92722C85) + W07); \
+ T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \
+ E = SPH_T32(E + T1); \
+ A = SPH_T32(T1 + T2); \
+ W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \
+ T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \
+ + SPH_C32(0xA2BFE8A1) + W08); \
+ T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \
+ D = SPH_T32(D + T1); \
+ H = SPH_T32(T1 + T2); \
+ W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \
+ T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \
+ + SPH_C32(0xA81A664B) + W09); \
+ T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \
+ C = SPH_T32(C + T1); \
+ G = SPH_T32(T1 + T2); \
+ W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \
+ T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \
+ + SPH_C32(0xC24B8B70) + W10); \
+ T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \
+ B = SPH_T32(B + T1); \
+ F = SPH_T32(T1 + T2); \
+ W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \
+ T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \
+ + SPH_C32(0xC76C51A3) + W11); \
+ T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \
+ A = SPH_T32(A + T1); \
+ E = SPH_T32(T1 + T2); \
+ W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \
+ T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \
+ + SPH_C32(0xD192E819) + W12); \
+ T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \
+ H = SPH_T32(H + T1); \
+ D = SPH_T32(T1 + T2); \
+ W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \
+ T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \
+ + SPH_C32(0xD6990624) + W13); \
+ T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \
+ G = SPH_T32(G + T1); \
+ C = SPH_T32(T1 + T2); \
+ W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \
+ T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \
+ + SPH_C32(0xF40E3585) + W14); \
+ T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \
+ F = SPH_T32(F + T1); \
+ B = SPH_T32(T1 + T2); \
+ W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \
+ T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \
+ + SPH_C32(0x106AA070) + W15); \
+ T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \
+ E = SPH_T32(E + T1); \
+ A = SPH_T32(T1 + T2); \
+ W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \
+ T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \
+ + SPH_C32(0x19A4C116) + W00); \
+ T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \
+ D = SPH_T32(D + T1); \
+ H = SPH_T32(T1 + T2); \
+ W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \
+ T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \
+ + SPH_C32(0x1E376C08) + W01); \
+ T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \
+ C = SPH_T32(C + T1); \
+ G = SPH_T32(T1 + T2); \
+ W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \
+ T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \
+ + SPH_C32(0x2748774C) + W02); \
+ T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \
+ B = SPH_T32(B + T1); \
+ F = SPH_T32(T1 + T2); \
+ W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \
+ T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \
+ + SPH_C32(0x34B0BCB5) + W03); \
+ T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \
+ A = SPH_T32(A + T1); \
+ E = SPH_T32(T1 + T2); \
+ W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \
+ T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \
+ + SPH_C32(0x391C0CB3) + W04); \
+ T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \
+ H = SPH_T32(H + T1); \
+ D = SPH_T32(T1 + T2); \
+ W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \
+ T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \
+ + SPH_C32(0x4ED8AA4A) + W05); \
+ T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \
+ G = SPH_T32(G + T1); \
+ C = SPH_T32(T1 + T2); \
+ W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \
+ T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \
+ + SPH_C32(0x5B9CCA4F) + W06); \
+ T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \
+ F = SPH_T32(F + T1); \
+ B = SPH_T32(T1 + T2); \
+ W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \
+ T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \
+ + SPH_C32(0x682E6FF3) + W07); \
+ T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \
+ E = SPH_T32(E + T1); \
+ A = SPH_T32(T1 + T2); \
+ W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \
+ T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \
+ + SPH_C32(0x748F82EE) + W08); \
+ T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \
+ D = SPH_T32(D + T1); \
+ H = SPH_T32(T1 + T2); \
+ W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \
+ T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \
+ + SPH_C32(0x78A5636F) + W09); \
+ T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \
+ C = SPH_T32(C + T1); \
+ G = SPH_T32(T1 + T2); \
+ W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \
+ T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \
+ + SPH_C32(0x84C87814) + W10); \
+ T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \
+ B = SPH_T32(B + T1); \
+ F = SPH_T32(T1 + T2); \
+ W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \
+ T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \
+ + SPH_C32(0x8CC70208) + W11); \
+ T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \
+ A = SPH_T32(A + T1); \
+ E = SPH_T32(T1 + T2); \
+ W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \
+ T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \
+ + SPH_C32(0x90BEFFFA) + W12); \
+ T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \
+ H = SPH_T32(H + T1); \
+ D = SPH_T32(T1 + T2); \
+ W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \
+ T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \
+ + SPH_C32(0xA4506CEB) + W13); \
+ T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \
+ G = SPH_T32(G + T1); \
+ C = SPH_T32(T1 + T2); \
+ W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \
+ T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \
+ + SPH_C32(0xBEF9A3F7) + W14); \
+ T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \
+ F = SPH_T32(F + T1); \
+ B = SPH_T32(T1 + T2); \
+ W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \
+ T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \
+ + SPH_C32(0xC67178F2) + W15); \
+ T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \
+ E = SPH_T32(E + T1); \
+ A = SPH_T32(T1 + T2); \
+ (r)[0] = SPH_T32((r)[0] + A); \
+ (r)[1] = SPH_T32((r)[1] + B); \
+ (r)[2] = SPH_T32((r)[2] + C); \
+ (r)[3] = SPH_T32((r)[3] + D); \
+ (r)[4] = SPH_T32((r)[4] + E); \
+ (r)[5] = SPH_T32((r)[5] + F); \
+ (r)[6] = SPH_T32((r)[6] + G); \
+ (r)[7] = SPH_T32((r)[7] + H); \
+ } while (0)
+
+#endif
+
+/*
+ * One round of SHA-224 / SHA-256. The data must be aligned for 32-bit access.
+ */
+static void
+sha2_round(const unsigned char *data, sph_u32 r[8])
+{
+#define SHA2_IN(x) sph_dec32be_aligned(data + (4 * (x)))
+ SHA2_ROUND_BODY(SHA2_IN, r);
+#undef SHA2_IN
+}
+
+/* see sph_sha2.h */
+void
+sph_sha224_init(void *cc)
+{
+ sph_sha224_context *sc;
+
+ sc = cc;
+ memcpy(sc->val, H224, sizeof H224);
+#if SPH_64
+ sc->count = 0;
+#else
+ sc->count_high = sc->count_low = 0;
+#endif
+}
+
+/* see sph_sha2.h */
+void
+sph_sha256_init(void *cc)
+{
+ sph_sha256_context *sc;
+
+ sc = cc;
+ memcpy(sc->val, H256, sizeof H256);
+#if SPH_64
+ sc->count = 0;
+#else
+ sc->count_high = sc->count_low = 0;
+#endif
+}
+
+#define RFUN sha2_round
+#define HASH sha224
+#define BE32 1
+#include "sph/md_helper.c"
+
+/* see sph_sha2.h */
+void
+sph_sha224_close(void *cc, void *dst)
+{
+ sha224_close(cc, dst, 7);
+ sph_sha224_init(cc);
+}
+
+/* see sph_sha2.h */
+void
+sph_sha224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst)
+{
+ sha224_addbits_and_close(cc, ub, n, dst, 7);
+ sph_sha224_init(cc);
+}
+
+/* see sph_sha2.h */
+void
+sph_sha256_close(void *cc, void *dst)
+{
+ sha224_close(cc, dst, 8);
+ sph_sha256_init(cc);
+}
+
+/* see sph_sha2.h */
+void
+sph_sha256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst)
+{
+ sha224_addbits_and_close(cc, ub, n, dst, 8);
+ sph_sha256_init(cc);
+}
+
+/* see sph_sha2.h */
+void
+sph_sha224_comp(const sph_u32 msg[16], sph_u32 val[8])
+{
+#define SHA2_IN(x) msg[x]
+ SHA2_ROUND_BODY(SHA2_IN, val);
+#undef SHA2_IN
+}
diff --git a/libqpdf/sha2big.c b/libqpdf/sha2big.c
new file mode 100644
index 0000000..e4aadbd
--- /dev/null
+++ b/libqpdf/sha2big.c
@@ -0,0 +1,247 @@
+/* $Id: sha2big.c 216 2010-06-08 09:46:57Z tp $ */
+/*
+ * SHA-384 / SHA-512 implementation.
+ *
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010 Projet RNRT SAPHIR
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ *
+ * @author Thomas Pornin <thomas.pornin@cryptolog.com>
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "sph/sph_sha2.h"
+
+#if SPH_64
+
+#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z))
+#define MAJ(X, Y, Z) (((X) & (Y)) | (((X) | (Y)) & (Z)))
+
+#define ROTR64 SPH_ROTR64
+
+#define BSG5_0(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39))
+#define BSG5_1(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41))
+#define SSG5_0(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ SPH_T64((x) >> 7))
+#define SSG5_1(x) (ROTR64(x, 19) ^ ROTR64(x, 61) ^ SPH_T64((x) >> 6))
+
+static const sph_u64 K512[80] = {
+ SPH_C64(0x428A2F98D728AE22), SPH_C64(0x7137449123EF65CD),
+ SPH_C64(0xB5C0FBCFEC4D3B2F), SPH_C64(0xE9B5DBA58189DBBC),
+ SPH_C64(0x3956C25BF348B538), SPH_C64(0x59F111F1B605D019),
+ SPH_C64(0x923F82A4AF194F9B), SPH_C64(0xAB1C5ED5DA6D8118),
+ SPH_C64(0xD807AA98A3030242), SPH_C64(0x12835B0145706FBE),
+ SPH_C64(0x243185BE4EE4B28C), SPH_C64(0x550C7DC3D5FFB4E2),
+ SPH_C64(0x72BE5D74F27B896F), SPH_C64(0x80DEB1FE3B1696B1),
+ SPH_C64(0x9BDC06A725C71235), SPH_C64(0xC19BF174CF692694),
+ SPH_C64(0xE49B69C19EF14AD2), SPH_C64(0xEFBE4786384F25E3),
+ SPH_C64(0x0FC19DC68B8CD5B5), SPH_C64(0x240CA1CC77AC9C65),
+ SPH_C64(0x2DE92C6F592B0275), SPH_C64(0x4A7484AA6EA6E483),
+ SPH_C64(0x5CB0A9DCBD41FBD4), SPH_C64(0x76F988DA831153B5),
+ SPH_C64(0x983E5152EE66DFAB), SPH_C64(0xA831C66D2DB43210),
+ SPH_C64(0xB00327C898FB213F), SPH_C64(0xBF597FC7BEEF0EE4),
+ SPH_C64(0xC6E00BF33DA88FC2), SPH_C64(0xD5A79147930AA725),
+ SPH_C64(0x06CA6351E003826F), SPH_C64(0x142929670A0E6E70),
+ SPH_C64(0x27B70A8546D22FFC), SPH_C64(0x2E1B21385C26C926),
+ SPH_C64(0x4D2C6DFC5AC42AED), SPH_C64(0x53380D139D95B3DF),
+ SPH_C64(0x650A73548BAF63DE), SPH_C64(0x766A0ABB3C77B2A8),
+ SPH_C64(0x81C2C92E47EDAEE6), SPH_C64(0x92722C851482353B),
+ SPH_C64(0xA2BFE8A14CF10364), SPH_C64(0xA81A664BBC423001),
+ SPH_C64(0xC24B8B70D0F89791), SPH_C64(0xC76C51A30654BE30),
+ SPH_C64(0xD192E819D6EF5218), SPH_C64(0xD69906245565A910),
+ SPH_C64(0xF40E35855771202A), SPH_C64(0x106AA07032BBD1B8),
+ SPH_C64(0x19A4C116B8D2D0C8), SPH_C64(0x1E376C085141AB53),
+ SPH_C64(0x2748774CDF8EEB99), SPH_C64(0x34B0BCB5E19B48A8),
+ SPH_C64(0x391C0CB3C5C95A63), SPH_C64(0x4ED8AA4AE3418ACB),
+ SPH_C64(0x5B9CCA4F7763E373), SPH_C64(0x682E6FF3D6B2B8A3),
+ SPH_C64(0x748F82EE5DEFB2FC), SPH_C64(0x78A5636F43172F60),
+ SPH_C64(0x84C87814A1F0AB72), SPH_C64(0x8CC702081A6439EC),
+ SPH_C64(0x90BEFFFA23631E28), SPH_C64(0xA4506CEBDE82BDE9),
+ SPH_C64(0xBEF9A3F7B2C67915), SPH_C64(0xC67178F2E372532B),
+ SPH_C64(0xCA273ECEEA26619C), SPH_C64(0xD186B8C721C0C207),
+ SPH_C64(0xEADA7DD6CDE0EB1E), SPH_C64(0xF57D4F7FEE6ED178),
+ SPH_C64(0x06F067AA72176FBA), SPH_C64(0x0A637DC5A2C898A6),
+ SPH_C64(0x113F9804BEF90DAE), SPH_C64(0x1B710B35131C471B),
+ SPH_C64(0x28DB77F523047D84), SPH_C64(0x32CAAB7B40C72493),
+ SPH_C64(0x3C9EBE0A15C9BEBC), SPH_C64(0x431D67C49C100D4C),
+ SPH_C64(0x4CC5D4BECB3E42B6), SPH_C64(0x597F299CFC657E2A),
+ SPH_C64(0x5FCB6FAB3AD6FAEC), SPH_C64(0x6C44198C4A475817)
+};
+
+static const sph_u64 H384[8] = {
+ SPH_C64(0xCBBB9D5DC1059ED8), SPH_C64(0x629A292A367CD507),
+ SPH_C64(0x9159015A3070DD17), SPH_C64(0x152FECD8F70E5939),
+ SPH_C64(0x67332667FFC00B31), SPH_C64(0x8EB44A8768581511),
+ SPH_C64(0xDB0C2E0D64F98FA7), SPH_C64(0x47B5481DBEFA4FA4)
+};
+
+static const sph_u64 H512[8] = {
+ SPH_C64(0x6A09E667F3BCC908), SPH_C64(0xBB67AE8584CAA73B),
+ SPH_C64(0x3C6EF372FE94F82B), SPH_C64(0xA54FF53A5F1D36F1),
+ SPH_C64(0x510E527FADE682D1), SPH_C64(0x9B05688C2B3E6C1F),
+ SPH_C64(0x1F83D9ABFB41BD6B), SPH_C64(0x5BE0CD19137E2179)
+};
+
+/*
+ * This macro defines the body for a SHA-384 / SHA-512 compression function
+ * implementation. The "in" parameter should evaluate, when applied to a
+ * numerical input parameter from 0 to 15, to an expression which yields
+ * the corresponding input block. The "r" parameter should evaluate to
+ * an array or pointer expression designating the array of 8 words which
+ * contains the input and output of the compression function.
+ *
+ * SHA-512 is hard for the compiler. If the loop is completely unrolled,
+ * then the code will be quite huge (possibly more than 100 kB), and the
+ * performance will be degraded due to cache misses on the code. We
+ * unroll only eight steps, which avoids all needless copies when
+ * 64-bit registers are swapped.
+ */
+
+#define SHA3_STEP(A, B, C, D, E, F, G, H, i) do { \
+ sph_u64 T1, T2; \
+ T1 = SPH_T64(H + BSG5_1(E) + CH(E, F, G) + K512[i] + W[i]); \
+ T2 = SPH_T64(BSG5_0(A) + MAJ(A, B, C)); \
+ D = SPH_T64(D + T1); \
+ H = SPH_T64(T1 + T2); \
+ } while (0)
+
+#define SHA3_ROUND_BODY(in, r) do { \
+ int i; \
+ sph_u64 A, B, C, D, E, F, G, H; \
+ sph_u64 W[80]; \
+ \
+ for (i = 0; i < 16; i ++) \
+ W[i] = in(i); \
+ for (i = 16; i < 80; i ++) \
+ W[i] = SPH_T64(SSG5_1(W[i - 2]) + W[i - 7] \
+ + SSG5_0(W[i - 15]) + W[i - 16]); \
+ A = (r)[0]; \
+ B = (r)[1]; \
+ C = (r)[2]; \
+ D = (r)[3]; \
+ E = (r)[4]; \
+ F = (r)[5]; \
+ G = (r)[6]; \
+ H = (r)[7]; \
+ for (i = 0; i < 80; i += 8) { \
+ SHA3_STEP(A, B, C, D, E, F, G, H, i + 0); \
+ SHA3_STEP(H, A, B, C, D, E, F, G, i + 1); \
+ SHA3_STEP(G, H, A, B, C, D, E, F, i + 2); \
+ SHA3_STEP(F, G, H, A, B, C, D, E, i + 3); \
+ SHA3_STEP(E, F, G, H, A, B, C, D, i + 4); \
+ SHA3_STEP(D, E, F, G, H, A, B, C, i + 5); \
+ SHA3_STEP(C, D, E, F, G, H, A, B, i + 6); \
+ SHA3_STEP(B, C, D, E, F, G, H, A, i + 7); \
+ } \
+ (r)[0] = SPH_T64((r)[0] + A); \
+ (r)[1] = SPH_T64((r)[1] + B); \
+ (r)[2] = SPH_T64((r)[2] + C); \
+ (r)[3] = SPH_T64((r)[3] + D); \
+ (r)[4] = SPH_T64((r)[4] + E); \
+ (r)[5] = SPH_T64((r)[5] + F); \
+ (r)[6] = SPH_T64((r)[6] + G); \
+ (r)[7] = SPH_T64((r)[7] + H); \
+ } while (0)
+
+/*
+ * One round of SHA-384 / SHA-512. The data must be aligned for 64-bit access.
+ */
+static void
+sha3_round(const unsigned char *data, sph_u64 r[8])
+{
+#define SHA3_IN(x) sph_dec64be_aligned(data + (8 * (x)))
+ SHA3_ROUND_BODY(SHA3_IN, r);
+#undef SHA3_IN
+}
+
+/* see sph_sha3.h */
+void
+sph_sha384_init(void *cc)
+{
+ sph_sha384_context *sc;
+
+ sc = cc;
+ memcpy(sc->val, H384, sizeof H384);
+ sc->count = 0;
+}
+
+/* see sph_sha3.h */
+void
+sph_sha512_init(void *cc)
+{
+ sph_sha512_context *sc;
+
+ sc = cc;
+ memcpy(sc->val, H512, sizeof H512);
+ sc->count = 0;
+}
+
+#define RFUN sha3_round
+#define HASH sha384
+#define BE64 1
+#include "sph/md_helper.c"
+
+/* see sph_sha3.h */
+void
+sph_sha384_close(void *cc, void *dst)
+{
+ sha384_close(cc, dst, 6);
+ sph_sha384_init(cc);
+}
+
+/* see sph_sha3.h */
+void
+sph_sha384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst)
+{
+ sha384_addbits_and_close(cc, ub, n, dst, 6);
+ sph_sha384_init(cc);
+}
+
+/* see sph_sha3.h */
+void
+sph_sha512_close(void *cc, void *dst)
+{
+ sha384_close(cc, dst, 8);
+ sph_sha512_init(cc);
+}
+
+/* see sph_sha3.h */
+void
+sph_sha512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst)
+{
+ sha384_addbits_and_close(cc, ub, n, dst, 8);
+ sph_sha512_init(cc);
+}
+
+/* see sph_sha3.h */
+void
+sph_sha384_comp(const sph_u64 msg[16], sph_u64 val[8])
+{
+#define SHA3_IN(x) msg[x]
+ SHA3_ROUND_BODY(SHA3_IN, val);
+#undef SHA3_IN
+}
+
+#endif
diff --git a/libqpdf/sph/md_helper.c b/libqpdf/sph/md_helper.c
new file mode 100644
index 0000000..5384f03
--- /dev/null
+++ b/libqpdf/sph/md_helper.c
@@ -0,0 +1,346 @@
+/* $Id: md_helper.c 216 2010-06-08 09:46:57Z tp $ */
+/*
+ * This file contains some functions which implement the external data
+ * handling and padding for Merkle-Damgard hash functions which follow
+ * the conventions set out by MD4 (little-endian) or SHA-1 (big-endian).
+ *
+ * API: this file is meant to be included, not compiled as a stand-alone
+ * file. Some macros must be defined:
+ * RFUN name for the round function
+ * HASH "short name" for the hash function
+ * BE32 defined for big-endian, 32-bit based (e.g. SHA-1)
+ * LE32 defined for little-endian, 32-bit based (e.g. MD5)
+ * BE64 defined for big-endian, 64-bit based (e.g. SHA-512)
+ * LE64 defined for little-endian, 64-bit based (no example yet)
+ * PW01 if defined, append 0x01 instead of 0x80 (for Tiger)
+ * BLEN if defined, length of a message block (in bytes)
+ * PLW1 if defined, length is defined on one 64-bit word only (for Tiger)
+ * PLW4 if defined, length is defined on four 64-bit words (for WHIRLPOOL)
+ * SVAL if defined, reference to the context state information
+ *
+ * BLEN is used when a message block is not 16 (32-bit or 64-bit) words:
+ * this is used for instance for Tiger, which works on 64-bit words but
+ * uses 512-bit message blocks (eight 64-bit words). PLW1 and PLW4 are
+ * ignored if 32-bit words are used; if 64-bit words are used and PLW1 is
+ * set, then only one word (64 bits) will be used to encode the input
+ * message length (in bits), otherwise two words will be used (as in
+ * SHA-384 and SHA-512). If 64-bit words are used and PLW4 is defined (but
+ * not PLW1), four 64-bit words will be used to encode the message length
+ * (in bits). Note that regardless of those settings, only 64-bit message
+ * lengths are supported (in bits): messages longer than 2 Exabytes will be
+ * improperly hashed (this is unlikely to happen soon: 2 Exabytes is about
+ * 2 millions Terabytes, which is huge).
+ *
+ * If CLOSE_ONLY is defined, then this file defines only the sph_XXX_close()
+ * function. This is used for Tiger2, which is identical to Tiger except
+ * when it comes to the padding (Tiger2 uses the standard 0x80 byte instead
+ * of the 0x01 from original Tiger).
+ *
+ * The RFUN function is invoked with two arguments, the first pointing to
+ * aligned data (as a "const void *"), the second being state information
+ * from the context structure. By default, this state information is the
+ * "val" field from the context, and this field is assumed to be an array
+ * of words ("sph_u32" or "sph_u64", depending on BE32/LE32/BE64/LE64).
+ * from the context structure. The "val" field can have any type, except
+ * for the output encoding which assumes that it is an array of "sph_u32"
+ * values. By defining NO_OUTPUT, this last step is deactivated; the
+ * includer code is then responsible for writing out the hash result. When
+ * NO_OUTPUT is defined, the third parameter to the "close()" function is
+ * ignored.
+ *
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010 Projet RNRT SAPHIR
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ *
+ * @author Thomas Pornin <thomas.pornin@cryptolog.com>
+ */
+
+#ifdef _MSC_VER
+#pragma warning (disable: 4146)
+#endif
+
+#undef SPH_XCAT
+#define SPH_XCAT(a, b) SPH_XCAT_(a, b)
+#undef SPH_XCAT_
+#define SPH_XCAT_(a, b) a ## b
+
+#undef SPH_BLEN
+#undef SPH_WLEN
+#if defined BE64 || defined LE64
+#define SPH_BLEN 128U
+#define SPH_WLEN 8U
+#else
+#define SPH_BLEN 64U
+#define SPH_WLEN 4U
+#endif
+
+#ifdef BLEN
+#undef SPH_BLEN
+#define SPH_BLEN BLEN
+#endif
+
+#undef SPH_MAXPAD
+#if defined PLW1
+#define SPH_MAXPAD (SPH_BLEN - SPH_WLEN)
+#elif defined PLW4
+#define SPH_MAXPAD (SPH_BLEN - (SPH_WLEN << 2))
+#else
+#define SPH_MAXPAD (SPH_BLEN - (SPH_WLEN << 1))
+#endif
+
+#undef SPH_VAL
+#undef SPH_NO_OUTPUT
+#ifdef SVAL
+#define SPH_VAL SVAL
+#define SPH_NO_OUTPUT 1
+#else
+#define SPH_VAL sc->val
+#endif
+
+#ifndef CLOSE_ONLY
+
+#ifdef SPH_UPTR
+static void
+SPH_XCAT(HASH, _short)(void *cc, const void *data, size_t len)
+#else
+void
+SPH_XCAT(sph_, HASH)(void *cc, const void *data, size_t len)
+#endif
+{
+ SPH_XCAT(sph_, SPH_XCAT(HASH, _context)) *sc;
+ unsigned current;
+
+ sc = cc;
+#if SPH_64
+ current = (unsigned)sc->count & (SPH_BLEN - 1U);
+#else
+ current = (unsigned)sc->count_low & (SPH_BLEN - 1U);
+#endif
+ while (len > 0) {
+ unsigned clen;
+#if !SPH_64
+ sph_u32 clow, clow2;
+#endif
+
+ clen = SPH_BLEN - current;
+ if (clen > len)
+ clen = len;
+ memcpy(sc->buf + current, data, clen);
+ data = (const unsigned char *)data + clen;
+ current += clen;
+ len -= clen;
+ if (current == SPH_BLEN) {
+ RFUN(sc->buf, SPH_VAL);
+ current = 0;
+ }
+#if SPH_64
+ sc->count += clen;
+#else
+ clow = sc->count_low;
+ clow2 = SPH_T32(clow + clen);
+ sc->count_low = clow2;
+ if (clow2 < clow)
+ sc->count_high ++;
+#endif
+ }
+}
+
+#ifdef SPH_UPTR
+void
+SPH_XCAT(sph_, HASH)(void *cc, const void *data, size_t len)
+{
+ SPH_XCAT(sph_, SPH_XCAT(HASH, _context)) *sc;
+ unsigned current;
+ size_t orig_len;
+#if !SPH_64
+ sph_u32 clow, clow2;
+#endif
+
+ if (len < (2 * SPH_BLEN)) {
+ SPH_XCAT(HASH, _short)(cc, data, len);
+ return;
+ }
+ sc = cc;
+#if SPH_64
+ current = (unsigned)sc->count & (SPH_BLEN - 1U);
+#else
+ current = (unsigned)sc->count_low & (SPH_BLEN - 1U);
+#endif
+ if (current > 0) {
+ unsigned t;
+
+ t = SPH_BLEN - current;
+ SPH_XCAT(HASH, _short)(cc, data, t);
+ data = (const unsigned char *)data + t;
+ len -= t;
+ }
+#if !SPH_UNALIGNED
+ if (((SPH_UPTR)data & (SPH_WLEN - 1U)) != 0) {
+ SPH_XCAT(HASH, _short)(cc, data, len);
+ return;
+ }
+#endif
+ orig_len = len;
+ while (len >= SPH_BLEN) {
+ RFUN(data, SPH_VAL);
+ len -= SPH_BLEN;
+ data = (const unsigned char *)data + SPH_BLEN;
+ }
+ if (len > 0)
+ memcpy(sc->buf, data, len);
+#if SPH_64
+ sc->count += (sph_u64)orig_len;
+#else
+ clow = sc->count_low;
+ clow2 = SPH_T32(clow + orig_len);
+ sc->count_low = clow2;
+ if (clow2 < clow)
+ sc->count_high ++;
+ /*
+ * This code handles the improbable situation where "size_t" is
+ * greater than 32 bits, and yet we do not have a 64-bit type.
+ */
+ orig_len >>= 12;
+ orig_len >>= 10;
+ orig_len >>= 10;
+ sc->count_high += orig_len;
+#endif
+}
+#endif
+
+#endif
+
+/*
+ * Perform padding and produce result. The context is NOT reinitialized
+ * by this function.
+ */
+static void
+SPH_XCAT(HASH, _addbits_and_close)(void *cc,
+ unsigned ub, unsigned n, void *dst, unsigned rnum)
+{
+ SPH_XCAT(sph_, SPH_XCAT(HASH, _context)) *sc;
+ unsigned current, u;
+#if !SPH_64
+ sph_u32 low, high;
+#endif
+
+ sc = cc;
+#if SPH_64
+ current = (unsigned)sc->count & (SPH_BLEN - 1U);
+#else
+ current = (unsigned)sc->count_low & (SPH_BLEN - 1U);
+#endif
+#ifdef PW01
+ sc->buf[current ++] = (0x100 | (ub & 0xFF)) >> (8 - n);
+#else
+ {
+ unsigned z;
+
+ z = 0x80 >> n;
+ sc->buf[current ++] = ((ub & -z) | z) & 0xFF;
+ }
+#endif
+ if (current > SPH_MAXPAD) {
+ memset(sc->buf + current, 0, SPH_BLEN - current);
+ RFUN(sc->buf, SPH_VAL);
+ memset(sc->buf, 0, SPH_MAXPAD);
+ } else {
+ memset(sc->buf + current, 0, SPH_MAXPAD - current);
+ }
+#if defined BE64
+#if defined PLW1
+ sph_enc64be_aligned(sc->buf + SPH_MAXPAD,
+ SPH_T64(sc->count << 3) + (sph_u64)n);
+#elif defined PLW4
+ memset(sc->buf + SPH_MAXPAD, 0, 2 * SPH_WLEN);
+ sph_enc64be_aligned(sc->buf + SPH_MAXPAD + 2 * SPH_WLEN,
+ sc->count >> 61);
+ sph_enc64be_aligned(sc->buf + SPH_MAXPAD + 3 * SPH_WLEN,
+ SPH_T64(sc->count << 3) + (sph_u64)n);
+#else
+ sph_enc64be_aligned(sc->buf + SPH_MAXPAD, sc->count >> 61);
+ sph_enc64be_aligned(sc->buf + SPH_MAXPAD + SPH_WLEN,
+ SPH_T64(sc->count << 3) + (sph_u64)n);
+#endif
+#elif defined LE64
+#if defined PLW1
+ sph_enc64le_aligned(sc->buf + SPH_MAXPAD,
+ SPH_T64(sc->count << 3) + (sph_u64)n);
+#elif defined PLW1
+ sph_enc64le_aligned(sc->buf + SPH_MAXPAD,
+ SPH_T64(sc->count << 3) + (sph_u64)n);
+ sph_enc64le_aligned(sc->buf + SPH_MAXPAD + SPH_WLEN, sc->count >> 61);
+ memset(sc->buf + SPH_MAXPAD + 2 * SPH_WLEN, 0, 2 * SPH_WLEN);
+#else
+ sph_enc64le_aligned(sc->buf + SPH_MAXPAD,
+ SPH_T64(sc->count << 3) + (sph_u64)n);
+ sph_enc64le_aligned(sc->buf + SPH_MAXPAD + SPH_WLEN, sc->count >> 61);
+#endif
+#else
+#if SPH_64
+#ifdef BE32
+ sph_enc64be_aligned(sc->buf + SPH_MAXPAD,
+ SPH_T64(sc->count << 3) + (sph_u64)n);
+#else
+ sph_enc64le_aligned(sc->buf + SPH_MAXPAD,
+ SPH_T64(sc->count << 3) + (sph_u64)n);
+#endif
+#else
+ low = sc->count_low;
+ high = SPH_T32((sc->count_high << 3) | (low >> 29));
+ low = SPH_T32(low << 3) + (sph_u32)n;
+#ifdef BE32
+ sph_enc32be(sc->buf + SPH_MAXPAD, high);
+ sph_enc32be(sc->buf + SPH_MAXPAD + SPH_WLEN, low);
+#else
+ sph_enc32le(sc->buf + SPH_MAXPAD, low);
+ sph_enc32le(sc->buf + SPH_MAXPAD + SPH_WLEN, high);
+#endif
+#endif
+#endif
+ RFUN(sc->buf, SPH_VAL);
+#ifdef SPH_NO_OUTPUT
+ (void)dst;
+ (void)rnum;
+ (void)u;
+#else
+ for (u = 0; u < rnum; u ++) {
+#if defined BE64
+ sph_enc64be((unsigned char *)dst + 8 * u, sc->val[u]);
+#elif defined LE64
+ sph_enc64le((unsigned char *)dst + 8 * u, sc->val[u]);
+#elif defined BE32
+ sph_enc32be((unsigned char *)dst + 4 * u, sc->val[u]);
+#else
+ sph_enc32le((unsigned char *)dst + 4 * u, sc->val[u]);
+#endif
+ }
+#endif
+}
+
+static void
+SPH_XCAT(HASH, _close)(void *cc, void *dst, unsigned rnum)
+{
+ SPH_XCAT(HASH, _addbits_and_close)(cc, 0, 0, dst, rnum);
+}
diff --git a/libqpdf/sph/sph_sha2.h b/libqpdf/sph/sph_sha2.h
new file mode 100644
index 0000000..4bff9cd
--- /dev/null
+++ b/libqpdf/sph/sph_sha2.h
@@ -0,0 +1,378 @@
+/* $Id: sph_sha2.h 216 2010-06-08 09:46:57Z tp $ */
+/**
+ * SHA-224, SHA-256, SHA-384 and SHA-512 interface.
+ *
+ * SHA-256 has been published in FIPS 180-2, now amended with a change
+ * notice to include SHA-224 as well (which is a simple variation on
+ * SHA-256). SHA-384 and SHA-512 are also defined in FIPS 180-2. FIPS
+ * standards can be found at:
+ * http://csrc.nist.gov/publications/fips/
+ *
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010 Projet RNRT SAPHIR
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ *
+ * @file sph_sha2.h
+ * @author Thomas Pornin <thomas.pornin@cryptolog.com>
+ */
+
+#ifndef SPH_SHA2_H__
+#define SPH_SHA2_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include "sph_types.h"
+
+/**
+ * Output size (in bits) for SHA-224.
+ */
+#define SPH_SIZE_sha224 224
+
+/**
+ * Output size (in bits) for SHA-256.
+ */
+#define SPH_SIZE_sha256 256
+
+/**
+ * This structure is a context for SHA-224 computations: it contains the
+ * intermediate values and some data from the last entered block. Once
+ * a SHA-224 computation has been performed, the context can be reused for
+ * another computation.
+ *
+ * The contents of this structure are private. A running SHA-224 computation
+ * can be cloned by copying the context (e.g. with a simple
+ * <code>memcpy()</code>).
+ */
+typedef struct {
+#ifndef DOXYGEN_IGNORE
+ unsigned char buf[64]; /* first field, for alignment */
+ sph_u32 val[8];
+#if SPH_64
+ sph_u64 count;
+#else
+ sph_u32 count_high, count_low;
+#endif
+#endif
+} sph_sha224_context;
+
+/**
+ * This structure is a context for SHA-256 computations. It is identical
+ * to the SHA-224 context. However, a context is initialized for SHA-224
+ * <strong>or</strong> SHA-256, but not both (the internal IV is not the
+ * same).
+ */
+typedef sph_sha224_context sph_sha256_context;
+
+/**
+ * Initialize a SHA-224 context. This process performs no memory allocation.
+ *
+ * @param cc the SHA-224 context (pointer to
+ * a <code>sph_sha224_context</code>)
+ */
+void sph_sha224_init(void *cc);
+
+/**
+ * Process some data bytes. It is acceptable that <code>len</code> is zero
+ * (in which case this function does nothing).
+ *
+ * @param cc the SHA-224 context
+ * @param data the input data
+ * @param len the input data length (in bytes)
+ */
+void sph_sha224(void *cc, const void *data, size_t len);
+
+/**
+ * Terminate the current SHA-224 computation and output the result into the
+ * provided buffer. The destination buffer must be wide enough to
+ * accomodate the result (28 bytes). The context is automatically
+ * reinitialized.
+ *
+ * @param cc the SHA-224 context
+ * @param dst the destination buffer
+ */
+void sph_sha224_close(void *cc, void *dst);
+
+/**
+ * Add a few additional bits (0 to 7) to the current computation, then
+ * terminate it and output the result in the provided buffer, which must
+ * be wide enough to accomodate the result (28 bytes). If bit number i
+ * in <code>ub</code> has value 2^i, then the extra bits are those
+ * numbered 7 downto 8-n (this is the big-endian convention at the byte
+ * level). The context is automatically reinitialized.
+ *
+ * @param cc the SHA-224 context
+ * @param ub the extra bits
+ * @param n the number of extra bits (0 to 7)
+ * @param dst the destination buffer
+ */
+void sph_sha224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst);
+
+/**
+ * Apply the SHA-224 compression function on the provided data. The
+ * <code>msg</code> parameter contains the 16 32-bit input blocks,
+ * as numerical values (hence after the big-endian decoding). The
+ * <code>val</code> parameter contains the 8 32-bit input blocks for
+ * the compression function; the output is written in place in this
+ * array.
+ *
+ * @param msg the message block (16 values)
+ * @param val the function 256-bit input and output
+ */
+void sph_sha224_comp(const sph_u32 msg[16], sph_u32 val[8]);
+
+/**
+ * Initialize a SHA-256 context. This process performs no memory allocation.
+ *
+ * @param cc the SHA-256 context (pointer to
+ * a <code>sph_sha256_context</code>)
+ */
+void sph_sha256_init(void *cc);
+
+#ifdef DOXYGEN_IGNORE
+/**
+ * Process some data bytes, for SHA-256. This function is identical to
+ * <code>sha_224()</code>
+ *
+ * @param cc the SHA-224 context
+ * @param data the input data
+ * @param len the input data length (in bytes)
+ */
+void sph_sha256(void *cc, const void *data, size_t len);
+#endif
+
+#ifndef DOXYGEN_IGNORE
+#define sph_sha256 sph_sha224
+#endif
+
+/**
+ * Terminate the current SHA-256 computation and output the result into the
+ * provided buffer. The destination buffer must be wide enough to
+ * accomodate the result (32 bytes). The context is automatically
+ * reinitialized.
+ *
+ * @param cc the SHA-256 context
+ * @param dst the destination buffer
+ */
+void sph_sha256_close(void *cc, void *dst);
+
+/**
+ * Add a few additional bits (0 to 7) to the current computation, then
+ * terminate it and output the result in the provided buffer, which must
+ * be wide enough to accomodate the result (32 bytes). If bit number i
+ * in <code>ub</code> has value 2^i, then the extra bits are those
+ * numbered 7 downto 8-n (this is the big-endian convention at the byte
+ * level). The context is automatically reinitialized.
+ *
+ * @param cc the SHA-256 context
+ * @param ub the extra bits
+ * @param n the number of extra bits (0 to 7)
+ * @param dst the destination buffer
+ */
+void sph_sha256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst);
+
+#ifdef DOXYGEN_IGNORE
+/**
+ * Apply the SHA-256 compression function on the provided data. This
+ * function is identical to <code>sha224_comp()</code>.
+ *
+ * @param msg the message block (16 values)
+ * @param val the function 256-bit input and output
+ */
+void sph_sha256_comp(const sph_u32 msg[16], sph_u32 val[8]);
+#endif
+
+#ifndef DOXYGEN_IGNORE
+#define sph_sha256_comp sph_sha224_comp
+#endif
+
+#if SPH_64
+
+/**
+ * Output size (in bits) for SHA-384.
+ */
+#define SPH_SIZE_sha384 384
+
+/**
+ * Output size (in bits) for SHA-512.
+ */
+#define SPH_SIZE_sha512 512
+
+/**
+ * This structure is a context for SHA-384 computations: it contains the
+ * intermediate values and some data from the last entered block. Once
+ * a SHA-384 computation has been performed, the context can be reused for
+ * another computation.
+ *
+ * The contents of this structure are private. A running SHA-384 computation
+ * can be cloned by copying the context (e.g. with a simple
+ * <code>memcpy()</code>).
+ */
+typedef struct {
+#ifndef DOXYGEN_IGNORE
+ unsigned char buf[128]; /* first field, for alignment */
+ sph_u64 val[8];
+ sph_u64 count;
+#endif
+} sph_sha384_context;
+
+/**
+ * Initialize a SHA-384 context. This process performs no memory allocation.
+ *
+ * @param cc the SHA-384 context (pointer to
+ * a <code>sph_sha384_context</code>)
+ */
+void sph_sha384_init(void *cc);
+
+/**
+ * Process some data bytes. It is acceptable that <code>len</code> is zero
+ * (in which case this function does nothing).
+ *
+ * @param cc the SHA-384 context
+ * @param data the input data
+ * @param len the input data length (in bytes)
+ */
+void sph_sha384(void *cc, const void *data, size_t len);
+
+/**
+ * Terminate the current SHA-384 computation and output the result into the
+ * provided buffer. The destination buffer must be wide enough to
+ * accomodate the result (48 bytes). The context is automatically
+ * reinitialized.
+ *
+ * @param cc the SHA-384 context
+ * @param dst the destination buffer
+ */
+void sph_sha384_close(void *cc, void *dst);
+
+/**
+ * Add a few additional bits (0 to 7) to the current computation, then
+ * terminate it and output the result in the provided buffer, which must
+ * be wide enough to accomodate the result (48 bytes). If bit number i
+ * in <code>ub</code> has value 2^i, then the extra bits are those
+ * numbered 7 downto 8-n (this is the big-endian convention at the byte
+ * level). The context is automatically reinitialized.
+ *
+ * @param cc the SHA-384 context
+ * @param ub the extra bits
+ * @param n the number of extra bits (0 to 7)
+ * @param dst the destination buffer
+ */
+void sph_sha384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst);
+
+/**
+ * Apply the SHA-384 compression function on the provided data. The
+ * <code>msg</code> parameter contains the 16 64-bit input blocks,
+ * as numerical values (hence after the big-endian decoding). The
+ * <code>val</code> parameter contains the 8 64-bit input blocks for
+ * the compression function; the output is written in place in this
+ * array.
+ *
+ * @param msg the message block (16 values)
+ * @param val the function 512-bit input and output
+ */
+void sph_sha384_comp(const sph_u64 msg[16], sph_u64 val[8]);
+
+/**
+ * This structure is a context for SHA-512 computations. It is identical
+ * to the SHA-384 context. However, a context is initialized for SHA-384
+ * <strong>or</strong> SHA-512, but not both (the internal IV is not the
+ * same).
+ */
+typedef sph_sha384_context sph_sha512_context;
+
+/**
+ * Initialize a SHA-512 context. This process performs no memory allocation.
+ *
+ * @param cc the SHA-512 context (pointer to
+ * a <code>sph_sha512_context</code>)
+ */
+void sph_sha512_init(void *cc);
+
+#ifdef DOXYGEN_IGNORE
+/**
+ * Process some data bytes, for SHA-512. This function is identical to
+ * <code>sph_sha384()</code>.
+ *
+ * @param cc the SHA-384 context
+ * @param data the input data
+ * @param len the input data length (in bytes)
+ */
+void sph_sha512(void *cc, const void *data, size_t len);
+#endif
+
+#ifndef DOXYGEN_IGNORE
+#define sph_sha512 sph_sha384
+#endif
+
+/**
+ * Terminate the current SHA-512 computation and output the result into the
+ * provided buffer. The destination buffer must be wide enough to
+ * accomodate the result (64 bytes). The context is automatically
+ * reinitialized.
+ *
+ * @param cc the SHA-512 context
+ * @param dst the destination buffer
+ */
+void sph_sha512_close(void *cc, void *dst);
+
+/**
+ * Add a few additional bits (0 to 7) to the current computation, then
+ * terminate it and output the result in the provided buffer, which must
+ * be wide enough to accomodate the result (64 bytes). If bit number i
+ * in <code>ub</code> has value 2^i, then the extra bits are those
+ * numbered 7 downto 8-n (this is the big-endian convention at the byte
+ * level). The context is automatically reinitialized.
+ *
+ * @param cc the SHA-512 context
+ * @param ub the extra bits
+ * @param n the number of extra bits (0 to 7)
+ * @param dst the destination buffer
+ */
+void sph_sha512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst);
+
+#ifdef DOXYGEN_IGNORE
+/**
+ * Apply the SHA-512 compression function. This function is identical to
+ * <code>sph_sha384_comp()</code>.
+ *
+ * @param msg the message block (16 values)
+ * @param val the function 512-bit input and output
+ */
+void sph_sha512_comp(const sph_u64 msg[16], sph_u64 val[8]);
+#endif
+
+#ifndef DOXYGEN_IGNORE
+#define sph_sha512_comp sph_sha384_comp
+#endif
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libqpdf/sph/sph_types.h b/libqpdf/sph/sph_types.h
new file mode 100644
index 0000000..7295b0b
--- /dev/null
+++ b/libqpdf/sph/sph_types.h
@@ -0,0 +1,1976 @@
+/* $Id: sph_types.h 260 2011-07-21 01:02:38Z tp $ */
+/**
+ * Basic type definitions.
+ *
+ * This header file defines the generic integer types that will be used
+ * for the implementation of hash functions; it also contains helper
+ * functions which encode and decode multi-byte integer values, using
+ * either little-endian or big-endian conventions.
+ *
+ * This file contains a compile-time test on the size of a byte
+ * (the <code>unsigned char</code> C type). If bytes are not octets,
+ * i.e. if they do not have a size of exactly 8 bits, then compilation
+ * is aborted. Architectures where bytes are not octets are relatively
+ * rare, even in the embedded devices market. We forbid non-octet bytes
+ * because there is no clear convention on how octet streams are encoded
+ * on such systems.
+ *
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010 Projet RNRT SAPHIR
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ *
+ * @file sph_types.h
+ * @author Thomas Pornin <thomas.pornin@cryptolog.com>
+ */
+
+#ifndef SPH_TYPES_H__
+#define SPH_TYPES_H__
+
+#include <limits.h>
+
+/*
+ * All our I/O functions are defined over octet streams. We do not know
+ * how to handle input data if bytes are not octets.
+ */
+#if CHAR_BIT != 8
+#error This code requires 8-bit bytes
+#endif
+
+/* ============= BEGIN documentation block for Doxygen ============ */
+
+#ifdef DOXYGEN_IGNORE
+
+/** @mainpage sphlib C code documentation
+ *
+ * @section overview Overview
+ *
+ * <code>sphlib</code> is a library which contains implementations of
+ * various cryptographic hash functions. These pages have been generated
+ * with <a href="http://www.doxygen.org/index.html">doxygen</a> and
+ * document the API for the C implementations.
+ *
+ * The API is described in appropriate header files, which are available
+ * in the "Files" section. Each hash function family has its own header,
+ * whose name begins with <code>"sph_"</code> and contains the family
+ * name. For instance, the API for the RIPEMD hash functions is available
+ * in the header file <code>sph_ripemd.h</code>.
+ *
+ * @section principles API structure and conventions
+ *
+ * @subsection io Input/output conventions
+ *
+ * In all generality, hash functions operate over strings of bits.
+ * Individual bits are rarely encountered in C programming or actual
+ * communication protocols; most protocols converge on the ubiquitous
+ * "octet" which is a group of eight bits. Data is thus expressed as a
+ * stream of octets. The C programming language contains the notion of a
+ * "byte", which is a data unit managed under the type <code>"unsigned
+ * char"</code>. The C standard prescribes that a byte should hold at
+ * least eight bits, but possibly more. Most modern architectures, even
+ * in the embedded world, feature eight-bit bytes, i.e. map bytes to
+ * octets.
+ *
+ * Nevertheless, for some of the implemented hash functions, an extra
+ * API has been added, which allows the input of arbitrary sequences of
+ * bits: when the computation is about to be closed, 1 to 7 extra bits
+ * can be added. The functions for which this API is implemented include
+ * the SHA-2 functions and all SHA-3 candidates.
+ *
+ * <code>sphlib</code> defines hash function which may hash octet streams,
+ * i.e. streams of bits where the number of bits is a multiple of eight.
+ * The data input functions in the <code>sphlib</code> API expect data
+ * as anonymous pointers (<code>"const void *"</code>) with a length
+ * (of type <code>"size_t"</code>) which gives the input data chunk length
+ * in bytes. A byte is assumed to be an octet; the <code>sph_types.h</code>
+ * header contains a compile-time test which prevents compilation on
+ * architectures where this property is not met.
+ *
+ * The hash function output is also converted into bytes. All currently
+ * implemented hash functions have an output width which is a multiple of
+ * eight, and this is likely to remain true for new designs.
+ *
+ * Most hash functions internally convert input data into 32-bit of 64-bit
+ * words, using either little-endian or big-endian conversion. The hash
+ * output also often consists of such words, which are encoded into output
+ * bytes with a similar endianness convention. Some hash functions have
+ * been only loosely specified on that subject; when necessary,
+ * <code>sphlib</code> has been tested against published "reference"
+ * implementations in order to use the same conventions.
+ *
+ * @subsection shortname Function short name
+ *
+ * Each implemented hash function has a "short name" which is used
+ * internally to derive the identifiers for the functions and context
+ * structures which the function uses. For instance, MD5 has the short
+ * name <code>"md5"</code>. Short names are listed in the next section,
+ * for the implemented hash functions. In subsequent sections, the
+ * short name will be assumed to be <code>"XXX"</code>: replace with the
+ * actual hash function name to get the C identifier.
+ *
+ * Note: some functions within the same family share the same core
+ * elements, such as update function or context structure. Correspondingly,
+ * some of the defined types or functions may actually be macros which
+ * transparently evaluate to another type or function name.
+ *
+ * @subsection context Context structure
+ *
+ * Each implemented hash fonction has its own context structure, available
+ * under the type name <code>"sph_XXX_context"</code> for the hash function
+ * with short name <code>"XXX"</code>. This structure holds all needed
+ * state for a running hash computation.
+ *
+ * The contents of these structures are meant to be opaque, and private
+ * to the implementation. However, these contents are specified in the
+ * header files so that application code which uses <code>sphlib</code>
+ * may access the size of those structures.
+ *
+ * The caller is responsible for allocating the context structure,
+ * whether by dynamic allocation (<code>malloc()</code> or equivalent),
+ * static allocation (a global permanent variable), as an automatic
+ * variable ("on the stack"), or by any other mean which ensures proper
+ * structure alignment. <code>sphlib</code> code performs no dynamic
+ * allocation by itself.
+ *
+ * The context must be initialized before use, using the
+ * <code>sph_XXX_init()</code> function. This function sets the context
+ * state to proper initial values for hashing.
+ *
+ * Since all state data is contained within the context structure,
+ * <code>sphlib</code> is thread-safe and reentrant: several hash
+ * computations may be performed in parallel, provided that they do not
+ * operate on the same context. Moreover, a running computation can be
+ * cloned by copying the context (with a simple <code>memcpy()</code>):
+ * the context and its clone are then independant and may be updated
+ * with new data and/or closed without interfering with each other.
+ * Similarly, a context structure can be moved in memory at will:
+ * context structures contain no pointer, in particular no pointer to
+ * themselves.
+ *
+ * @subsection dataio Data input
+ *
+ * Hashed data is input with the <code>sph_XXX()</code> fonction, which
+ * takes as parameters a pointer to the context, a pointer to the data
+ * to hash, and the number of data bytes to hash. The context is updated
+ * with the new data.
+ *
+ * Data can be input in one or several calls, with arbitrary input lengths.
+ * However, it is best, performance wise, to input data by relatively big
+ * chunks (say a few kilobytes), because this allows <code>sphlib</code> to
+ * optimize things and avoid internal copying.
+ *
+ * When all data has been input, the context can be closed with
+ * <code>sph_XXX_close()</code>. The hash output is computed and written
+ * into the provided buffer. The caller must take care to provide a
+ * buffer of appropriate length; e.g., when using SHA-1, the output is
+ * a 20-byte word, therefore the output buffer must be at least 20-byte
+ * long.
+ *
+ * For some hash functions, the <code>sph_XXX_addbits_and_close()</code>
+ * function can be used instead of <code>sph_XXX_close()</code>. This
+ * function can take a few extra <strong>bits</strong> to be added at
+ * the end of the input message. This allows hashing messages with a
+ * bit length which is not a multiple of 8. The extra bits are provided
+ * as an unsigned integer value, and a bit count. The bit count must be
+ * between 0 and 7, inclusive. The extra bits are provided as bits 7 to
+ * 0 (bits of numerical value 128, 64, 32... downto 0), in that order.
+ * For instance, to add three bits of value 1, 1 and 0, the unsigned
+ * integer will have value 192 (1*128 + 1*64 + 0*32) and the bit count
+ * will be 3.
+ *
+ * The <code>SPH_SIZE_XXX</code> macro is defined for each hash function;
+ * it evaluates to the function output size, expressed in bits. For instance,
+ * <code>SPH_SIZE_sha1</code> evaluates to <code>160</code>.
+ *
+ * When closed, the context is automatically reinitialized and can be
+ * immediately used for another computation. It is not necessary to call
+ * <code>sph_XXX_init()</code> after a close. Note that
+ * <code>sph_XXX_init()</code> can still be called to "reset" a context,
+ * i.e. forget previously input data, and get back to the initial state.
+ *
+ * @subsection alignment Data alignment
+ *
+ * "Alignment" is a property of data, which is said to be "properly
+ * aligned" when its emplacement in memory is such that the data can
+ * be optimally read by full words. This depends on the type of access;
+ * basically, some hash functions will read data by 32-bit or 64-bit
+ * words. <code>sphlib</code> does not mandate such alignment for input
+ * data, but using aligned data can substantially improve performance.
+ *
+ * As a rule, it is best to input data by chunks whose length (in bytes)
+ * is a multiple of eight, and which begins at "generally aligned"
+ * addresses, such as the base address returned by a call to
+ * <code>malloc()</code>.
+ *
+ * @section functions Implemented functions
+ *
+ * We give here the list of implemented functions. They are grouped by
+ * family; to each family corresponds a specific header file. Each
+ * individual function has its associated "short name". Please refer to
+ * the documentation for that header file to get details on the hash
+ * function denomination and provenance.
+ *
+ * Note: the functions marked with a '(64)' in the list below are
+ * available only if the C compiler provides an integer type of length
+ * 64 bits or more. Such a type is mandatory in the latest C standard
+ * (ISO 9899:1999, aka "C99") and is present in several older compilers
+ * as well, so chances are that such a type is available.
+ *
+ * - HAVAL family: file <code>sph_haval.h</code>
+ * - HAVAL-128/3 (128-bit, 3 passes): short name: <code>haval128_3</code>
+ * - HAVAL-128/4 (128-bit, 4 passes): short name: <code>haval128_4</code>
+ * - HAVAL-128/5 (128-bit, 5 passes): short name: <code>haval128_5</code>
+ * - HAVAL-160/3 (160-bit, 3 passes): short name: <code>haval160_3</code>
+ * - HAVAL-160/4 (160-bit, 4 passes): short name: <code>haval160_4</code>
+ * - HAVAL-160/5 (160-bit, 5 passes): short name: <code>haval160_5</code>
+ * - HAVAL-192/3 (192-bit, 3 passes): short name: <code>haval192_3</code>
+ * - HAVAL-192/4 (192-bit, 4 passes): short name: <code>haval192_4</code>
+ * - HAVAL-192/5 (192-bit, 5 passes): short name: <code>haval192_5</code>
+ * - HAVAL-224/3 (224-bit, 3 passes): short name: <code>haval224_3</code>
+ * - HAVAL-224/4 (224-bit, 4 passes): short name: <code>haval224_4</code>
+ * - HAVAL-224/5 (224-bit, 5 passes): short name: <code>haval224_5</code>
+ * - HAVAL-256/3 (256-bit, 3 passes): short name: <code>haval256_3</code>
+ * - HAVAL-256/4 (256-bit, 4 passes): short name: <code>haval256_4</code>
+ * - HAVAL-256/5 (256-bit, 5 passes): short name: <code>haval256_5</code>
+ * - MD2: file <code>sph_md2.h</code>, short name: <code>md2</code>
+ * - MD4: file <code>sph_md4.h</code>, short name: <code>md4</code>
+ * - MD5: file <code>sph_md5.h</code>, short name: <code>md5</code>
+ * - PANAMA: file <code>sph_panama.h</code>, short name: <code>panama</code>
+ * - RadioGatun family: file <code>sph_radiogatun.h</code>
+ * - RadioGatun[32]: short name: <code>radiogatun32</code>
+ * - RadioGatun[64]: short name: <code>radiogatun64</code> (64)
+ * - RIPEMD family: file <code>sph_ripemd.h</code>
+ * - RIPEMD: short name: <code>ripemd</code>
+ * - RIPEMD-128: short name: <code>ripemd128</code>
+ * - RIPEMD-160: short name: <code>ripemd160</code>
+ * - SHA-0: file <code>sph_sha0.h</code>, short name: <code>sha0</code>
+ * - SHA-1: file <code>sph_sha1.h</code>, short name: <code>sha1</code>
+ * - SHA-2 family, 32-bit hashes: file <code>sph_sha2.h</code>
+ * - SHA-224: short name: <code>sha224</code>
+ * - SHA-256: short name: <code>sha256</code>
+ * - SHA-384: short name: <code>sha384</code> (64)
+ * - SHA-512: short name: <code>sha512</code> (64)
+ * - Tiger family: file <code>sph_tiger.h</code>
+ * - Tiger: short name: <code>tiger</code> (64)
+ * - Tiger2: short name: <code>tiger2</code> (64)
+ * - WHIRLPOOL family: file <code>sph_whirlpool.h</code>
+ * - WHIRLPOOL-0: short name: <code>whirlpool0</code> (64)
+ * - WHIRLPOOL-1: short name: <code>whirlpool1</code> (64)
+ * - WHIRLPOOL: short name: <code>whirlpool</code> (64)
+ *
+ * The fourteen second-round SHA-3 candidates are also implemented;
+ * when applicable, the implementations follow the "final" specifications
+ * as published for the third round of the SHA-3 competition (BLAKE,
+ * Groestl, JH, Keccak and Skein have been tweaked for third round).
+ *
+ * - BLAKE family: file <code>sph_blake.h</code>
+ * - BLAKE-224: short name: <code>blake224</code>
+ * - BLAKE-256: short name: <code>blake256</code>
+ * - BLAKE-384: short name: <code>blake384</code>
+ * - BLAKE-512: short name: <code>blake512</code>
+ * - BMW (Blue Midnight Wish) family: file <code>sph_bmw.h</code>
+ * - BMW-224: short name: <code>bmw224</code>
+ * - BMW-256: short name: <code>bmw256</code>
+ * - BMW-384: short name: <code>bmw384</code> (64)
+ * - BMW-512: short name: <code>bmw512</code> (64)
+ * - CubeHash family: file <code>sph_cubehash.h</code> (specified as
+ * CubeHash16/32 in the CubeHash specification)
+ * - CubeHash-224: short name: <code>cubehash224</code>
+ * - CubeHash-256: short name: <code>cubehash256</code>
+ * - CubeHash-384: short name: <code>cubehash384</code>
+ * - CubeHash-512: short name: <code>cubehash512</code>
+ * - ECHO family: file <code>sph_echo.h</code>
+ * - ECHO-224: short name: <code>echo224</code>
+ * - ECHO-256: short name: <code>echo256</code>
+ * - ECHO-384: short name: <code>echo384</code>
+ * - ECHO-512: short name: <code>echo512</code>
+ * - Fugue family: file <code>sph_fugue.h</code>
+ * - Fugue-224: short name: <code>fugue224</code>
+ * - Fugue-256: short name: <code>fugue256</code>
+ * - Fugue-384: short name: <code>fugue384</code>
+ * - Fugue-512: short name: <code>fugue512</code>
+ * - Groestl family: file <code>sph_groestl.h</code>
+ * - Groestl-224: short name: <code>groestl224</code>
+ * - Groestl-256: short name: <code>groestl256</code>
+ * - Groestl-384: short name: <code>groestl384</code>
+ * - Groestl-512: short name: <code>groestl512</code>
+ * - Hamsi family: file <code>sph_hamsi.h</code>
+ * - Hamsi-224: short name: <code>hamsi224</code>
+ * - Hamsi-256: short name: <code>hamsi256</code>
+ * - Hamsi-384: short name: <code>hamsi384</code>
+ * - Hamsi-512: short name: <code>hamsi512</code>
+ * - JH family: file <code>sph_jh.h</code>
+ * - JH-224: short name: <code>jh224</code>
+ * - JH-256: short name: <code>jh256</code>
+ * - JH-384: short name: <code>jh384</code>
+ * - JH-512: short name: <code>jh512</code>
+ * - Keccak family: file <code>sph_keccak.h</code>
+ * - Keccak-224: short name: <code>keccak224</code>
+ * - Keccak-256: short name: <code>keccak256</code>
+ * - Keccak-384: short name: <code>keccak384</code>
+ * - Keccak-512: short name: <code>keccak512</code>
+ * - Luffa family: file <code>sph_luffa.h</code>
+ * - Luffa-224: short name: <code>luffa224</code>
+ * - Luffa-256: short name: <code>luffa256</code>
+ * - Luffa-384: short name: <code>luffa384</code>
+ * - Luffa-512: short name: <code>luffa512</code>
+ * - Shabal family: file <code>sph_shabal.h</code>
+ * - Shabal-192: short name: <code>shabal192</code>
+ * - Shabal-224: short name: <code>shabal224</code>
+ * - Shabal-256: short name: <code>shabal256</code>
+ * - Shabal-384: short name: <code>shabal384</code>
+ * - Shabal-512: short name: <code>shabal512</code>
+ * - SHAvite-3 family: file <code>sph_shavite.h</code>
+ * - SHAvite-224 (nominally "SHAvite-3 with 224-bit output"):
+ * short name: <code>shabal224</code>
+ * - SHAvite-256 (nominally "SHAvite-3 with 256-bit output"):
+ * short name: <code>shabal256</code>
+ * - SHAvite-384 (nominally "SHAvite-3 with 384-bit output"):
+ * short name: <code>shabal384</code>
+ * - SHAvite-512 (nominally "SHAvite-3 with 512-bit output"):
+ * short name: <code>shabal512</code>
+ * - SIMD family: file <code>sph_simd.h</code>
+ * - SIMD-224: short name: <code>simd224</code>
+ * - SIMD-256: short name: <code>simd256</code>
+ * - SIMD-384: short name: <code>simd384</code>
+ * - SIMD-512: short name: <code>simd512</code>
+ * - Skein family: file <code>sph_skein.h</code>
+ * - Skein-224 (nominally specified as Skein-512-224): short name:
+ * <code>skein224</code> (64)
+ * - Skein-256 (nominally specified as Skein-512-256): short name:
+ * <code>skein256</code> (64)
+ * - Skein-384 (nominally specified as Skein-512-384): short name:
+ * <code>skein384</code> (64)
+ * - Skein-512 (nominally specified as Skein-512-512): short name:
+ * <code>skein512</code> (64)
+ *
+ * For the second-round SHA-3 candidates, the functions are as specified
+ * for round 2, i.e. with the "tweaks" that some candidates added
+ * between round 1 and round 2. Also, some of the submitted packages for
+ * round 2 contained errors, in the specification, reference code, or
+ * both. <code>sphlib</code> implements the corrected versions.
+ */
+
+/** @hideinitializer
+ * Unsigned integer type whose length is at least 32 bits; on most
+ * architectures, it will have a width of exactly 32 bits. Unsigned C
+ * types implement arithmetics modulo a power of 2; use the
+ * <code>SPH_T32()</code> macro to ensure that the value is truncated
+ * to exactly 32 bits. Unless otherwise specified, all macros and
+ * functions which accept <code>sph_u32</code> values assume that these
+ * values fit on 32 bits, i.e. do not exceed 2^32-1, even on architectures
+ * where <code>sph_u32</code> is larger than that.
+ */
+typedef __arch_dependant__ sph_u32;
+
+/** @hideinitializer
+ * Signed integer type corresponding to <code>sph_u32</code>; it has
+ * width 32 bits or more.
+ */
+typedef __arch_dependant__ sph_s32;
+
+/** @hideinitializer
+ * Unsigned integer type whose length is at least 64 bits; on most
+ * architectures which feature such a type, it will have a width of
+ * exactly 64 bits. C99-compliant platform will have this type; it
+ * is also defined when the GNU compiler (gcc) is used, and on
+ * platforms where <code>unsigned long</code> is large enough. If this
+ * type is not available, then some hash functions which depends on
+ * a 64-bit type will not be available (most notably SHA-384, SHA-512,
+ * Tiger and WHIRLPOOL).
+ */
+typedef __arch_dependant__ sph_u64;
+
+/** @hideinitializer
+ * Signed integer type corresponding to <code>sph_u64</code>; it has
+ * width 64 bits or more.
+ */
+typedef __arch_dependant__ sph_s64;
+
+/**
+ * This macro expands the token <code>x</code> into a suitable
+ * constant expression of type <code>sph_u32</code>. Depending on
+ * how this type is defined, a suffix such as <code>UL</code> may
+ * be appended to the argument.
+ *
+ * @param x the token to expand into a suitable constant expression
+ */
+#define SPH_C32(x)
+
+/**
+ * Truncate a 32-bit value to exactly 32 bits. On most systems, this is
+ * a no-op, recognized as such by the compiler.
+ *
+ * @param x the value to truncate (of type <code>sph_u32</code>)
+ */
+#define SPH_T32(x)
+
+/**
+ * Rotate a 32-bit value by a number of bits to the left. The rotate
+ * count must reside between 1 and 31. This macro assumes that its
+ * first argument fits in 32 bits (no extra bit allowed on machines where
+ * <code>sph_u32</code> is wider); both arguments may be evaluated
+ * several times.
+ *
+ * @param x the value to rotate (of type <code>sph_u32</code>)
+ * @param n the rotation count (between 1 and 31, inclusive)
+ */
+#define SPH_ROTL32(x, n)
+
+/**
+ * Rotate a 32-bit value by a number of bits to the left. The rotate
+ * count must reside between 1 and 31. This macro assumes that its
+ * first argument fits in 32 bits (no extra bit allowed on machines where
+ * <code>sph_u32</code> is wider); both arguments may be evaluated
+ * several times.
+ *
+ * @param x the value to rotate (of type <code>sph_u32</code>)
+ * @param n the rotation count (between 1 and 31, inclusive)
+ */
+#define SPH_ROTR32(x, n)
+
+/**
+ * This macro is defined on systems for which a 64-bit type has been
+ * detected, and is used for <code>sph_u64</code>.
+ */
+#define SPH_64
+
+/**
+ * This macro is defined on systems for the "native" integer size is
+ * 64 bits (64-bit values fit in one register).
+ */
+#define SPH_64_TRUE
+
+/**
+ * This macro expands the token <code>x</code> into a suitable
+ * constant expression of type <code>sph_u64</code>. Depending on
+ * how this type is defined, a suffix such as <code>ULL</code> may
+ * be appended to the argument. This macro is defined only if a
+ * 64-bit type was detected and used for <code>sph_u64</code>.
+ *
+ * @param x the token to expand into a suitable constant expression
+ */
+#define SPH_C64(x)
+
+/**
+ * Truncate a 64-bit value to exactly 64 bits. On most systems, this is
+ * a no-op, recognized as such by the compiler. This macro is defined only
+ * if a 64-bit type was detected and used for <code>sph_u64</code>.
+ *
+ * @param x the value to truncate (of type <code>sph_u64</code>)
+ */
+#define SPH_T64(x)
+
+/**
+ * Rotate a 64-bit value by a number of bits to the left. The rotate
+ * count must reside between 1 and 63. This macro assumes that its
+ * first argument fits in 64 bits (no extra bit allowed on machines where
+ * <code>sph_u64</code> is wider); both arguments may be evaluated
+ * several times. This macro is defined only if a 64-bit type was detected
+ * and used for <code>sph_u64</code>.
+ *
+ * @param x the value to rotate (of type <code>sph_u64</code>)
+ * @param n the rotation count (between 1 and 63, inclusive)
+ */
+#define SPH_ROTL64(x, n)
+
+/**
+ * Rotate a 64-bit value by a number of bits to the left. The rotate
+ * count must reside between 1 and 63. This macro assumes that its
+ * first argument fits in 64 bits (no extra bit allowed on machines where
+ * <code>sph_u64</code> is wider); both arguments may be evaluated
+ * several times. This macro is defined only if a 64-bit type was detected
+ * and used for <code>sph_u64</code>.
+ *
+ * @param x the value to rotate (of type <code>sph_u64</code>)
+ * @param n the rotation count (between 1 and 63, inclusive)
+ */
+#define SPH_ROTR64(x, n)
+
+/**
+ * This macro evaluates to <code>inline</code> or an equivalent construction,
+ * if available on the compilation platform, or to nothing otherwise. This
+ * is used to declare inline functions, for which the compiler should
+ * endeavour to include the code directly in the caller. Inline functions
+ * are typically defined in header files as replacement for macros.
+ */
+#define SPH_INLINE
+
+/**
+ * This macro is defined if the platform has been detected as using
+ * little-endian convention. This implies that the <code>sph_u32</code>
+ * type (and the <code>sph_u64</code> type also, if it is defined) has
+ * an exact width (i.e. exactly 32-bit, respectively 64-bit).
+ */
+#define SPH_LITTLE_ENDIAN
+
+/**
+ * This macro is defined if the platform has been detected as using
+ * big-endian convention. This implies that the <code>sph_u32</code>
+ * type (and the <code>sph_u64</code> type also, if it is defined) has
+ * an exact width (i.e. exactly 32-bit, respectively 64-bit).
+ */
+#define SPH_BIG_ENDIAN
+
+/**
+ * This macro is defined if 32-bit words (and 64-bit words, if defined)
+ * can be read from and written to memory efficiently in little-endian
+ * convention. This is the case for little-endian platforms, and also
+ * for the big-endian platforms which have special little-endian access
+ * opcodes (e.g. Ultrasparc).
+ */
+#define SPH_LITTLE_FAST
+
+/**
+ * This macro is defined if 32-bit words (and 64-bit words, if defined)
+ * can be read from and written to memory efficiently in big-endian
+ * convention. This is the case for little-endian platforms, and also
+ * for the little-endian platforms which have special big-endian access
+ * opcodes.
+ */
+#define SPH_BIG_FAST
+
+/**
+ * On some platforms, this macro is defined to an unsigned integer type
+ * into which pointer values may be cast. The resulting value can then
+ * be tested for being a multiple of 2, 4 or 8, indicating an aligned
+ * pointer for, respectively, 16-bit, 32-bit or 64-bit memory accesses.
+ */
+#define SPH_UPTR
+
+/**
+ * When defined, this macro indicates that unaligned memory accesses
+ * are possible with only a minor penalty, and thus should be prefered
+ * over strategies which first copy data to an aligned buffer.
+ */
+#define SPH_UNALIGNED
+
+/**
+ * Byte-swap a 32-bit word (i.e. <code>0x12345678</code> becomes
+ * <code>0x78563412</code>). This is an inline function which resorts
+ * to inline assembly on some platforms, for better performance.
+ *
+ * @param x the 32-bit value to byte-swap
+ * @return the byte-swapped value
+ */
+static inline sph_u32 sph_bswap32(sph_u32 x);
+
+/**
+ * Byte-swap a 64-bit word. This is an inline function which resorts
+ * to inline assembly on some platforms, for better performance. This
+ * function is defined only if a suitable 64-bit type was found for
+ * <code>sph_u64</code>
+ *
+ * @param x the 64-bit value to byte-swap
+ * @return the byte-swapped value
+ */
+static inline sph_u64 sph_bswap64(sph_u64 x);
+
+/**
+ * Decode a 16-bit unsigned value from memory, in little-endian convention
+ * (least significant byte comes first).
+ *
+ * @param src the source address
+ * @return the decoded value
+ */
+static inline unsigned sph_dec16le(const void *src);
+
+/**
+ * Encode a 16-bit unsigned value into memory, in little-endian convention
+ * (least significant byte comes first).
+ *
+ * @param dst the destination buffer
+ * @param val the value to encode
+ */
+static inline void sph_enc16le(void *dst, unsigned val);
+
+/**
+ * Decode a 16-bit unsigned value from memory, in big-endian convention
+ * (most significant byte comes first).
+ *
+ * @param src the source address
+ * @return the decoded value
+ */
+static inline unsigned sph_dec16be(const void *src);
+
+/**
+ * Encode a 16-bit unsigned value into memory, in big-endian convention
+ * (most significant byte comes first).
+ *
+ * @param dst the destination buffer
+ * @param val the value to encode
+ */
+static inline void sph_enc16be(void *dst, unsigned val);
+
+/**
+ * Decode a 32-bit unsigned value from memory, in little-endian convention
+ * (least significant byte comes first).
+ *
+ * @param src the source address
+ * @return the decoded value
+ */
+static inline sph_u32 sph_dec32le(const void *src);
+
+/**
+ * Decode a 32-bit unsigned value from memory, in little-endian convention
+ * (least significant byte comes first). This function assumes that the
+ * source address is suitably aligned for a direct access, if the platform
+ * supports such things; it can thus be marginally faster than the generic
+ * <code>sph_dec32le()</code> function.
+ *
+ * @param src the source address
+ * @return the decoded value
+ */
+static inline sph_u32 sph_dec32le_aligned(const void *src);
+
+/**
+ * Encode a 32-bit unsigned value into memory, in little-endian convention
+ * (least significant byte comes first).
+ *
+ * @param dst the destination buffer
+ * @param val the value to encode
+ */
+static inline void sph_enc32le(void *dst, sph_u32 val);
+
+/**
+ * Encode a 32-bit unsigned value into memory, in little-endian convention
+ * (least significant byte comes first). This function assumes that the
+ * destination address is suitably aligned for a direct access, if the
+ * platform supports such things; it can thus be marginally faster than
+ * the generic <code>sph_enc32le()</code> function.
+ *
+ * @param dst the destination buffer
+ * @param val the value to encode
+ */
+static inline void sph_enc32le_aligned(void *dst, sph_u32 val);
+
+/**
+ * Decode a 32-bit unsigned value from memory, in big-endian convention
+ * (most significant byte comes first).
+ *
+ * @param src the source address
+ * @return the decoded value
+ */
+static inline sph_u32 sph_dec32be(const void *src);
+
+/**
+ * Decode a 32-bit unsigned value from memory, in big-endian convention
+ * (most significant byte comes first). This function assumes that the
+ * source address is suitably aligned for a direct access, if the platform
+ * supports such things; it can thus be marginally faster than the generic
+ * <code>sph_dec32be()</code> function.
+ *
+ * @param src the source address
+ * @return the decoded value
+ */
+static inline sph_u32 sph_dec32be_aligned(const void *src);
+
+/**
+ * Encode a 32-bit unsigned value into memory, in big-endian convention
+ * (most significant byte comes first).
+ *
+ * @param dst the destination buffer
+ * @param val the value to encode
+ */
+static inline void sph_enc32be(void *dst, sph_u32 val);
+
+/**
+ * Encode a 32-bit unsigned value into memory, in big-endian convention
+ * (most significant byte comes first). This function assumes that the
+ * destination address is suitably aligned for a direct access, if the
+ * platform supports such things; it can thus be marginally faster than
+ * the generic <code>sph_enc32be()</code> function.
+ *
+ * @param dst the destination buffer
+ * @param val the value to encode
+ */
+static inline void sph_enc32be_aligned(void *dst, sph_u32 val);
+
+/**
+ * Decode a 64-bit unsigned value from memory, in little-endian convention
+ * (least significant byte comes first). This function is defined only
+ * if a suitable 64-bit type was detected and used for <code>sph_u64</code>.
+ *
+ * @param src the source address
+ * @return the decoded value
+ */
+static inline sph_u64 sph_dec64le(const void *src);
+
+/**
+ * Decode a 64-bit unsigned value from memory, in little-endian convention
+ * (least significant byte comes first). This function assumes that the
+ * source address is suitably aligned for a direct access, if the platform
+ * supports such things; it can thus be marginally faster than the generic
+ * <code>sph_dec64le()</code> function. This function is defined only
+ * if a suitable 64-bit type was detected and used for <code>sph_u64</code>.
+ *
+ * @param src the source address
+ * @return the decoded value
+ */
+static inline sph_u64 sph_dec64le_aligned(const void *src);
+
+/**
+ * Encode a 64-bit unsigned value into memory, in little-endian convention
+ * (least significant byte comes first). This function is defined only
+ * if a suitable 64-bit type was detected and used for <code>sph_u64</code>.
+ *
+ * @param dst the destination buffer
+ * @param val the value to encode
+ */
+static inline void sph_enc64le(void *dst, sph_u64 val);
+
+/**
+ * Encode a 64-bit unsigned value into memory, in little-endian convention
+ * (least significant byte comes first). This function assumes that the
+ * destination address is suitably aligned for a direct access, if the
+ * platform supports such things; it can thus be marginally faster than
+ * the generic <code>sph_enc64le()</code> function. This function is defined
+ * only if a suitable 64-bit type was detected and used for
+ * <code>sph_u64</code>.
+ *
+ * @param dst the destination buffer
+ * @param val the value to encode
+ */
+static inline void sph_enc64le_aligned(void *dst, sph_u64 val);
+
+/**
+ * Decode a 64-bit unsigned value from memory, in big-endian convention
+ * (most significant byte comes first). This function is defined only
+ * if a suitable 64-bit type was detected and used for <code>sph_u64</code>.
+ *
+ * @param src the source address
+ * @return the decoded value
+ */
+static inline sph_u64 sph_dec64be(const void *src);
+
+/**
+ * Decode a 64-bit unsigned value from memory, in big-endian convention
+ * (most significant byte comes first). This function assumes that the
+ * source address is suitably aligned for a direct access, if the platform
+ * supports such things; it can thus be marginally faster than the generic
+ * <code>sph_dec64be()</code> function. This function is defined only
+ * if a suitable 64-bit type was detected and used for <code>sph_u64</code>.
+ *
+ * @param src the source address
+ * @return the decoded value
+ */
+static inline sph_u64 sph_dec64be_aligned(const void *src);
+
+/**
+ * Encode a 64-bit unsigned value into memory, in big-endian convention
+ * (most significant byte comes first). This function is defined only
+ * if a suitable 64-bit type was detected and used for <code>sph_u64</code>.
+ *
+ * @param dst the destination buffer
+ * @param val the value to encode
+ */
+static inline void sph_enc64be(void *dst, sph_u64 val);
+
+/**
+ * Encode a 64-bit unsigned value into memory, in big-endian convention
+ * (most significant byte comes first). This function assumes that the
+ * destination address is suitably aligned for a direct access, if the
+ * platform supports such things; it can thus be marginally faster than
+ * the generic <code>sph_enc64be()</code> function. This function is defined
+ * only if a suitable 64-bit type was detected and used for
+ * <code>sph_u64</code>.
+ *
+ * @param dst the destination buffer
+ * @param val the value to encode
+ */
+static inline void sph_enc64be_aligned(void *dst, sph_u64 val);
+
+#endif
+
+/* ============== END documentation block for Doxygen ============= */
+
+#ifndef DOXYGEN_IGNORE
+
+/*
+ * We want to define the types "sph_u32" and "sph_u64" which hold
+ * unsigned values of at least, respectively, 32 and 64 bits. These
+ * tests should select appropriate types for most platforms. The
+ * macro "SPH_64" is defined if the 64-bit is supported.
+ */
+
+#undef SPH_64
+#undef SPH_64_TRUE
+
+#if defined __STDC__ && __STDC_VERSION__ >= 199901L
+
+/*
+ * On C99 implementations, we can use <stdint.h> to get an exact 64-bit
+ * type, if any, or otherwise use a wider type (which must exist, for
+ * C99 conformance).
+ */
+
+#include <stdint.h>
+
+#ifdef UINT32_MAX
+typedef uint32_t sph_u32;
+typedef int32_t sph_s32;
+#else
+typedef uint_fast32_t sph_u32;
+typedef int_fast32_t sph_s32;
+#endif
+#if !SPH_NO_64
+#ifdef UINT64_MAX
+typedef uint64_t sph_u64;
+typedef int64_t sph_s64;
+#else
+typedef uint_fast64_t sph_u64;
+typedef int_fast64_t sph_s64;
+#endif
+#endif
+
+#define SPH_C32(x) ((sph_u32)(x))
+#if !SPH_NO_64
+#define SPH_C64(x) ((sph_u64)(x))
+#define SPH_64 1
+#endif
+
+#else
+
+/*
+ * On non-C99 systems, we use "unsigned int" if it is wide enough,
+ * "unsigned long" otherwise. This supports all "reasonable" architectures.
+ * We have to be cautious: pre-C99 preprocessors handle constants
+ * differently in '#if' expressions. Hence the shifts to test UINT_MAX.
+ */
+
+#if ((UINT_MAX >> 11) >> 11) >= 0x3FF
+
+typedef unsigned int sph_u32;
+typedef int sph_s32;
+
+#define SPH_C32(x) ((sph_u32)(x ## U))
+
+#else
+
+typedef unsigned long sph_u32;
+typedef long sph_s32;
+
+#define SPH_C32(x) ((sph_u32)(x ## UL))
+
+#endif
+
+#if !SPH_NO_64
+
+/*
+ * We want a 64-bit type. We use "unsigned long" if it is wide enough (as
+ * is common on 64-bit architectures such as AMD64, Alpha or Sparcv9),
+ * "unsigned long long" otherwise, if available. We use ULLONG_MAX to
+ * test whether "unsigned long long" is available; we also know that
+ * gcc features this type, even if the libc header do not know it.
+ */
+
+#if ((ULONG_MAX >> 31) >> 31) >= 3
+
+typedef unsigned long sph_u64;
+typedef long sph_s64;
+
+#define SPH_C64(x) ((sph_u64)(x ## UL))
+
+#define SPH_64 1
+
+#elif ((ULLONG_MAX >> 31) >> 31) >= 3 || defined __GNUC__
+
+typedef unsigned long long sph_u64;
+typedef long long sph_s64;
+
+#define SPH_C64(x) ((sph_u64)(x ## ULL))
+
+#define SPH_64 1
+
+#else
+
+/*
+ * No 64-bit type...
+ */
+
+#endif
+
+#endif
+
+#endif
+
+/*
+ * If the "unsigned long" type has length 64 bits or more, then this is
+ * a "true" 64-bit architectures. This is also true with Visual C on
+ * amd64, even though the "long" type is limited to 32 bits.
+ */
+#if SPH_64 && (((ULONG_MAX >> 31) >> 31) >= 3 || defined _M_X64)
+#define SPH_64_TRUE 1
+#endif
+
+/*
+ * Implementation note: some processors have specific opcodes to perform
+ * a rotation. Recent versions of gcc recognize the expression above and
+ * use the relevant opcodes, when appropriate.
+ */
+
+#define SPH_T32(x) ((x) & SPH_C32(0xFFFFFFFF))
+#define SPH_ROTL32(x, n) SPH_T32(((x) << (n)) | ((x) >> (32 - (n))))
+#define SPH_ROTR32(x, n) SPH_ROTL32(x, (32 - (n)))
+
+#if SPH_64
+
+#define SPH_T64(x) ((x) & SPH_C64(0xFFFFFFFFFFFFFFFF))
+#define SPH_ROTL64(x, n) SPH_T64(((x) << (n)) | ((x) >> (64 - (n))))
+#define SPH_ROTR64(x, n) SPH_ROTL64(x, (64 - (n)))
+
+#endif
+
+#ifndef DOXYGEN_IGNORE
+/*
+ * Define SPH_INLINE to be an "inline" qualifier, if available. We define
+ * some small macro-like functions which benefit greatly from being inlined.
+ */
+#if (defined __STDC__ && __STDC_VERSION__ >= 199901L) || defined __GNUC__
+#define SPH_INLINE inline
+#elif defined _MSC_VER
+#define SPH_INLINE __inline
+#else
+#define SPH_INLINE
+#endif
+#endif
+
+/*
+ * We define some macros which qualify the architecture. These macros
+ * may be explicit set externally (e.g. as compiler parameters). The
+ * code below sets those macros if they are not already defined.
+ *
+ * Most macros are boolean, thus evaluate to either zero or non-zero.
+ * The SPH_UPTR macro is special, in that it evaluates to a C type,
+ * or is not defined.
+ *
+ * SPH_UPTR if defined: unsigned type to cast pointers into
+ *
+ * SPH_UNALIGNED non-zero if unaligned accesses are efficient
+ * SPH_LITTLE_ENDIAN non-zero if architecture is known to be little-endian
+ * SPH_BIG_ENDIAN non-zero if architecture is known to be big-endian
+ * SPH_LITTLE_FAST non-zero if little-endian decoding is fast
+ * SPH_BIG_FAST non-zero if big-endian decoding is fast
+ *
+ * If SPH_UPTR is defined, then encoding and decoding of 32-bit and 64-bit
+ * values will try to be "smart". Either SPH_LITTLE_ENDIAN or SPH_BIG_ENDIAN
+ * _must_ be non-zero in those situations. The 32-bit and 64-bit types
+ * _must_ also have an exact width.
+ *
+ * SPH_SPARCV9_GCC_32 UltraSPARC-compatible with gcc, 32-bit mode
+ * SPH_SPARCV9_GCC_64 UltraSPARC-compatible with gcc, 64-bit mode
+ * SPH_SPARCV9_GCC UltraSPARC-compatible with gcc
+ * SPH_I386_GCC x86-compatible (32-bit) with gcc
+ * SPH_I386_MSVC x86-compatible (32-bit) with Microsoft Visual C
+ * SPH_AMD64_GCC x86-compatible (64-bit) with gcc
+ * SPH_AMD64_MSVC x86-compatible (64-bit) with Microsoft Visual C
+ * SPH_PPC32_GCC PowerPC, 32-bit, with gcc
+ * SPH_PPC64_GCC PowerPC, 64-bit, with gcc
+ *
+ * TODO: enhance automatic detection, for more architectures and compilers.
+ * Endianness is the most important. SPH_UNALIGNED and SPH_UPTR help with
+ * some very fast functions (e.g. MD4) when using unaligned input data.
+ * The CPU-specific-with-GCC macros are useful only for inline assembly,
+ * normally restrained to this header file.
+ */
+
+/*
+ * 32-bit x86, aka "i386 compatible".
+ */
+#if defined __i386__ || defined _M_IX86
+
+#define SPH_DETECT_UNALIGNED 1
+#define SPH_DETECT_LITTLE_ENDIAN 1
+#define SPH_DETECT_UPTR sph_u32
+#ifdef __GNUC__
+#define SPH_DETECT_I386_GCC 1
+#endif
+#ifdef _MSC_VER
+#define SPH_DETECT_I386_MSVC 1
+#endif
+
+/*
+ * 64-bit x86, hereafter known as "amd64".
+ */
+#elif defined __x86_64 || defined _M_X64
+
+#define SPH_DETECT_UNALIGNED 1
+#define SPH_DETECT_LITTLE_ENDIAN 1
+#define SPH_DETECT_UPTR sph_u64
+#ifdef __GNUC__
+#define SPH_DETECT_AMD64_GCC 1
+#endif
+#ifdef _MSC_VER
+#define SPH_DETECT_AMD64_MSVC 1
+#endif
+
+/*
+ * 64-bit Sparc architecture (implies v9).
+ */
+#elif ((defined __sparc__ || defined __sparc) && defined __arch64__) \
+ || defined __sparcv9
+
+#define SPH_DETECT_BIG_ENDIAN 1
+#define SPH_DETECT_UPTR sph_u64
+#ifdef __GNUC__
+#define SPH_DETECT_SPARCV9_GCC_64 1
+#define SPH_DETECT_LITTLE_FAST 1
+#endif
+
+/*
+ * 32-bit Sparc.
+ */
+#elif (defined __sparc__ || defined __sparc) \
+ && !(defined __sparcv9 || defined __arch64__)
+
+#define SPH_DETECT_BIG_ENDIAN 1
+#define SPH_DETECT_UPTR sph_u32
+#if defined __GNUC__ && defined __sparc_v9__
+#define SPH_DETECT_SPARCV9_GCC_32 1
+#define SPH_DETECT_LITTLE_FAST 1
+#endif
+
+/*
+ * ARM, little-endian.
+ */
+#elif defined __arm__ && __ARMEL__
+
+#define SPH_DETECT_LITTLE_ENDIAN 1
+
+/*
+ * MIPS, little-endian.
+ */
+#elif MIPSEL || _MIPSEL || __MIPSEL || __MIPSEL__
+
+#define SPH_DETECT_LITTLE_ENDIAN 1
+
+/*
+ * MIPS, big-endian.
+ */
+#elif MIPSEB || _MIPSEB || __MIPSEB || __MIPSEB__
+
+#define SPH_DETECT_BIG_ENDIAN 1
+
+/*
+ * PowerPC.
+ */
+#elif defined __powerpc__ || defined __POWERPC__ || defined __ppc__ \
+ || defined _ARCH_PPC
+
+/*
+ * Note: we do not declare cross-endian access to be "fast": even if
+ * using inline assembly, implementation should still assume that
+ * keeping the decoded word in a temporary is faster than decoding
+ * it again.
+ */
+#if defined __GNUC__
+#if SPH_64_TRUE
+#define SPH_DETECT_PPC64_GCC 1
+#else
+#define SPH_DETECT_PPC32_GCC 1
+#endif
+#endif
+
+#if defined __BIG_ENDIAN__ || defined _BIG_ENDIAN
+#define SPH_DETECT_BIG_ENDIAN 1
+#elif defined __LITTLE_ENDIAN__ || defined _LITTLE_ENDIAN
+#define SPH_DETECT_LITTLE_ENDIAN 1
+#endif
+
+/*
+ * Itanium, 64-bit.
+ */
+#elif defined __ia64 || defined __ia64__ \
+ || defined __itanium__ || defined _M_IA64
+
+#if defined __BIG_ENDIAN__ || defined _BIG_ENDIAN
+#define SPH_DETECT_BIG_ENDIAN 1
+#else
+#define SPH_DETECT_LITTLE_ENDIAN 1
+#endif
+#if defined __LP64__ || defined _LP64
+#define SPH_DETECT_UPTR sph_u64
+#else
+#define SPH_DETECT_UPTR sph_u32
+#endif
+
+#endif
+
+#if defined SPH_DETECT_SPARCV9_GCC_32 || defined SPH_DETECT_SPARCV9_GCC_64
+#define SPH_DETECT_SPARCV9_GCC 1
+#endif
+
+#if defined SPH_DETECT_UNALIGNED && !defined SPH_UNALIGNED
+#define SPH_UNALIGNED SPH_DETECT_UNALIGNED
+#endif
+#if defined SPH_DETECT_UPTR && !defined SPH_UPTR
+#define SPH_UPTR SPH_DETECT_UPTR
+#endif
+#if defined SPH_DETECT_LITTLE_ENDIAN && !defined SPH_LITTLE_ENDIAN
+#define SPH_LITTLE_ENDIAN SPH_DETECT_LITTLE_ENDIAN
+#endif
+#if defined SPH_DETECT_BIG_ENDIAN && !defined SPH_BIG_ENDIAN
+#define SPH_BIG_ENDIAN SPH_DETECT_BIG_ENDIAN
+#endif
+#if defined SPH_DETECT_LITTLE_FAST && !defined SPH_LITTLE_FAST
+#define SPH_LITTLE_FAST SPH_DETECT_LITTLE_FAST
+#endif
+#if defined SPH_DETECT_BIG_FAST && !defined SPH_BIG_FAST
+#define SPH_BIG_FAST SPH_DETECT_BIG_FAST
+#endif
+#if defined SPH_DETECT_SPARCV9_GCC_32 && !defined SPH_SPARCV9_GCC_32
+#define SPH_SPARCV9_GCC_32 SPH_DETECT_SPARCV9_GCC_32
+#endif
+#if defined SPH_DETECT_SPARCV9_GCC_64 && !defined SPH_SPARCV9_GCC_64
+#define SPH_SPARCV9_GCC_64 SPH_DETECT_SPARCV9_GCC_64
+#endif
+#if defined SPH_DETECT_SPARCV9_GCC && !defined SPH_SPARCV9_GCC
+#define SPH_SPARCV9_GCC SPH_DETECT_SPARCV9_GCC
+#endif
+#if defined SPH_DETECT_I386_GCC && !defined SPH_I386_GCC
+#define SPH_I386_GCC SPH_DETECT_I386_GCC
+#endif
+#if defined SPH_DETECT_I386_MSVC && !defined SPH_I386_MSVC
+#define SPH_I386_MSVC SPH_DETECT_I386_MSVC
+#endif
+#if defined SPH_DETECT_AMD64_GCC && !defined SPH_AMD64_GCC
+#define SPH_AMD64_GCC SPH_DETECT_AMD64_GCC
+#endif
+#if defined SPH_DETECT_AMD64_MSVC && !defined SPH_AMD64_MSVC
+#define SPH_AMD64_MSVC SPH_DETECT_AMD64_MSVC
+#endif
+#if defined SPH_DETECT_PPC32_GCC && !defined SPH_PPC32_GCC
+#define SPH_PPC32_GCC SPH_DETECT_PPC32_GCC
+#endif
+#if defined SPH_DETECT_PPC64_GCC && !defined SPH_PPC64_GCC
+#define SPH_PPC64_GCC SPH_DETECT_PPC64_GCC
+#endif
+
+#if SPH_LITTLE_ENDIAN && !defined SPH_LITTLE_FAST
+#define SPH_LITTLE_FAST 1
+#endif
+#if SPH_BIG_ENDIAN && !defined SPH_BIG_FAST
+#define SPH_BIG_FAST 1
+#endif
+
+#if defined SPH_UPTR && !(SPH_LITTLE_ENDIAN || SPH_BIG_ENDIAN)
+#error SPH_UPTR defined, but endianness is not known.
+#endif
+
+#if SPH_I386_GCC && !SPH_NO_ASM
+
+/*
+ * On x86 32-bit, with gcc, we use the bswapl opcode to byte-swap 32-bit
+ * values.
+ */
+
+static SPH_INLINE sph_u32
+sph_bswap32(sph_u32 x)
+{
+ __asm__ __volatile__ ("bswapl %0" : "=r" (x) : "0" (x));
+ return x;
+}
+
+#if SPH_64
+
+static SPH_INLINE sph_u64
+sph_bswap64(sph_u64 x)
+{
+ return ((sph_u64)sph_bswap32((sph_u32)x) << 32)
+ | (sph_u64)sph_bswap32((sph_u32)(x >> 32));
+}
+
+#endif
+
+#elif SPH_AMD64_GCC && !SPH_NO_ASM
+
+/*
+ * On x86 64-bit, with gcc, we use the bswapl opcode to byte-swap 32-bit
+ * and 64-bit values.
+ */
+
+static SPH_INLINE sph_u32
+sph_bswap32(sph_u32 x)
+{
+ __asm__ __volatile__ ("bswapl %0" : "=r" (x) : "0" (x));
+ return x;
+}
+
+#if SPH_64
+
+static SPH_INLINE sph_u64
+sph_bswap64(sph_u64 x)
+{
+ __asm__ __volatile__ ("bswapq %0" : "=r" (x) : "0" (x));
+ return x;
+}
+
+#endif
+
+/*
+ * Disabled code. Apparently, Microsoft Visual C 2005 is smart enough
+ * to generate proper opcodes for endianness swapping with the pure C
+ * implementation below.
+ *
+
+#elif SPH_I386_MSVC && !SPH_NO_ASM
+
+static __inline sph_u32 __declspec(naked) __fastcall
+sph_bswap32(sph_u32 x)
+{
+ __asm {
+ bswap ecx
+ mov eax,ecx
+ ret
+ }
+}
+
+#if SPH_64
+
+static SPH_INLINE sph_u64
+sph_bswap64(sph_u64 x)
+{
+ return ((sph_u64)sph_bswap32((sph_u32)x) << 32)
+ | (sph_u64)sph_bswap32((sph_u32)(x >> 32));
+}
+
+#endif
+
+ *
+ * [end of disabled code]
+ */
+
+#else
+
+static SPH_INLINE sph_u32
+sph_bswap32(sph_u32 x)
+{
+ x = SPH_T32((x << 16) | (x >> 16));
+ x = ((x & SPH_C32(0xFF00FF00)) >> 8)
+ | ((x & SPH_C32(0x00FF00FF)) << 8);
+ return x;
+}
+
+#if SPH_64
+
+/**
+ * Byte-swap a 64-bit value.
+ *
+ * @param x the input value
+ * @return the byte-swapped value
+ */
+static SPH_INLINE sph_u64
+sph_bswap64(sph_u64 x)
+{
+ x = SPH_T64((x << 32) | (x >> 32));
+ x = ((x & SPH_C64(0xFFFF0000FFFF0000)) >> 16)
+ | ((x & SPH_C64(0x0000FFFF0000FFFF)) << 16);
+ x = ((x & SPH_C64(0xFF00FF00FF00FF00)) >> 8)
+ | ((x & SPH_C64(0x00FF00FF00FF00FF)) << 8);
+ return x;
+}
+
+#endif
+
+#endif
+
+#if SPH_SPARCV9_GCC && !SPH_NO_ASM
+
+/*
+ * On UltraSPARC systems, native ordering is big-endian, but it is
+ * possible to perform little-endian read accesses by specifying the
+ * address space 0x88 (ASI_PRIMARY_LITTLE). Basically, either we use
+ * the opcode "lda [%reg]0x88,%dst", where %reg is the register which
+ * contains the source address and %dst is the destination register,
+ * or we use "lda [%reg+imm]%asi,%dst", which uses the %asi register
+ * to get the address space name. The latter format is better since it
+ * combines an addition and the actual access in a single opcode; but
+ * it requires the setting (and subsequent resetting) of %asi, which is
+ * slow. Some operations (i.e. MD5 compression function) combine many
+ * successive little-endian read accesses, which may share the same
+ * %asi setting. The macros below contain the appropriate inline
+ * assembly.
+ */
+
+#define SPH_SPARCV9_SET_ASI \
+ sph_u32 sph_sparcv9_asi; \
+ __asm__ __volatile__ ( \
+ "rd %%asi,%0\n\twr %%g0,0x88,%%asi" : "=r" (sph_sparcv9_asi));
+
+#define SPH_SPARCV9_RESET_ASI \
+ __asm__ __volatile__ ("wr %%g0,%0,%%asi" : : "r" (sph_sparcv9_asi));
+
+#define SPH_SPARCV9_DEC32LE(base, idx) ({ \
+ sph_u32 sph_sparcv9_tmp; \
+ __asm__ __volatile__ ("lda [%1+" #idx "*4]%%asi,%0" \
+ : "=r" (sph_sparcv9_tmp) : "r" (base)); \
+ sph_sparcv9_tmp; \
+ })
+
+#endif
+
+static SPH_INLINE void
+sph_enc16be(void *dst, unsigned val)
+{
+ ((unsigned char *)dst)[0] = (val >> 8);
+ ((unsigned char *)dst)[1] = val;
+}
+
+static SPH_INLINE unsigned
+sph_dec16be(const void *src)
+{
+ return ((unsigned)(((const unsigned char *)src)[0]) << 8)
+ | (unsigned)(((const unsigned char *)src)[1]);
+}
+
+static SPH_INLINE void
+sph_enc16le(void *dst, unsigned val)
+{
+ ((unsigned char *)dst)[0] = val;
+ ((unsigned char *)dst)[1] = val >> 8;
+}
+
+static SPH_INLINE unsigned
+sph_dec16le(const void *src)
+{
+ return (unsigned)(((const unsigned char *)src)[0])
+ | ((unsigned)(((const unsigned char *)src)[1]) << 8);
+}
+
+/**
+ * Encode a 32-bit value into the provided buffer (big endian convention).
+ *
+ * @param dst the destination buffer
+ * @param val the 32-bit value to encode
+ */
+static SPH_INLINE void
+sph_enc32be(void *dst, sph_u32 val)
+{
+#if defined SPH_UPTR
+#if SPH_UNALIGNED
+#if SPH_LITTLE_ENDIAN
+ val = sph_bswap32(val);
+#endif
+ *(sph_u32 *)dst = val;
+#else
+ if (((SPH_UPTR)dst & 3) == 0) {
+#if SPH_LITTLE_ENDIAN
+ val = sph_bswap32(val);
+#endif
+ *(sph_u32 *)dst = val;
+ } else {
+ ((unsigned char *)dst)[0] = (val >> 24);
+ ((unsigned char *)dst)[1] = (val >> 16);
+ ((unsigned char *)dst)[2] = (val >> 8);
+ ((unsigned char *)dst)[3] = val;
+ }
+#endif
+#else
+ ((unsigned char *)dst)[0] = (val >> 24);
+ ((unsigned char *)dst)[1] = (val >> 16);
+ ((unsigned char *)dst)[2] = (val >> 8);
+ ((unsigned char *)dst)[3] = val;
+#endif
+}
+
+/**
+ * Encode a 32-bit value into the provided buffer (big endian convention).
+ * The destination buffer must be properly aligned.
+ *
+ * @param dst the destination buffer (32-bit aligned)
+ * @param val the value to encode
+ */
+static SPH_INLINE void
+sph_enc32be_aligned(void *dst, sph_u32 val)
+{
+#if SPH_LITTLE_ENDIAN
+ *(sph_u32 *)dst = sph_bswap32(val);
+#elif SPH_BIG_ENDIAN
+ *(sph_u32 *)dst = val;
+#else
+ ((unsigned char *)dst)[0] = (val >> 24);
+ ((unsigned char *)dst)[1] = (val >> 16);
+ ((unsigned char *)dst)[2] = (val >> 8);
+ ((unsigned char *)dst)[3] = val;
+#endif
+}
+
+/**
+ * Decode a 32-bit value from the provided buffer (big endian convention).
+ *
+ * @param src the source buffer
+ * @return the decoded value
+ */
+static SPH_INLINE sph_u32
+sph_dec32be(const void *src)
+{
+#if defined SPH_UPTR
+#if SPH_UNALIGNED
+#if SPH_LITTLE_ENDIAN
+ return sph_bswap32(*(const sph_u32 *)src);
+#else
+ return *(const sph_u32 *)src;
+#endif
+#else
+ if (((SPH_UPTR)src & 3) == 0) {
+#if SPH_LITTLE_ENDIAN
+ return sph_bswap32(*(const sph_u32 *)src);
+#else
+ return *(const sph_u32 *)src;
+#endif
+ } else {
+ return ((sph_u32)(((const unsigned char *)src)[0]) << 24)
+ | ((sph_u32)(((const unsigned char *)src)[1]) << 16)
+ | ((sph_u32)(((const unsigned char *)src)[2]) << 8)
+ | (sph_u32)(((const unsigned char *)src)[3]);
+ }
+#endif
+#else
+ return ((sph_u32)(((const unsigned char *)src)[0]) << 24)
+ | ((sph_u32)(((const unsigned char *)src)[1]) << 16)
+ | ((sph_u32)(((const unsigned char *)src)[2]) << 8)
+ | (sph_u32)(((const unsigned char *)src)[3]);
+#endif
+}
+
+/**
+ * Decode a 32-bit value from the provided buffer (big endian convention).
+ * The source buffer must be properly aligned.
+ *
+ * @param src the source buffer (32-bit aligned)
+ * @return the decoded value
+ */
+static SPH_INLINE sph_u32
+sph_dec32be_aligned(const void *src)
+{
+#if SPH_LITTLE_ENDIAN
+ return sph_bswap32(*(const sph_u32 *)src);
+#elif SPH_BIG_ENDIAN
+ return *(const sph_u32 *)src;
+#else
+ return ((sph_u32)(((const unsigned char *)src)[0]) << 24)
+ | ((sph_u32)(((const unsigned char *)src)[1]) << 16)
+ | ((sph_u32)(((const unsigned char *)src)[2]) << 8)
+ | (sph_u32)(((const unsigned char *)src)[3]);
+#endif
+}
+
+/**
+ * Encode a 32-bit value into the provided buffer (little endian convention).
+ *
+ * @param dst the destination buffer
+ * @param val the 32-bit value to encode
+ */
+static SPH_INLINE void
+sph_enc32le(void *dst, sph_u32 val)
+{
+#if defined SPH_UPTR
+#if SPH_UNALIGNED
+#if SPH_BIG_ENDIAN
+ val = sph_bswap32(val);
+#endif
+ *(sph_u32 *)dst = val;
+#else
+ if (((SPH_UPTR)dst & 3) == 0) {
+#if SPH_BIG_ENDIAN
+ val = sph_bswap32(val);
+#endif
+ *(sph_u32 *)dst = val;
+ } else {
+ ((unsigned char *)dst)[0] = val;
+ ((unsigned char *)dst)[1] = (val >> 8);
+ ((unsigned char *)dst)[2] = (val >> 16);
+ ((unsigned char *)dst)[3] = (val >> 24);
+ }
+#endif
+#else
+ ((unsigned char *)dst)[0] = val;
+ ((unsigned char *)dst)[1] = (val >> 8);
+ ((unsigned char *)dst)[2] = (val >> 16);
+ ((unsigned char *)dst)[3] = (val >> 24);
+#endif
+}
+
+/**
+ * Encode a 32-bit value into the provided buffer (little endian convention).
+ * The destination buffer must be properly aligned.
+ *
+ * @param dst the destination buffer (32-bit aligned)
+ * @param val the value to encode
+ */
+static SPH_INLINE void
+sph_enc32le_aligned(void *dst, sph_u32 val)
+{
+#if SPH_LITTLE_ENDIAN
+ *(sph_u32 *)dst = val;
+#elif SPH_BIG_ENDIAN
+ *(sph_u32 *)dst = sph_bswap32(val);
+#else
+ ((unsigned char *)dst)[0] = val;
+ ((unsigned char *)dst)[1] = (val >> 8);
+ ((unsigned char *)dst)[2] = (val >> 16);
+ ((unsigned char *)dst)[3] = (val >> 24);
+#endif
+}
+
+/**
+ * Decode a 32-bit value from the provided buffer (little endian convention).
+ *
+ * @param src the source buffer
+ * @return the decoded value
+ */
+static SPH_INLINE sph_u32
+sph_dec32le(const void *src)
+{
+#if defined SPH_UPTR
+#if SPH_UNALIGNED
+#if SPH_BIG_ENDIAN
+ return sph_bswap32(*(const sph_u32 *)src);
+#else
+ return *(const sph_u32 *)src;
+#endif
+#else
+ if (((SPH_UPTR)src & 3) == 0) {
+#if SPH_BIG_ENDIAN
+#if SPH_SPARCV9_GCC && !SPH_NO_ASM
+ sph_u32 tmp;
+
+ /*
+ * "__volatile__" is needed here because without it,
+ * gcc-3.4.3 miscompiles the code and performs the
+ * access before the test on the address, thus triggering
+ * a bus error...
+ */
+ __asm__ __volatile__ (
+ "lda [%1]0x88,%0" : "=r" (tmp) : "r" (src));
+ return tmp;
+/*
+ * On PowerPC, this turns out not to be worth the effort: the inline
+ * assembly makes GCC optimizer uncomfortable, which tends to nullify
+ * the decoding gains.
+ *
+ * For most hash functions, using this inline assembly trick changes
+ * hashing speed by less than 5% and often _reduces_ it. The biggest
+ * gains are for MD4 (+11%) and CubeHash (+30%). For all others, it is
+ * less then 10%. The speed gain on CubeHash is probably due to the
+ * chronic shortage of registers that CubeHash endures; for the other
+ * functions, the generic code appears to be efficient enough already.
+ *
+#elif (SPH_PPC32_GCC || SPH_PPC64_GCC) && !SPH_NO_ASM
+ sph_u32 tmp;
+
+ __asm__ __volatile__ (
+ "lwbrx %0,0,%1" : "=r" (tmp) : "r" (src));
+ return tmp;
+ */
+#else
+ return sph_bswap32(*(const sph_u32 *)src);
+#endif
+#else
+ return *(const sph_u32 *)src;
+#endif
+ } else {
+ return (sph_u32)(((const unsigned char *)src)[0])
+ | ((sph_u32)(((const unsigned char *)src)[1]) << 8)
+ | ((sph_u32)(((const unsigned char *)src)[2]) << 16)
+ | ((sph_u32)(((const unsigned char *)src)[3]) << 24);
+ }
+#endif
+#else
+ return (sph_u32)(((const unsigned char *)src)[0])
+ | ((sph_u32)(((const unsigned char *)src)[1]) << 8)
+ | ((sph_u32)(((const unsigned char *)src)[2]) << 16)
+ | ((sph_u32)(((const unsigned char *)src)[3]) << 24);
+#endif
+}
+
+/**
+ * Decode a 32-bit value from the provided buffer (little endian convention).
+ * The source buffer must be properly aligned.
+ *
+ * @param src the source buffer (32-bit aligned)
+ * @return the decoded value
+ */
+static SPH_INLINE sph_u32
+sph_dec32le_aligned(const void *src)
+{
+#if SPH_LITTLE_ENDIAN
+ return *(const sph_u32 *)src;
+#elif SPH_BIG_ENDIAN
+#if SPH_SPARCV9_GCC && !SPH_NO_ASM
+ sph_u32 tmp;
+
+ __asm__ __volatile__ ("lda [%1]0x88,%0" : "=r" (tmp) : "r" (src));
+ return tmp;
+/*
+ * Not worth it generally.
+ *
+#elif (SPH_PPC32_GCC || SPH_PPC64_GCC) && !SPH_NO_ASM
+ sph_u32 tmp;
+
+ __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (tmp) : "r" (src));
+ return tmp;
+ */
+#else
+ return sph_bswap32(*(const sph_u32 *)src);
+#endif
+#else
+ return (sph_u32)(((const unsigned char *)src)[0])
+ | ((sph_u32)(((const unsigned char *)src)[1]) << 8)
+ | ((sph_u32)(((const unsigned char *)src)[2]) << 16)
+ | ((sph_u32)(((const unsigned char *)src)[3]) << 24);
+#endif
+}
+
+#if SPH_64
+
+/**
+ * Encode a 64-bit value into the provided buffer (big endian convention).
+ *
+ * @param dst the destination buffer
+ * @param val the 64-bit value to encode
+ */
+static SPH_INLINE void
+sph_enc64be(void *dst, sph_u64 val)
+{
+#if defined SPH_UPTR
+#if SPH_UNALIGNED
+#if SPH_LITTLE_ENDIAN
+ val = sph_bswap64(val);
+#endif
+ *(sph_u64 *)dst = val;
+#else
+ if (((SPH_UPTR)dst & 7) == 0) {
+#if SPH_LITTLE_ENDIAN
+ val = sph_bswap64(val);
+#endif
+ *(sph_u64 *)dst = val;
+ } else {
+ ((unsigned char *)dst)[0] = (val >> 56);
+ ((unsigned char *)dst)[1] = (val >> 48);
+ ((unsigned char *)dst)[2] = (val >> 40);
+ ((unsigned char *)dst)[3] = (val >> 32);
+ ((unsigned char *)dst)[4] = (val >> 24);
+ ((unsigned char *)dst)[5] = (val >> 16);
+ ((unsigned char *)dst)[6] = (val >> 8);
+ ((unsigned char *)dst)[7] = val;
+ }
+#endif
+#else
+ ((unsigned char *)dst)[0] = (val >> 56);
+ ((unsigned char *)dst)[1] = (val >> 48);
+ ((unsigned char *)dst)[2] = (val >> 40);
+ ((unsigned char *)dst)[3] = (val >> 32);
+ ((unsigned char *)dst)[4] = (val >> 24);
+ ((unsigned char *)dst)[5] = (val >> 16);
+ ((unsigned char *)dst)[6] = (val >> 8);
+ ((unsigned char *)dst)[7] = val;
+#endif
+}
+
+/**
+ * Encode a 64-bit value into the provided buffer (big endian convention).
+ * The destination buffer must be properly aligned.
+ *
+ * @param dst the destination buffer (64-bit aligned)
+ * @param val the value to encode
+ */
+static SPH_INLINE void
+sph_enc64be_aligned(void *dst, sph_u64 val)
+{
+#if SPH_LITTLE_ENDIAN
+ *(sph_u64 *)dst = sph_bswap64(val);
+#elif SPH_BIG_ENDIAN
+ *(sph_u64 *)dst = val;
+#else
+ ((unsigned char *)dst)[0] = (val >> 56);
+ ((unsigned char *)dst)[1] = (val >> 48);
+ ((unsigned char *)dst)[2] = (val >> 40);
+ ((unsigned char *)dst)[3] = (val >> 32);
+ ((unsigned char *)dst)[4] = (val >> 24);
+ ((unsigned char *)dst)[5] = (val >> 16);
+ ((unsigned char *)dst)[6] = (val >> 8);
+ ((unsigned char *)dst)[7] = val;
+#endif
+}
+
+/**
+ * Decode a 64-bit value from the provided buffer (big endian convention).
+ *
+ * @param src the source buffer
+ * @return the decoded value
+ */
+static SPH_INLINE sph_u64
+sph_dec64be(const void *src)
+{
+#if defined SPH_UPTR
+#if SPH_UNALIGNED
+#if SPH_LITTLE_ENDIAN
+ return sph_bswap64(*(const sph_u64 *)src);
+#else
+ return *(const sph_u64 *)src;
+#endif
+#else
+ if (((SPH_UPTR)src & 7) == 0) {
+#if SPH_LITTLE_ENDIAN
+ return sph_bswap64(*(const sph_u64 *)src);
+#else
+ return *(const sph_u64 *)src;
+#endif
+ } else {
+ return ((sph_u64)(((const unsigned char *)src)[0]) << 56)
+ | ((sph_u64)(((const unsigned char *)src)[1]) << 48)
+ | ((sph_u64)(((const unsigned char *)src)[2]) << 40)
+ | ((sph_u64)(((const unsigned char *)src)[3]) << 32)
+ | ((sph_u64)(((const unsigned char *)src)[4]) << 24)
+ | ((sph_u64)(((const unsigned char *)src)[5]) << 16)
+ | ((sph_u64)(((const unsigned char *)src)[6]) << 8)
+ | (sph_u64)(((const unsigned char *)src)[7]);
+ }
+#endif
+#else
+ return ((sph_u64)(((const unsigned char *)src)[0]) << 56)
+ | ((sph_u64)(((const unsigned char *)src)[1]) << 48)
+ | ((sph_u64)(((const unsigned char *)src)[2]) << 40)
+ | ((sph_u64)(((const unsigned char *)src)[3]) << 32)
+ | ((sph_u64)(((const unsigned char *)src)[4]) << 24)
+ | ((sph_u64)(((const unsigned char *)src)[5]) << 16)
+ | ((sph_u64)(((const unsigned char *)src)[6]) << 8)
+ | (sph_u64)(((const unsigned char *)src)[7]);
+#endif
+}
+
+/**
+ * Decode a 64-bit value from the provided buffer (big endian convention).
+ * The source buffer must be properly aligned.
+ *
+ * @param src the source buffer (64-bit aligned)
+ * @return the decoded value
+ */
+static SPH_INLINE sph_u64
+sph_dec64be_aligned(const void *src)
+{
+#if SPH_LITTLE_ENDIAN
+ return sph_bswap64(*(const sph_u64 *)src);
+#elif SPH_BIG_ENDIAN
+ return *(const sph_u64 *)src;
+#else
+ return ((sph_u64)(((const unsigned char *)src)[0]) << 56)
+ | ((sph_u64)(((const unsigned char *)src)[1]) << 48)
+ | ((sph_u64)(((const unsigned char *)src)[2]) << 40)
+ | ((sph_u64)(((const unsigned char *)src)[3]) << 32)
+ | ((sph_u64)(((const unsigned char *)src)[4]) << 24)
+ | ((sph_u64)(((const unsigned char *)src)[5]) << 16)
+ | ((sph_u64)(((const unsigned char *)src)[6]) << 8)
+ | (sph_u64)(((const unsigned char *)src)[7]);
+#endif
+}
+
+/**
+ * Encode a 64-bit value into the provided buffer (little endian convention).
+ *
+ * @param dst the destination buffer
+ * @param val the 64-bit value to encode
+ */
+static SPH_INLINE void
+sph_enc64le(void *dst, sph_u64 val)
+{
+#if defined SPH_UPTR
+#if SPH_UNALIGNED
+#if SPH_BIG_ENDIAN
+ val = sph_bswap64(val);
+#endif
+ *(sph_u64 *)dst = val;
+#else
+ if (((SPH_UPTR)dst & 7) == 0) {
+#if SPH_BIG_ENDIAN
+ val = sph_bswap64(val);
+#endif
+ *(sph_u64 *)dst = val;
+ } else {
+ ((unsigned char *)dst)[0] = val;
+ ((unsigned char *)dst)[1] = (val >> 8);
+ ((unsigned char *)dst)[2] = (val >> 16);
+ ((unsigned char *)dst)[3] = (val >> 24);
+ ((unsigned char *)dst)[4] = (val >> 32);
+ ((unsigned char *)dst)[5] = (val >> 40);
+ ((unsigned char *)dst)[6] = (val >> 48);
+ ((unsigned char *)dst)[7] = (val >> 56);
+ }
+#endif
+#else
+ ((unsigned char *)dst)[0] = val;
+ ((unsigned char *)dst)[1] = (val >> 8);
+ ((unsigned char *)dst)[2] = (val >> 16);
+ ((unsigned char *)dst)[3] = (val >> 24);
+ ((unsigned char *)dst)[4] = (val >> 32);
+ ((unsigned char *)dst)[5] = (val >> 40);
+ ((unsigned char *)dst)[6] = (val >> 48);
+ ((unsigned char *)dst)[7] = (val >> 56);
+#endif
+}
+
+/**
+ * Encode a 64-bit value into the provided buffer (little endian convention).
+ * The destination buffer must be properly aligned.
+ *
+ * @param dst the destination buffer (64-bit aligned)
+ * @param val the value to encode
+ */
+static SPH_INLINE void
+sph_enc64le_aligned(void *dst, sph_u64 val)
+{
+#if SPH_LITTLE_ENDIAN
+ *(sph_u64 *)dst = val;
+#elif SPH_BIG_ENDIAN
+ *(sph_u64 *)dst = sph_bswap64(val);
+#else
+ ((unsigned char *)dst)[0] = val;
+ ((unsigned char *)dst)[1] = (val >> 8);
+ ((unsigned char *)dst)[2] = (val >> 16);
+ ((unsigned char *)dst)[3] = (val >> 24);
+ ((unsigned char *)dst)[4] = (val >> 32);
+ ((unsigned char *)dst)[5] = (val >> 40);
+ ((unsigned char *)dst)[6] = (val >> 48);
+ ((unsigned char *)dst)[7] = (val >> 56);
+#endif
+}
+
+/**
+ * Decode a 64-bit value from the provided buffer (little endian convention).
+ *
+ * @param src the source buffer
+ * @return the decoded value
+ */
+static SPH_INLINE sph_u64
+sph_dec64le(const void *src)
+{
+#if defined SPH_UPTR
+#if SPH_UNALIGNED
+#if SPH_BIG_ENDIAN
+ return sph_bswap64(*(const sph_u64 *)src);
+#else
+ return *(const sph_u64 *)src;
+#endif
+#else
+ if (((SPH_UPTR)src & 7) == 0) {
+#if SPH_BIG_ENDIAN
+#if SPH_SPARCV9_GCC_64 && !SPH_NO_ASM
+ sph_u64 tmp;
+
+ __asm__ __volatile__ (
+ "ldxa [%1]0x88,%0" : "=r" (tmp) : "r" (src));
+ return tmp;
+/*
+ * Not worth it generally.
+ *
+#elif SPH_PPC32_GCC && !SPH_NO_ASM
+ return (sph_u64)sph_dec32le_aligned(src)
+ | ((sph_u64)sph_dec32le_aligned(
+ (const char *)src + 4) << 32);
+#elif SPH_PPC64_GCC && !SPH_NO_ASM
+ sph_u64 tmp;
+
+ __asm__ __volatile__ (
+ "ldbrx %0,0,%1" : "=r" (tmp) : "r" (src));
+ return tmp;
+ */
+#else
+ return sph_bswap64(*(const sph_u64 *)src);
+#endif
+#else
+ return *(const sph_u64 *)src;
+#endif
+ } else {
+ return (sph_u64)(((const unsigned char *)src)[0])
+ | ((sph_u64)(((const unsigned char *)src)[1]) << 8)
+ | ((sph_u64)(((const unsigned char *)src)[2]) << 16)
+ | ((sph_u64)(((const unsigned char *)src)[3]) << 24)
+ | ((sph_u64)(((const unsigned char *)src)[4]) << 32)
+ | ((sph_u64)(((const unsigned char *)src)[5]) << 40)
+ | ((sph_u64)(((const unsigned char *)src)[6]) << 48)
+ | ((sph_u64)(((const unsigned char *)src)[7]) << 56);
+ }
+#endif
+#else
+ return (sph_u64)(((const unsigned char *)src)[0])
+ | ((sph_u64)(((const unsigned char *)src)[1]) << 8)
+ | ((sph_u64)(((const unsigned char *)src)[2]) << 16)
+ | ((sph_u64)(((const unsigned char *)src)[3]) << 24)
+ | ((sph_u64)(((const unsigned char *)src)[4]) << 32)
+ | ((sph_u64)(((const unsigned char *)src)[5]) << 40)
+ | ((sph_u64)(((const unsigned char *)src)[6]) << 48)
+ | ((sph_u64)(((const unsigned char *)src)[7]) << 56);
+#endif
+}
+
+/**
+ * Decode a 64-bit value from the provided buffer (little endian convention).
+ * The source buffer must be properly aligned.
+ *
+ * @param src the source buffer (64-bit aligned)
+ * @return the decoded value
+ */
+static SPH_INLINE sph_u64
+sph_dec64le_aligned(const void *src)
+{
+#if SPH_LITTLE_ENDIAN
+ return *(const sph_u64 *)src;
+#elif SPH_BIG_ENDIAN
+#if SPH_SPARCV9_GCC_64 && !SPH_NO_ASM
+ sph_u64 tmp;
+
+ __asm__ __volatile__ ("ldxa [%1]0x88,%0" : "=r" (tmp) : "r" (src));
+ return tmp;
+/*
+ * Not worth it generally.
+ *
+#elif SPH_PPC32_GCC && !SPH_NO_ASM
+ return (sph_u64)sph_dec32le_aligned(src)
+ | ((sph_u64)sph_dec32le_aligned((const char *)src + 4) << 32);
+#elif SPH_PPC64_GCC && !SPH_NO_ASM
+ sph_u64 tmp;
+
+ __asm__ __volatile__ ("ldbrx %0,0,%1" : "=r" (tmp) : "r" (src));
+ return tmp;
+ */
+#else
+ return sph_bswap64(*(const sph_u64 *)src);
+#endif
+#else
+ return (sph_u64)(((const unsigned char *)src)[0])
+ | ((sph_u64)(((const unsigned char *)src)[1]) << 8)
+ | ((sph_u64)(((const unsigned char *)src)[2]) << 16)
+ | ((sph_u64)(((const unsigned char *)src)[3]) << 24)
+ | ((sph_u64)(((const unsigned char *)src)[4]) << 32)
+ | ((sph_u64)(((const unsigned char *)src)[5]) << 40)
+ | ((sph_u64)(((const unsigned char *)src)[6]) << 48)
+ | ((sph_u64)(((const unsigned char *)src)[7]) << 56);
+#endif
+}
+
+#endif
+
+#endif /* Doxygen excluded block */
+
+#endif