1

The following code generates a deterministic stream of pseudo-random bytes, using SHA3.

use sha3::{Digest, Sha3_256};

pub struct ShaStream { passhash: [u8; 32], nonce: [u8; 32], index: u64, block: [u8; 32], hasher: Sha3_256, }

impl ShaStream { pub fn new(passhash: [u8; 32], nonce: [u8; 32]) -> ShaStream { ShaStream {passhash, nonce, index: 0, block: [0; 32], hasher: Sha3_256::new()} } }

impl Iterator for ShaStream { type Item = u8;

fn next(&mut self) -> Option<u8> {
    // make a new block, every 32 bytes
    if self.index % 32 == 0 {
        self.hasher.update(self.index.to_be_bytes());
        self.hasher.update(self.passhash);
        self.hasher.update(self.block);
        self.hasher.update(self.nonce);
        self.block = self.hasher.finalize_reset().into();
    }
    let value = self.block[self.index as usize % 32];
    self.index += 1;
    Some(value)
}

}

(passhash is the hash of a compressed, whitened, long passphrase. nonces must not be reused.)

If I XOR this stream with a file, will I get strong encryption?

Or are there weaknesses which I haven't considered?

P.S. The results of dieharder, on 1 MB of output, look reasonable - but I am not sure how to judge them. Is a single WEAK a problem?

$ dieharder -a -f xor.bin 
#=============================================================================#
#            dieharder version 3.31.1 Copyright 2003 Robert G. Brown          #
#=============================================================================#
   rng_name    |           filename             |rands/second|
        mt19937|                         xor.bin|  1.02e+08  |
#=============================================================================#
        test_name   |ntup| tsamples |psamples|  p-value |Assessment
#=============================================================================#
   diehard_birthdays|   0|       100|     100|0.23634314|  PASSED  
      diehard_operm5|   0|   1000000|     100|0.44073675|  PASSED  
  diehard_rank_32x32|   0|     40000|     100|0.09071670|  PASSED  
    diehard_rank_6x8|   0|    100000|     100|0.98413526|  PASSED  
   diehard_bitstream|   0|   2097152|     100|0.80928251|  PASSED  
        diehard_opso|   0|   2097152|     100|0.30077190|  PASSED  
        diehard_oqso|   0|   2097152|     100|0.91046849|  PASSED  
         diehard_dna|   0|   2097152|     100|0.23134815|  PASSED  
