diff options
Diffstat (limited to 'libqpdf')
-rw-r--r-- | libqpdf/OffsetInputSource.cc | 61 | ||||
-rw-r--r-- | libqpdf/Pl_AES_PDF.cc | 112 | ||||
-rw-r--r-- | libqpdf/Pl_SHA2.cc | 164 | ||||
-rw-r--r-- | libqpdf/QPDF.cc | 145 | ||||
-rw-r--r-- | libqpdf/QPDFWriter.cc | 611 | ||||
-rw-r--r-- | libqpdf/QPDF_Stream.cc | 250 | ||||
-rw-r--r-- | libqpdf/QPDF_encryption.cc | 801 | ||||
-rw-r--r-- | libqpdf/QPDF_optimization.cc | 101 | ||||
-rw-r--r-- | libqpdf/QUtil.cc | 39 | ||||
-rw-r--r-- | libqpdf/build.mk | 19 | ||||
-rw-r--r-- | libqpdf/qpdf-c.cc | 48 | ||||
-rw-r--r-- | libqpdf/qpdf/OffsetInputSource.hh | 29 | ||||
-rw-r--r-- | libqpdf/qpdf/Pl_AES_PDF.hh | 28 | ||||
-rw-r--r-- | libqpdf/qpdf/Pl_SHA2.hh | 50 | ||||
-rw-r--r-- | libqpdf/qpdf/QPDF_Stream.hh | 3 | ||||
-rw-r--r-- | libqpdf/sha2.c | 690 | ||||
-rw-r--r-- | libqpdf/sha2big.c | 247 | ||||
-rw-r--r-- | libqpdf/sph/md_helper.c | 346 | ||||
-rw-r--r-- | libqpdf/sph/sph_sha2.h | 378 | ||||
-rw-r--r-- | libqpdf/sph/sph_types.h | 1976 |
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 |