++ed by:

1 non-PAUSE user.

Author image Alex J. G. Burzyński

NAME

Data::BytesLocker - Guarded storage for sensitive data

VERSION

version 1.0.8.0

SYNOPSIS

    use Crypt::NaCl::Sodium qw(:utils);

    # lock by default
    $Data::BytesLocker::DEFAULT_LOCKED = 1;

    # some sensitive data read from external sources, eg. database
    my $password = ...;

    my $password_locker = Data::BytesLocker->new($password, wipe => 1);

    # $password now is overwritten with null bytes
    $password =~ /^\0+$/ or die;

    # as requested locker is locked
    $password_locker->is_locked or die;

    # dies with: "Unlock BytesLocker object before accessing the data"
    print "password: ", $password_locker->bytes, "\n";

    # unlock the data
    $password_locker->unlock;

    # as requested locker is unlocked
    $password_locker->is_locked and die;

    # prints the password using overloaded stringification
    print "password: $password_locker\n";

    # Crypt::NaCl::Sodium functions and methods return binary data locked in Data::BytesLocker objects
    my $random_password = random_bytes( 32 );

    # we wanted locked by default
    $random_password->unlock;

    # helper function to convert into hexadecimal string
    print "random password: ", $random_password->to_hex, "\n";

    # clone the data into new object
    my $copy = $password_locker->clone;

    # nonce increment
    my $next_nonce = $nonce->increment;

    # add number
    my $next_nonce = $nonce->add( $step );

    # check if the data contains zero bits only
    $next_nonce->is_zero and print "back to square zero\n";

    # always lock the data once done using it
    $password_locker->lock;
    $random_password->lock;

    # wipe out the memory and destroy the object
    undef $password_locker;
    undef $random_password;
    undef $copy;

DESCRIPTION

Heartbleed was a serious vulnerability in OpenSSL. The ability to read past the end of a buffer is a serious bug, but what made it even worse is the fact that secret data could be disclosed by doing so.

In order to mitigate the impact of similar bugs, Data::BytesLocker provides heap allocation functions for storing sensitive data.

These are not general-purpose allocation functions. In particular, they are slower than regular scalars, and they require 3 or 4 extra pages of virtual memory (usually between 12-16kb extra memory will be used).

The stored data is placed at the end of a page boundary, immediately followed by a guard page. As a result, accessing memory past the end of the region will immediately terminate the application.

A canary is also placed right before the stored data. Modification of this canary are detected when trying to free the allocated region, and also cause the application to immediately terminate.

An additional guard page is placed before this canary. In a Heartbleed-like scenario, this guard page is likely to be read before the actual data, and this access will cause the application to terminate instead of leaking sensitive data.

The allocated region is filled with 0xd0 bytes in order to help catch bugs due to initialized data.

On operating systems supporting MAP_NOCORE or MADV_DONTDUMP, the memory allocated this way will also not be part of core dumps and can help avoid the data being swapped to disk.

METHODS

new

    my $locker = Data::BytesLocker->new($data, wipe => 1 );

Returns object that stores the input $data in a protected memory location.

If the optional parameter wipe is given and is true, then the input $data variable will be overwritten with null bytes.

Returned $locker object will contain the data that cannot be modified and if the object is locked it cannot be accessed as well.

Data::BytesLocker object when used in string context return the protected data. See "OVERLOADED OPERATIONS" for more details.

clone

    my $cloned = $locker->clone;

Returns new data object which will contain the copied data from $locker.

lock

    $locker->lock();

When called makes the data stored inaccessible. It cannot be read or written, but the data are preserved.

unlock

    $locker->unlock();

When called makes the data stored accessible for read access only.

is_locked

    if ( $locker->is_locked ) {
        $locker->unlock;
    }

Returns true if the $locker object is locked, false otherwise.

length

    my $data_length = $locker->length();

Returns the length of protected bytes.

to_hex

    my $hexencoded = $locker->to_hex();

Returns the protected data converted into a hexadecimal string.

NOTE: the $locker object must be unlocked.

Returns regular scalar.

bytes

    my $bytes = $locker->bytes();

Returns the protected data as regular scalar.

NOTE: the $locker object must be unlocked.

is_zero

    if ( $locker->is_zero ) {
        print "data contains zero bits only\n";
    }

Returns true if the $locker object contains zero bits only. Runs in constant-time for objects of the same length.

memcmp

    $locker->memcmp($bytes, $length ) or die "\$locker ne \$bytes for length: $length";

Compares strings in constant-time. Returns true if they match, false otherwise.

The argument $length is optional if length of $bytes is equal to the length of the data stored in $locker. Otherwise it is required and cannot be greater then the length of the shorter of compared variables.

compare

    $nonce->compare( $number, $length ) == -1 and print "\$nonce < \$number for length: $length";

A constant-time version of "memcmp", useful to compare nonces and counters in little-endian format, that plays well with "increment".

Returns -1 if $nonce is lower then $number, 0 if $nonce and $number are identical, or 1 if $nonce is greater then $number. Both $nonce and $number are assumed to be numbers encoded in little-endian format.

The argument $length is optional if variables are of the same length. Otherwise it is required and cannot be greater then the length of the shorter of compared variables.

increment

    my $next_nonce = $nonce->increment();

Increments an arbitrary long unsigned number. Method runs in constant-time for a given length of locked data and considers it to be encoded in little-endian format.

This method is meant to be used to increment nonces and counters.

Returns the incremented object.

add

    my $next_nonce = $nonce->add($number, $length);

Method computes ($nonce + $number) mod 2 ^ (8 * $length) in constant time for a given length and returns the result of that computation. Both $nonce and $number are assumed to be numbers encoded in little-endian format.

The argument $length is optional if variables are of the same length. Otherwise it is required and cannot be greater then the length of the shorter of compared variables.

This method is meant to be used to increment nonces and counters using specified step.

OVERLOADED OPERATIONS

Only operations listed below are supported.

stringification

    print "Password: $locker\n";

Returns the protected data as regular scalar.

stringwise equality

    if ( $locker eq $expected ) {
        print "matches\n";
    }

    if ( $locker ne $expected ) {
        print "does not match\n";
    }

The eq and ne operations are overloaded and allow to compare the $locker object with variable of equal length.

boolean context

    if ( $locker ) {
        print "locker has some non-zero length data\n";
    }

    if ( ! $locker ) {
        print "locker has some zero length data\n";
    }

The bool and ! operations are overloaded and allow to check if the $locker object contains the data at least one byte long.

concatenation

    my $kv = "password:". $locker;

The concatenation operator . is overloaded and allows to create a new Data::BytesLocker object that is a result of joining the data together.

repetition

    my $tripled_data = $locker x 3;

The repetition operator x is overloaded and allow to create a new Data::BytesLocker object that is a result of repeating the protected data specified number of times.

SEE ALSO

AUTHOR

Alex J. G. Burzyński <ajgb@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Alex J. G. Burzyński <ajgb@cpan.org>.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.