Commit 0873e4230047553c366dff11444d56fe9977b61f

Authored by Jay Berkenbilt
1 parent c9da66a0

SHA2 pipeline with support for 256, 384, and 512 bits

Implemented pipeline around sph sha calls using standard test vectors
for full-byte values.  Did not test or support partial byte values.
libqpdf/Pl_SHA2.cc 0 → 100644
  1 +#include <qpdf/Pl_SHA2.hh>
  2 +#include <stdexcept>
  3 +#include <cstdio>
  4 +#include <qpdf/PointerHolder.hh>
  5 +
  6 +Pl_SHA2::Pl_SHA2(int bits, Pipeline* next) :
  7 + Pipeline("sha2", next),
  8 + in_progress(false),
  9 + bits(0)
  10 +{
  11 + if (bits)
  12 + {
  13 + resetBits(bits);
  14 + }
  15 +}
  16 +
  17 +Pl_SHA2::~Pl_SHA2()
  18 +{
  19 +}
  20 +
  21 +void
  22 +Pl_SHA2::badBits()
  23 +{
  24 + throw std::logic_error("Pl_SHA2 has unexpected value for bits");
  25 +}
  26 +
  27 +void
  28 +Pl_SHA2::write(unsigned char* buf, size_t len)
  29 +{
  30 + if (! this->in_progress)
  31 + {
  32 + switch (bits)
  33 + {
  34 + case 256:
  35 + sph_sha256_init(&this->ctx256);
  36 + break;
  37 + case 384:
  38 + sph_sha384_init(&this->ctx384);
  39 + break;
  40 + case 512:
  41 + sph_sha512_init(&this->ctx512);
  42 + break;
  43 + default:
  44 + badBits();
  45 + break;
  46 + }
  47 + this->in_progress = true;
  48 + }
  49 +
  50 + // Write in chunks in case len is too big to fit in an int.
  51 + // Assume int is at least 32 bits.
  52 + static size_t const max_bytes = 1 << 30;
  53 + size_t bytes_left = len;
  54 + unsigned char* data = buf;
  55 + while (bytes_left > 0)
  56 + {
  57 + size_t bytes = (bytes_left >= max_bytes ? max_bytes : bytes_left);
  58 + switch (bits)
  59 + {
  60 + case 256:
  61 + sph_sha256(&this->ctx256, data, bytes);
  62 + break;
  63 + case 384:
  64 + sph_sha384(&this->ctx384, data, bytes);
  65 + break;
  66 + case 512:
  67 + sph_sha512(&this->ctx512, data, bytes);
  68 + break;
  69 + default:
  70 + badBits();
  71 + break;
  72 + }
  73 + bytes_left -= bytes;
  74 + data += bytes;
  75 + }
  76 +
  77 + if (this->getNext(true))
  78 + {
  79 + this->getNext()->write(buf, len);
  80 + }
  81 +}
  82 +
  83 +void
  84 +Pl_SHA2::finish()
  85 +{
  86 + if (this->getNext(true))
  87 + {
  88 + this->getNext()->finish();
  89 + }
  90 + switch (bits)
  91 + {
  92 + case 256:
  93 + sph_sha256_close(&this->ctx256, sha256sum);
  94 + break;
  95 + case 384:
  96 + sph_sha384_close(&this->ctx384, sha384sum);
  97 + break;
  98 + case 512:
  99 + sph_sha512_close(&this->ctx512, sha512sum);
  100 + break;
  101 + default:
  102 + badBits();
  103 + break;
  104 + }
  105 + this->in_progress = false;
  106 +}
  107 +
  108 +void
  109 +Pl_SHA2::resetBits(int bits)
  110 +{
  111 + if (this->in_progress)
  112 + {
  113 + throw std::logic_error(
  114 + "bit reset requested for in-progress SHA2 Pipeline");
  115 + }
  116 + if (! ((bits == 256) || (bits == 384) || (bits == 512)))
  117 + {
  118 + throw std::logic_error("Pl_SHA2 called with bits != 256, 384, or 512");
  119 + }
  120 + this->bits = bits;
  121 +}
  122 +
  123 +std::string
  124 +Pl_SHA2::getRawDigest()
  125 +{
  126 + std::string result;
  127 + switch (bits)
  128 + {
  129 + case 256:
  130 + result = std::string((char*)this->sha256sum, sizeof(this->sha256sum));
  131 + break;
  132 + case 384:
  133 + result = std::string((char*)this->sha384sum, sizeof(this->sha384sum));
  134 + break;
  135 + case 512:
  136 + result = std::string((char*)this->sha512sum, sizeof(this->sha512sum));
  137 + break;
  138 + default:
  139 + badBits();
  140 + break;
  141 + }
  142 + return result;
  143 +}
  144 +
  145 +std::string
  146 +Pl_SHA2::getHexDigest()
  147 +{
  148 + if (this->in_progress)
  149 + {
  150 + throw std::logic_error(
  151 + "digest requested for in-progress SHA2 Pipeline");
  152 + }
  153 + std::string raw = getRawDigest();
  154 + size_t raw_size = raw.length();
  155 + size_t hex_size = 1 + (2 * raw_size);
  156 + PointerHolder<char> bufp(true, new char[hex_size]);
  157 + char* buf = bufp.getPointer();
  158 + buf[hex_size - 1] = '\0';
  159 + for (unsigned int i = 0; i < raw_size; ++i)
  160 + {
  161 + std::sprintf(buf + i * 2, "%02x", (unsigned char)raw[i]);
  162 + }
  163 + return buf;
  164 +}
