SHA2: switch to pluggable crypto

This commit is contained in:
Jay Berkenbilt 2019-11-04 09:55:43 -05:00
parent eadc222ff9
commit bb427bd117
8 changed files with 233 additions and 136 deletions

View File

@ -23,7 +23,7 @@
#define QPDFCRYPTOIMPL_HH
#include <qpdf/DLL.h>
#include <cstring>
#include <string>
// This class is part of qpdf's pluggable crypto provider support.
// Most users won't need to know or care about this class, but you can
@ -41,6 +41,8 @@ class QPDF_DLL_CLASS QPDFCryptoImpl
QPDF_DLL
virtual ~QPDFCryptoImpl() = default;
// Hashing
typedef unsigned char MD5_Digest[16];
QPDF_DLL
virtual void MD5_init() = 0;
@ -51,6 +53,17 @@ class QPDF_DLL_CLASS QPDFCryptoImpl
QPDF_DLL
virtual void MD5_digest(MD5_Digest) = 0;
QPDF_DLL
virtual void SHA2_init(int bits) = 0;
QPDF_DLL
virtual void SHA2_update(unsigned char const* data, size_t len) = 0;
QPDF_DLL
virtual void SHA2_finalize() = 0;
QPDF_DLL
virtual std::string SHA2_digest() = 0;
// Encryption/Decryption
// key_len of -1 means treat key_data as a null-terminated string
QPDF_DLL
virtual void RC4_init(unsigned char const* key_data, int key_len = -1) = 0;

92
libqpdf/Pl_SHA2.cc Normal file
View File

@ -0,0 +1,92 @@
#include <qpdf/Pl_SHA2.hh>
#include <stdexcept>
#include <cstdio>
#include <qpdf/PointerHolder.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/QPDFCryptoProvider.hh>
Pl_SHA2::Pl_SHA2(int bits, Pipeline* next) :
Pipeline("sha2", next),
in_progress(false)
{
if (bits)
{
resetBits(bits);
}
}
Pl_SHA2::~Pl_SHA2()
{
}
void
Pl_SHA2::write(unsigned char* buf, size_t len)
{
if (! this->in_progress)
{
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);
this->crypto->SHA2_update(data, bytes);
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();
}
this->crypto->SHA2_finalize();
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");
}
this->crypto = QPDFCryptoProvider::getImpl();
this->crypto->SHA2_init(bits);
}
std::string
Pl_SHA2::getRawDigest()
{
if (this->in_progress)
{
throw std::logic_error(
"digest requested for in-progress SHA2 Pipeline");
}
return this->crypto->SHA2_digest();
}
std::string
Pl_SHA2::getHexDigest()
{
if (this->in_progress)
{
throw std::logic_error(
"digest requested for in-progress SHA2 Pipeline");
}
return QUtil::hex_encode(getRawDigest());
}

View File

@ -42,3 +42,27 @@ void
QPDFCrypto_native::RC4_finalize()
{
}
void
QPDFCrypto_native::SHA2_init(int bits)
{
this->sha2 = std::make_shared<SHA2_native>(bits);
}
void
QPDFCrypto_native::SHA2_update(unsigned char const* data, size_t len)
{
this->sha2->update(data, len);
}
void
QPDFCrypto_native::SHA2_finalize()
{
this->sha2->finalize();
}
std::string
QPDFCrypto_native::SHA2_digest()
{
return this->sha2->getRawDigest();
}

View File

@ -1,93 +1,59 @@
#include <qpdf/Pl_SHA2.hh>
#include <qpdf/SHA2_native.hh>
#include <stdexcept>
#include <cstdio>
#include <qpdf/PointerHolder.hh>
#include <qpdf/QUtil.hh>
Pl_SHA2::Pl_SHA2(int bits, Pipeline* next) :
Pipeline("sha2", next),
in_progress(false),
bits(0)
SHA2_native::SHA2_native(int bits) :
bits(bits)
{
if (bits)
switch (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);
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;
}
}
void
Pl_SHA2::finish()
SHA2_native::badBits()
{
if (this->getNext(true))
throw std::logic_error("SHA2_native has bits != 256, 384, or 512");
}
void
SHA2_native::update(unsigned char const* buf, size_t len)
{
switch (bits)
{
this->getNext()->finish();
case 256:
sph_sha256(&this->ctx256, buf, len);
break;
case 384:
sph_sha384(&this->ctx384, buf, len);
break;
case 512:
sph_sha512(&this->ctx512, buf, len);
break;
default:
badBits();
break;
}
}
void
SHA2_native::finalize()
{
switch (bits)
{
case 256:
@ -103,26 +69,10 @@ Pl_SHA2::finish()
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()
SHA2_native::getRawDigest()
{
std::string result;
switch (bits)
@ -145,14 +95,3 @@ Pl_SHA2::getRawDigest()
}
return result;
}
std::string
Pl_SHA2::getHexDigest()
{
if (this->in_progress)
{
throw std::logic_error(
"digest requested for in-progress SHA2 Pipeline");
}
return QUtil::hex_encode(getRawDigest());
}

View File

@ -79,6 +79,7 @@ SRCS_libqpdf = \
libqpdf/QUtil.cc \
libqpdf/RC4.cc \
libqpdf/RC4_native.cc \
libqpdf/SHA2_native.cc \
libqpdf/SecureRandomDataProvider.cc \
libqpdf/SparseOHArray.cc \
libqpdf/qpdf-c.cc \

43
libqpdf/qpdf/Pl_SHA2.hh Normal file
View File

@ -0,0 +1,43 @@
#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 <qpdf/QPDFCryptoImpl.hh>
#include <memory>
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:
bool in_progress;
std::shared_ptr<QPDFCryptoImpl> crypto;
};
#endif // PL_SHA2_HH

View File

@ -5,6 +5,7 @@
#include <qpdf/QPDFCryptoImpl.hh>
#include <qpdf/MD5_native.hh>
#include <qpdf/RC4_native.hh>
#include <qpdf/SHA2_native.hh>
#include <memory>
class QPDFCrypto_native: public QPDFCryptoImpl
@ -25,9 +26,15 @@ class QPDFCrypto_native: public QPDFCryptoImpl
unsigned char* out_data = 0);
virtual void RC4_finalize();
virtual void SHA2_init(int bits);
virtual void SHA2_update(unsigned char const* data, size_t len);
virtual void SHA2_finalize();
virtual std::string SHA2_digest();
private:
std::shared_ptr<MD5_native> md5;
std::shared_ptr<RC4_native> rc4;
std::shared_ptr<SHA2_native> sha2;
};
#endif // QPDFCRYPTO_NATIVE_HH

View File

@ -1,43 +1,21 @@
#ifndef PL_SHA2_HH
#define PL_SHA2_HH
#ifndef SHA2_NATIVE_HH
#define SHA2_NATIVE_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>
#include <string>
class Pl_SHA2: public Pipeline
class SHA2_native
{
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
SHA2_native(int bits);
~SHA2_native() = default;
void update(unsigned char const* const, size_t);
void finalize();
std::string getRawDigest();
private:
void badBits();
bool in_progress;
int bits;
sph_sha256_context ctx256;
sph_sha384_context ctx384;
@ -47,4 +25,4 @@ class Pl_SHA2: public Pipeline
unsigned char sha512sum[64];
};
#endif // PL_SHA2_HH
#endif // SHA2_NATIVE_HH