Crypt-OpenSSL-FASTPBKDF2

 view release on metacpan or  search on metacpan

FASTPBKDF2.xs  view on Meta::CPAN

  printf("%s: ", label);
  for (size_t i = 0; i < n; i++)
    printf("%02x", data[i]);
  printf("\n");
}

MODULE = Crypt::OpenSSL::FASTPBKDF2		PACKAGE = Crypt::OpenSSL::FASTPBKDF2
PROTOTYPES: ENABLE

SV *
fastpbkdf2_hmac_interface(pw, salt, iterations, nout, IN_OUT data_buffer = newAV())
        SV * pw
        SV * salt
        uint32_t iterations
        STRLEN nout
        AV * &data_buffer
    PROTOTYPE: $$$$;\@
    PREINIT:
        STRLEN npw;
        STRLEN nsalt;
        uint8_t * cpw;
        uint8_t * csalt;
        uint8_t * hashPtr;
        SV * hash = newSVpv("",0);
    INIT:
        cpw = SvPVbyte(pw, npw);
        csalt = SvPVbyte(salt, nsalt);
        Newx(hashPtr, nout+1, uint8_t);
        sv_usepvn_flags(hash, hashPtr, nout, SV_SMAGIC | SV_HAS_TRAILING_NUL);
    C_ARGS:
        cpw, npw, csalt, nsalt, iterations, hashPtr, nout
    INTERFACE:
        fastpbkdf2_hmac_sha1 fastpbkdf2_hmac_sha256 fastpbkdf2_hmac_sha512
	POSTCALL:
        if(ST(5)) av_push(data_buffer, newSVpvn(hashPtr, nout)); // Append to @data_buffer array, if provided
        hashPtr[nout] = '\0'; // NUL-terminated string
        RETVAL = hash;

README  view on Meta::CPAN


This module requires these other modules and libraries:

  DynaLoader
  OpenSSL

SYNOPSIS

  use Crypt::OpenSSL::FASTPBKDF2 qw/fastpbkdf2_hmac_sha1 fastpbkdf2_hmac_sha256 fastpbkdf2_hmac_sha512/;

  # Initialize parameters for password, salt, number of iterations, and desired output length (in bytes)
  my ($password, $salt, $num_iterations, $output_len) = ('password', 'salt', 100, 32);

  # Initialize buffer array (optional argument)
  my @buffer;

  # Set hash results into scalar variables
  my $hash_sha1 = fastpbkdf2_hmac_sha1($password, $salt, $num_iterations, $output_len, @buffer);        #= 0x8595d7aea0e7c952a35af9a838cc6b393449307cfcc7bd340e7e32ee90115650
  my $hash_sha256 = fastpbkdf2_hmac_sha256($password, $salt, $num_iterations, $output_len, @buffer);    #= 0x07e6997180cf7f12904f04100d405d34888fdf62af6d506a0ecc23b196fe99d8
  my $hash_sha512 = fastpbkdf2_hmac_sha512($password, $salt, $num_iterations, $output_len, @buffer);    #= 0xfef7276b107040a0a713bcbec9fd3e191cc6153249e245a3e1a22087dbe61606

  # Print the contents of the buffer as HEX
  print unpack('H*', join('', @buffer)); # "8595d7aea0e7c952a35af9a838cc6b393449307cfcc7bd340e7e32ee9011565007e6997180cf7f12904f04100d405d34888fdf62af6d506a0ecc23b196fe99d8fef7276b107040a0a713bcbec9fd3e191cc6153249e245a3e1a22087dbe61606"

COPYRIGHT AND LICENCE

Copyright (C) 2017 Duane Hutchins - Univeral Printing Company

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.16 or,

lib/Crypt/OpenSSL/FASTPBKDF2.pm  view on Meta::CPAN

__END__

=head1 NAME

Crypt::OpenSSL::FASTPBKDF2 - Perl wrapper for PBKDF2 keys derivation function of the OpenSSL library using fastpbkdf2