diehard_count_1s_str|   0|    256000|     100|0.28623905|  PASSED  
diehard_count_1s_byt|   0|    256000|     100|0.53924476|  PASSED  
 diehard_parking_lot|   0|     12000|     100|0.68281092|  PASSED  
    diehard_2dsphere|   2|      8000|     100|0.78529773|  PASSED  
    diehard_3dsphere|   3|      4000|     100|0.31723984|  PASSED  
     diehard_squeeze|   0|    100000|     100|0.96834170|  PASSED  
        diehard_sums|   0|       100|     100|0.48488812|  PASSED  
        diehard_runs|   0|    100000|     100|0.83280719|  PASSED  
        diehard_runs|   0|    100000|     100|0.91027127|  PASSED  
       diehard_craps|   0|    200000|     100|0.37347197|  PASSED  
       diehard_craps|   0|    200000|     100|0.91605252|  PASSED  
 marsaglia_tsang_gcd|   0|  10000000|     100|0.67400210|  PASSED  
 marsaglia_tsang_gcd|   0|  10000000|     100|0.36182964|  PASSED  
         sts_monobit|   1|    100000|     100|0.04375935|  PASSED  
            sts_runs|   2|    100000|     100|0.99425607|  PASSED  
          sts_serial|   1|    100000|     100|0.82838574|  PASSED  
          sts_serial|   2|    100000|     100|0.86544431|  PASSED  
          sts_serial|   3|    100000|     100|0.82113526|  PASSED  
          sts_serial|   3|    100000|     100|0.99367519|  PASSED  
          sts_serial|   4|    100000|     100|0.43314255|  PASSED  
          sts_serial|   4|    100000|     100|0.81866202|  PASSED  
          sts_serial|   5|    100000|     100|0.50034343|  PASSED  
          sts_serial|   5|    100000|     100|0.64324459|  PASSED  
          sts_serial|   6|    100000|     100|0.88971853|  PASSED  
          sts_serial|   6|    100000|     100|0.97804225|  PASSED  
          sts_serial|   7|    100000|     100|0.82763353|  PASSED  
          sts_serial|   7|    100000|     100|0.73522587|  PASSED  
          sts_serial|   8|    100000|     100|0.99950104|   WEAK
          sts_serial|   8|    100000|     100|0.50056257|  PASSED  
          sts_serial|   9|    100000|     100|0.17834420|  PASSED  
          sts_serial|   9|    100000|     100|0.81670387|  PASSED  
          sts_serial|  10|    100000|     100|0.06053301|  PASSED  
          sts_serial|  10|    100000|     100|0.16833877|  PASSED  
          sts_serial|  11|    100000|     100|0.45143111|  PASSED  
          sts_serial|  11|    100000|     100|0.90007517|  PASSED  
          sts_serial|  12|    100000|     100|0.29181206|  PASSED  
          sts_serial|  12|    100000|     100|0.23372932|  PASSED  
          sts_serial|  13|    100000|     100|0.26269445|  PASSED  
          sts_serial|  13|    100000|     100|0.39691685|  PASSED  
          sts_serial|  14|    100000|     100|0.27804065|  PASSED  
          sts_serial|  14|    100000|     100|0.55210331|  PASSED  
          sts_serial|  15|    100000|     100|0.02302363|  PASSED  
          sts_serial|  15|    100000|     100|0.04573078|  PASSED  
          sts_serial|  16|    100000|     100|0.03990599|  PASSED  
          sts_serial|  16|    100000|     100|0.96326029|  PASSED  
         rgb_bitdist|   1|    100000|     100|0.84215365|  PASSED  
         rgb_bitdist|   2|    100000|     100|0.49349609|  PASSED  
         rgb_bitdist|   3|    100000|     100|0.18203352|  PASSED  
         rgb_bitdist|   4|    100000|     100|0.42475472|  PASSED  
         rgb_bitdist|   5|    100000|     100|0.24511091|  PASSED  
         rgb_bitdist|   6|    100000|     100|0.92089949|  PASSED  
         rgb_bitdist|   7|    100000|     100|0.84628916|  PASSED  
         rgb_bitdist|   8|    100000|     100|0.22711540|  PASSED  
         rgb_bitdist|   9|    100000|     100|0.34303650|  PASSED  
         rgb_bitdist|  10|    100000|     100|0.59588524|  PASSED  
         rgb_bitdist|  11|    100000|     100|0.91612653|  PASSED  
         rgb_bitdist|  12|    100000|     100|0.27207545|  PASSED  