... ...
libqpdf/build.mk
... ... @@ -28,6 +28,7 @@ SRCS_libqpdf = \
28 28 libqpdf/Pl_PNGFilter.cc \
29 29 libqpdf/Pl_QPDFTokenizer.cc \
30 30 libqpdf/Pl_RC4.cc \
  31 + libqpdf/Pl_SHA2.cc \
31 32 libqpdf/Pl_StdioFile.cc \
32 33 libqpdf/QPDF.cc \
33 34 libqpdf/QPDFExc.cc \
... ...
libqpdf/qpdf/Pl_SHA2.hh 0 → 100644
  1 +#ifndef __PL_SHA2_HH__
  2 +#define __PL_SHA2_HH__
  3 +
  4 +// Bits must be a supported number of bits, currently only 256, 384,
  5 +// or 512. Passing 0 as bits leaves the pipeline uncommitted, in
  6 +// which case resetBits must be called before the pipeline is used.
  7 +// If a next is provided, this pipeline sends its output to its
  8 +// successor unmodified. After calling finish, the SHA2 checksum of
  9 +// the data that passed through the pipeline is available.
  10 +
  11 +// This pipeline is reusable; i.e., it is safe to call write() after
  12 +// calling finish(). The first call to write() after a call to
  13 +// finish() initializes a new SHA2 object. resetBits may also be
  14 +// called between finish and the next call to write.
  15 +
  16 +#include <qpdf/Pipeline.hh>
  17 +#include <sph/sph_sha2.h>
  18 +
  19 +class Pl_SHA2: public Pipeline
  20 +{
  21 + public:
  22 + QPDF_DLL
  23 + Pl_SHA2(int bits = 0, Pipeline* next = 0);
  24 + QPDF_DLL
  25 + virtual ~Pl_SHA2();
  26 + QPDF_DLL
  27 + virtual void write(unsigned char*, size_t);
  28 + QPDF_DLL
  29 + virtual void finish();
  30 + QPDF_DLL
  31 + void resetBits(int bits);
  32 + QPDF_DLL
  33 + std::string getHexDigest();
  34 + QPDF_DLL
  35 + std::string getRawDigest();
  36 +
  37 + private:
  38 + void badBits();
  39 +
  40 + bool in_progress;
  41 + int bits;
  42 + sph_sha256_context ctx256;
  43 + sph_sha384_context ctx384;
  44 + sph_sha512_context ctx512;
  45 + unsigned char sha256sum[32];
  46 + unsigned char sha384sum[48];
  47 + unsigned char sha512sum[64];
  48 +};
  49 +
  50 +#endif // __PL_SHA2_HH__