=head1 SYNOPSIS

  use Crypt::OpenSSL::FASTPBKDF2 qw/fastpbkdf2_hmac_sha1 fastpbkdf2_hmac_sha256 fastpbkdf2_hmac_sha512/;

  # Initialize parameters for password, salt, number of iterations, and desired output length (in bytes)
  my ($password, $salt, $num_iterations, $output_len) = ('password', 'salt', 100, 32);

  # Initialize buffer array (optional argument)
  my @buffer;

  # Set hash results into scalar variables
  my $hash_sha1 = fastpbkdf2_hmac_sha1($password, $salt, $num_iterations, $output_len, @buffer);        #= 0x8595d7aea0e7c952a35af9a838cc6b393449307cfcc7bd340e7e32ee90115650
  my $hash_sha256 = fastpbkdf2_hmac_sha256($password, $salt, $num_iterations, $output_len, @buffer);    #= 0x07e6997180cf7f12904f04100d405d34888fdf62af6d506a0ecc23b196fe99d8
  my $hash_sha512 = fastpbkdf2_hmac_sha512($password, $salt, $num_iterations, $output_len, @buffer);    #= 0xfef7276b107040a0a713bcbec9fd3e191cc6153249e245a3e1a22087dbe61606

  # Print the contents of the buffer as HEX
  print unpack('H*', join('', @buffer)); # "8595d7aea0e7c952a35af9a838cc6b393449307cfcc7bd340e7e32ee9011565007e6997180cf7f12904f04100d405d34888fdf62af6d506a0ecc23b196fe99d8fef7276b107040a0a713bcbec9fd3e191cc6153249e245a3e1a22087dbe61606"

=head1 DESCRIPTION

PBKDF2 applies a pseudorandom function, such as hash-based message authentication code (HMAC), to the input password or passphrase along with a salt value and repeats the process many times to produce a derived key, which can then be used as a crypto...
fastpbkdf2 is a fast PBKDF2-HMAC-{SHA1,SHA256,SHA512} implementation in C. It uses OpenSSL's hash functions, but out-performs OpenSSL's own PBKDF2 thanks to various optimisations in the inner loop.

Crypt::OpenSSL::FASTPBKDF2 is a set of Perl bindings for fastpbkdf2.

=head1 Static Methods

=head2 fastpbkdf2_hmac_sha1 ($password, $salt, $iterations, $output_len, :@buffer)

Executes PBKDF2 via HMAC_SHA1 to hash C<$password> with C<$salt> repeatedly, C<$iterations> times, to derive and return a hash that is C<$output_len> bytes long.
If the optional C<@buffer> param is provided, the result will also be appended onto the array.

=head2 fastpbkdf2_hmac_sha256 ($password, $salt, $iterations, $output_len, :@buffer)

Same as C<fastpbkdf2_hmac_sha1> but instead uses HMAC_SHA256

=head2 fastpbkdf2_hmac_sha512 ($password, $salt, $iterations, $output_len, :@buffer)

Same as C<fastpbkdf2_hmac_sha1> but instead uses HMAC_SHA512

=head1 SEE ALSO

NIST-PBKDF2 L<http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf>

Joseph Birr-Pixton - fastpbkdf2 L<https://github.com/ctz/fastpbkdf2>

=head1 AUTHOR

src/README.md  view on Meta::CPAN