rgb_minimum_distance|   2|     10000|    1000|0.75765186|  PASSED  
rgb_minimum_distance|   3|     10000|    1000|0.34765391|  PASSED  
rgb_minimum_distance|   4|     10000|    1000|0.56023937|  PASSED  
rgb_minimum_distance|   5|     10000|    1000|0.79620902|  PASSED  
    rgb_permutations|   2|    100000|     100|0.99300316|  PASSED  
    rgb_permutations|   3|    100000|     100|0.28066491|  PASSED  
    rgb_permutations|   4|    100000|     100|0.36539121|  PASSED  
    rgb_permutations|   5|    100000|     100|0.33314486|  PASSED  
      rgb_lagged_sum|   0|   1000000|     100|0.15207425|  PASSED  
      rgb_lagged_sum|   1|   1000000|     100|0.07013932|  PASSED  
      rgb_lagged_sum|   2|   1000000|     100|0.27477884|  PASSED  
      rgb_lagged_sum|   3|   1000000|     100|0.95111023|  PASSED  
      rgb_lagged_sum|   4|   1000000|     100|0.02796184|  PASSED  
      rgb_lagged_sum|   5|   1000000|     100|0.45874461|  PASSED  
      rgb_lagged_sum|   6|   1000000|     100|0.18328909|  PASSED  
      rgb_lagged_sum|   7|   1000000|     100|0.02821152|  PASSED  
      rgb_lagged_sum|   8|   1000000|     100|0.16271011|  PASSED  
      rgb_lagged_sum|   9|   1000000|     100|0.63672591|  PASSED  
      rgb_lagged_sum|  10|   1000000|     100|0.97828167|  PASSED  
      rgb_lagged_sum|  11|   1000000|     100|0.71885077|  PASSED  
      rgb_lagged_sum|  12|   1000000|     100|0.75394600|  PASSED  
      rgb_lagged_sum|  13|   1000000|     100|0.61132545|  PASSED  
      rgb_lagged_sum|  14|   1000000|     100|0.36991248|  PASSED  
      rgb_lagged_sum|  15|   1000000|     100|0.98795179|  PASSED  
      rgb_lagged_sum|  16|   1000000|     100|0.39086917|  PASSED  
      rgb_lagged_sum|  17|   1000000|     100|0.72342485|  PASSED  
      rgb_lagged_sum|  18|   1000000|     100|0.32210912|  PASSED  
      rgb_lagged_sum|  19|   1000000|     100|0.23889372|  PASSED  
      rgb_lagged_sum|  20|   1000000|     100|0.02979572|  PASSED  
      rgb_lagged_sum|  21|   1000000|     100|0.99132612|  PASSED  
      rgb_lagged_sum|  22|   1000000|     100|0.63051091|  PASSED  
      rgb_lagged_sum|  23|   1000000|     100|0.40911221|  PASSED  
      rgb_lagged_sum|  24|   1000000|     100|0.95798515|  PASSED  
      rgb_lagged_sum|  25|   1000000|     100|0.81390689|  PASSED  
      rgb_lagged_sum|  26|   1000000|     100|0.21471266|  PASSED  
      rgb_lagged_sum|  27|   1000000|     100|0.97590860|  PASSED  
      rgb_lagged_sum|  28|   1000000|     100|0.65097372|  PASSED  
      rgb_lagged_sum|  29|   1000000|     100|0.40351426|  PASSED  
      rgb_lagged_sum|  30|   1000000|     100|0.67212314|  PASSED  
      rgb_lagged_sum|  31|   1000000|     100|0.36602753|  PASSED  
      rgb_lagged_sum|  32|   1000000|     100|0.54699284|  PASSED  
     rgb_kstest_test|   0|     10000|    1000|0.47824082|  PASSED  
     dab_bytedistrib|   0|  51200000|       1|0.27617055|  PASSED  
             dab_dct| 256|     50000|       1|0.06378066|  PASSED  
Preparing to run test 207.  ntuple = 0
        dab_filltree|  32|  15000000|       1|0.51156512|  PASSED  
        dab_filltree|  32|  15000000|       1|0.77505955|  PASSED  
Preparing to run test 208.  ntuple = 0
       dab_filltree2|   0|   5000000|       1|0.92507779|  PASSED  
       dab_filltree2|   1|   5000000|       1|0.86783259|  PASSED  
Preparing to run test 209.  ntuple = 0
        dab_monobit2|  12|  65000000|       1|0.39811014|  PASSED  
fadedbee
  • 968
  • 1
  • 11
  • 31

3 Answers3

1

I wrote something similar in C using SHA256 several years ago and it also passed all the DieHarder tests.

I intended to use it as a stream cipher in a project that employed variable length data, i.e. conversational messages, where both sides remained synchronised and therefor could continue calling off blocks of pseudo-random numbers for use with XOR.

Other than the potential issue with synchronisation I do not understand why the field of cryptography is obsessed with block ciphers when many real-world applications need stream ciphers.

Mike Tubby
  • 11
  • 1
1

I think the consensus is that there's nothing obviously insecure with this, and that I can safely carry on hammering-in nails with my spanner.

As long as the hash used also functions as a CSPRNG you're fine. Any (secure) hashing algorithm would create a secure stream cipher in this model.

It should be noted that not all hashing algorithms must function as a CSPRNG. This is not a requirement for a secure hash, and secure hashes have been constructed without this property. However, the hashing algorithms I reference in stating this are rarely used and just as rarely known about. SHA-3 acts as a CSPRNG so this note is irrelevant in your case.

The ciphertext does become distinguishable from random after the hash's state repeats. For example, a 512 bit hash would become distinguishable from random after $512 * 2^{512}$ bits. A 384-bit hash after $384 * 2^{384}$ bits... You get the idea.

Serpent27
  • 1,471
  • 6
  • 11
0

I think the consensus is that there's nothing obviously insecure with this, and that I can safely carry on hammering-in nails with my spanner.

Perhaps this is about trust? If I trusted SHA3, but not block ciphers, would this be reasonable?

(I have no reason to trust SHA3 and not block ciphers.)

fadedbee
  • 968
  • 1
  • 11
  • 31