... ...
libqpdf/sph/sph_sha2.h
... ... @@ -40,6 +40,10 @@
40 40 #ifndef SPH_SHA2_H__
41 41 #define SPH_SHA2_H__
42 42  
  43 +#ifdef __cplusplus
  44 +extern "C" {
  45 +#endif
  46 +
43 47 #include <stddef.h>
44 48 #include "sph_types.h"
45 49  
... ... @@ -367,4 +371,8 @@ void sph_sha512_comp(const sph_u64 msg[16], sph_u64 val[8]);
367 371  
368 372 #endif
369 373  
  374 +#ifdef __cplusplus
  375 +}
  376 +#endif
  377 +
370 378 #endif
... ...
libtests/build.mk
... ... @@ -12,7 +12,8 @@ BINS_libtests = \
12 12 png_filter \
13 13 pointer_holder \
14 14 qutil \
15   - rc4
  15 + rc4 \
  16 + sha2
16 17  
17 18 TARGETS_libtests = $(foreach B,$(BINS_libtests),libtests/$(OUTPUT_DIR)/$(call binname,$(B)))
18 19  
... ...
libtests/qtest/sha2.test 0 → 100644
  1 +#!/usr/bin/env perl
  2 +require 5.008;
  3 +BEGIN { $^W = 1; }
  4 +use strict;
  5 +
  6 +chdir("sha2") or die "chdir testdir failed: $!\n";
  7 +
  8 +require TestDriver;
  9 +
  10 +my $td = new TestDriver('sha2');
  11 +
  12 +$td->runtest("sha2",
  13 + {$td->COMMAND => "sha2"},
  14 + {$td->FILE => "sha2.out",
  15 + $td->EXIT_STATUS => 0},
  16 + $td->NORMALIZE_NEWLINES);
  17 +
  18 +$td->report(1);
... ...
libtests/qtest/sha2/sha2.out 0 → 100644
  1 +256 short: passed
  2 +256 long: passed
  3 +256 million: passed
  4 +384 short: passed
  5 +384 long: passed
  6 +384 million: passed
  7 +512 short: passed
  8 +512 long: passed
  9 +512 million: passed
... ...
libtests/sha2.cc 0 → 100644
  1 +#include <qpdf/Pl_SHA2.hh>
  2 +#include <iostream>
  3 +#include <stdlib.h>
  4 +#include <string.h>
  5 +#include <qpdf/QUtil.hh>
  6 +
  7 +static void test(Pl_SHA2& sha2, char const* description, int bits,
  8 + char const* input, std::string const& output)
  9 +{
  10 + sha2.resetBits(bits);
  11 + sha2.write((unsigned char*) input, strlen(input));
  12 + sha2.finish();
  13 + std::cout << description << ": ";
  14 + if (output == sha2.getHexDigest())
  15 + {
  16 + std::cout << "passed\n";
  17 + }
  18 + else
  19 + {
  20 + std::cout << "failed\n"
  21 + << " expected: " << output << "\n"
  22 + << " actual: " << sha2.getHexDigest() << "\n";
  23 + }
  24 +}
  25 +
  26 +int main( int argc, char *argv[] )
  27 +{
  28 + Pl_SHA2 sha2;
  29 + char million_a[1000001];
  30 + memset(million_a, 'a', 1000000);
  31 + million_a[1000000] = '\0';
  32 + test(sha2, "256 short", 256,
  33 + "abc",
  34 + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
  35 + test(sha2, "256 long", 256,
  36 + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
  37 + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
  38 + test(sha2, "256 million", 256,
  39 + million_a,
  40 + "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
  41 + test(sha2, "384 short", 384,
  42 + "abc",
  43 + "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded163"
  44 + "1a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7");
  45 + test(sha2, "384 long", 384,
  46 + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
  47 + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
  48 + "09330c33f71147e83d192fc782cd1b4753111b173b3b05d2"
  49 + "2fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039");
  50 + test(sha2, "384 million", 384,
  51 + million_a,
  52 + "9d0e1809716474cb086e834e310a4a1ced149e9c00f24852"
  53 + "7972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985");
  54 + test(sha2, "512 short", 512,
  55 + "abc",
  56 + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a"
  57 + "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f");
  58 + test(sha2, "512 long", 512,
  59 + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
  60 + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
  61 + "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018"
  62 + "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909");
  63 + test(sha2, "512 million", 512,
  64 + million_a,
  65 + "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb"
  66 + "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b");
  67 +
  68 + return 0;
  69 +}
... ...