It uses OpenSSL's hash functions, but out-performs OpenSSL's own PBKDF2
thanks to [various optimisations in the inner loop](https://jbp.io/2015/08/11/pbkdf2-performance-matters/#strategies).

[![Build Status](https://travis-ci.org/ctz/fastpbkdf2.svg?branch=master)](https://travis-ci.org/ctz/fastpbkdf2)

## Interface

```c
void fastpbkdf2_hmac_sha1(const uint8_t *pw, size_t npw,
                          const uint8_t *salt, size_t nsalt,
                          uint32_t iterations,
                          uint8_t *out, size_t nout);

void fastpbkdf2_hmac_sha256(const uint8_t *pw, size_t npw,
                            const uint8_t *salt, size_t nsalt,
                            uint32_t iterations,
                            uint8_t *out, size_t nout);

void fastpbkdf2_hmac_sha512(const uint8_t *pw, size_t npw,
                            const uint8_t *salt, size_t nsalt,
                            uint32_t iterations,
                            uint8_t *out, size_t nout);
```

Please see the header file for details and constraints.

## Performance

These values are wall time, output from the `bench` tool.

### AMD64
Hash     | OpenSSL     | fastpbkdf2   | (comparison)
---------|-------------|--------------|--------------
SHA1     | 11.84s      | 3.07s        | x3.86
SHA256   | 16.54s      | 7.45s        | x2.22
SHA512   | 21.90s      | 9.33s        | x2.34

2<sup>22</sup> iterations, 1.86GHz Intel Atom N2800, amd64.

### ARM
Hash     | OpenSSL     | fastpbkdf2   | (comparison)
---------|-------------|--------------|--------------
SHA1     | 30.4s       | 4.43s        | x6.86
SHA256   | 36.52s      | 7.04s        | x5.19
SHA512   | 77.44s      | 28.1s        | x2.76

2<sup>20</sup> iterations, Raspberry Pi - 700MHz ARM11.

## Requirements
* OpenSSL's libcrypto.
* C compiler supporting C99.

## Building and testing
Run 'make test' to build and run tests.

The program `bench` provides a very basic performance comparison between OpenSSL and fastpbkdf2.

src/fastpbkdf2.c  view on Meta::CPAN

    _final(out, &ctx->inner);                                                 \
    _update(&ctx->outer, out, _hashsz);                                       \
    _final(out, &ctx->outer);                                                 \
  }                                                                           \
                                                                              \
                                                                              \
  /* --- PBKDF2 --- */                                                        \
  static inline void PBKDF2_F(_name)(const HMAC_CTX(_name) *startctx,         \
                                     uint32_t counter,                        \
                                     const uint8_t *salt, size_t nsalt,       \
                                     uint32_t iterations,                     \
                                     uint8_t *out)                            \
  {                                                                           \
    uint8_t countbuf[4];                                                      \
    write32_be(counter, countbuf);                                            \
                                                                              \
    /* Prepare loop-invariant padding block. */                               \
    uint8_t Ublock[_blocksz];                                                 \
    md_pad(Ublock, _blocksz, _hashsz, _blocksz + _hashsz);                    \
                                                                              \
    /* First iteration:                                                       \
     *   U_1 = PRF(P, S || INT_32_BE(i))                                      \
     */                                                                       \
    HMAC_CTX(_name) ctx = *startctx;                                          \
    HMAC_UPDATE(_name)(&ctx, salt, nsalt);                                    \
    HMAC_UPDATE(_name)(&ctx, countbuf, sizeof countbuf);                      \
    HMAC_FINAL(_name)(&ctx, Ublock);                                          \
    _ctx result = ctx.outer;                                                  \
                                                                              \
    /* Subsequent iterations:                                                 \
     *   U_c = PRF(P, U_{c-1})                                                \
     */                                                                       \
    for (uint32_t i = 1; i < iterations; i++)                                 \
    {                                                                         \
      /* Complete inner hash with previous U */                               \
      _xcpy(&ctx.inner, &startctx->inner);                                    \
      _xform(&ctx.inner, Ublock);                                             \
      _xtract(&ctx.inner, Ublock);                                            \
      /* Complete outer hash with inner output */                             \
      _xcpy(&ctx.outer, &startctx->outer);                                    \
      _xform(&ctx.outer, Ublock);                                             \
      _xtract(&ctx.outer, Ublock);                                            \
      _xxor(&result, &ctx.outer);                                             \
    }                                                                         \
                                                                              \
    /* Reform result into output buffer. */                                   \
    _xtract(&result, out);                                                    \
  }                                                                           \
                                                                              \
  static inline void PBKDF2(_name)(const uint8_t *pw, size_t npw,             \
                     const uint8_t *salt, size_t nsalt,                       \
                     uint32_t iterations,                                     \
                     uint8_t *out, size_t nout)                               \
  {                                                                           \
    assert(iterations);                                                       \
    assert(out && nout);                                                      \
                                                                              \
    /* Starting point for inner loop. */                                      \
    HMAC_CTX(_name) ctx;                                                      \
    HMAC_INIT(_name)(&ctx, pw, npw);                                          \
                                                                              \
    /* How many blocks do we need? */                                         \
    uint32_t blocks_needed = (uint32_t)(nout + _hashsz - 1) / _hashsz;        \
                                                                              \
    OPENMP_PARALLEL_FOR                                                       \
    for (uint32_t counter = 1; counter <= blocks_needed; counter++)           \
    {                                                                         \
      uint8_t block[_hashsz];                                                 \
      PBKDF2_F(_name)(&ctx, counter, salt, nsalt, iterations, block);         \
                                                                              \
      size_t offset = (counter - 1) * _hashsz;                                \
      size_t taken = MIN(nout - offset, _hashsz);                             \
      memcpy(out + offset, block, taken);                                     \
    }                                                                         \
  }

static inline void sha1_extract(SHA_CTX *restrict ctx, uint8_t *restrict out)
{
  write32_be(ctx->h0, out);

src/fastpbkdf2.c  view on Meta::CPAN

            SHA512_Init,
            SHA512_Update,
            SHA512_Transform,
            SHA512_Final,
            sha512_cpy,
            sha512_extract,
            sha512_xor)

void fastpbkdf2_hmac_sha1(const uint8_t *pw, size_t npw,
                          const uint8_t *salt, size_t nsalt,
                          uint32_t iterations,
                          uint8_t *out, size_t nout)
{
  PBKDF2(sha1)(pw, npw, salt, nsalt, iterations, out, nout);
}

void fastpbkdf2_hmac_sha256(const uint8_t *pw, size_t npw,
                            const uint8_t *salt, size_t nsalt,
                            uint32_t iterations,
                            uint8_t *out, size_t nout)
{
  PBKDF2(sha256)(pw, npw, salt, nsalt, iterations, out, nout);
}

void fastpbkdf2_hmac_sha512(const uint8_t *pw, size_t npw,
                            const uint8_t *salt, size_t nsalt,
                            uint32_t iterations,
                            uint8_t *out, size_t nout)
{
  PBKDF2(sha512)(pw, npw, salt, nsalt, iterations, out, nout);
}

src/fastpbkdf2.h  view on Meta::CPAN

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/** Calculates PBKDF2-HMAC-SHA1.
 *
 *  @p npw bytes at @p pw are the password input.
 *  @p nsalt bytes at @p salt are the salt input.
 *  @p iterations is the PBKDF2 iteration count and must be non-zero.
 *  @p nout bytes of output are written to @p out.  @p nout must be non-zero.
 *
 *  This function cannot fail; it does not report errors.
 */
void fastpbkdf2_hmac_sha1(const uint8_t *pw, size_t npw,
                          const uint8_t *salt, size_t nsalt,
                          uint32_t iterations,
                          uint8_t *out, size_t nout);

/** Calculates PBKDF2-HMAC-SHA256.
 *
 *  @p npw bytes at @p pw are the password input.
 *  @p nsalt bytes at @p salt are the salt input.
 *  @p iterations is the PBKDF2 iteration count and must be non-zero.
 *  @p nout bytes of output are written to @p out.  @p nout must be non-zero.
 *
 *  This function cannot fail; it does not report errors.
 */
void fastpbkdf2_hmac_sha256(const uint8_t *pw, size_t npw,
                            const uint8_t *salt, size_t nsalt,
                            uint32_t iterations,
                            uint8_t *out, size_t nout);

/** Calculates PBKDF2-HMAC-SHA512.
 *
 *  @p npw bytes at @p pw are the password input.
 *  @p nsalt bytes at @p salt are the salt input.
 *  @p iterations is the PBKDF2 iteration count and must be non-zero.
 *  @p nout bytes of output are written to @p out.  @p nout must be non-zero.
 *
 *  This function cannot fail; it does not report errors.
 */
void fastpbkdf2_hmac_sha512(const uint8_t *pw, size_t npw,
                            const uint8_t *salt, size_t nsalt,
                            uint32_t iterations,
                            uint8_t *out, size_t nout);

#ifdef __cplusplus
}
#endif

#endif

t/Crypt-OpenSSL-FASTPBKDF2.t  view on Meta::CPAN

BEGIN { use_ok('Crypt::OpenSSL::FASTPBKDF2', qw/fastpbkdf2_hmac_sha1 fastpbkdf2_hmac_sha256 fastpbkdf2_hmac_sha512/) };

use constant HMAC_SUBS => {
#   sha0 => $subref
    sha1 => \&fastpbkdf2_hmac_sha1,
    sha256 => \&fastpbkdf2_hmac_sha256,
    sha512 => \&fastpbkdf2_hmac_sha512,
};

use constant HMAC_DATA => {
#   sha0 => [ [password, salt, iterations, hex_output_expected] ... ]
    sha1 => [
        ['password', 'salt', 1, '0c60c80f961f0e71f3a9b524af6012062fe037a6'],
        ['password', 'salt', 2, 'ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957'],
        ['password', 'salt', 4096, '4b007901b765489abead49d926f721d065a429c1'],
        # ['password', 'salt', 16777216, 'eefe3d61cd4da4e4e9945b3d6ba2158c2634e984'],
        ['passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt', 4096, '3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038'],
        ["pass\x00\x77\x6f\x72\x64", "sa\x00\x6c\x74", 4096, '56fa6aa75548099dcc37d7f03425e0c3'],
    ],
    sha256 => [
        ['passwd', 'salt', 1, '55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783'],

t/Crypt-OpenSSL-FASTPBKDF2.t  view on Meta::CPAN

    my $hmac = shift;
    my $test_sub = HMAC_SUBS()->{$hmac};
    my $test_data = HMAC_DATA()->{$hmac};

    subtest "HMAC $hmac"=>sub {
        plan tests => 1 + scalar @$test_data;
        my @buffer;

        # Data Test
        foreach my $t (@$test_data) {
            my ($pw, $salt, $iterations, $expected) = @$t;
            my $out_len = length($expected)/2;
            my $output = $test_sub->($pw, $salt, $iterations, $out_len, \@buffer);
            is( unpack('H*', $output), $expected, $hmac.' data' );
        }

        # Buffer Test
        my @buf_expected = map { $_->[3] } @$test_data;
        my @buf_output = map { unpack('H*', $_) } @buffer;
        is_deeply(\@buf_output, \@buf_expected, $hmac.' buffer');
    };
};



( run in 0.817 second using v1.01-cache-2.11-cpan-71847e10f99 )