Crypt::Password - Unix-style, Variously Hashed Passwords


Crypt-Password documentation Contained in the Crypt-Password distribution.

Index


Code Index:

NAME

Top

Crypt::Password - Unix-style, Variously Hashed Passwords

SYNOPSIS

Top

 use Crypt::Password;

 my $hashed = password("password");

 $user->set_password($hashed);

 if ($user->get_password eq password($from_client)) {
     # authenticated
 }

 # This is called Modular Crypt Format.

 if (password($from_database)->check($from_user)) {
     # authenticated
 }

 # Default algorithm, supplied salt:
 my $hashed = password("password", "salt");

 # md5, no salt:
 my $hashed = password("password", "", "md5");

 # sha512, invented salt: 
 my $hashed = password("password", undef, "sha512");

DESCRIPTION

Top

This is just a wrapper for perl's crypt(), which can do everything you would probably want to do to store a password, but this is to make usage easier.

The object stringifies to the return string of the crypt() function, which is usually (see KNOWN ISSUES) in Modular Crypt Format:

 # scalar($hashed):
 #    v digest   v hash ->
 #   $5$%RK2BU%L$aFZd1/4Gpko/sJZ8Oh.ZHg9UvxCjkH1YYoLZI6tw7K8
 #      ^ salt ^

That you can store, etc, retrieve then give it to password() again to ->check($given_password) or string compare to the output of a new password($given_password).

If the given string is already hashed it is assumed to be okay to use it as is. This means users can supply pre-hashed passwords to you.

FUNCTIONS

Top

password ( $password [, $salt [, $algorithm]] )

Constructs a Crypt::Password object.

METHODS

Top

check ( $another_password )

Checks the given password hashes the same as that this object represents.

hash

Returns the hash.

salt

Returns the salt.

algorithm

Returns the algorithm by name.

algorithm_arg

Returns the algorithm as it is represented in the Modular Crypt Formatted output of crypt(3).

KNOWN ISSUES

Top

Cryptographic functionality depends greatly on your local glibc's crypt(3). Old Linux may not support sha*, many other platforms only support md5, or that and Blowfish, etc.

SUPPORT, SOURCE

Top

If you have a problem, submit a test case via a fork of the github repo.

 http://github.com/st3vil/Crypt-Password

AUTHOR AND LICENCE

Top

Code by Steve Eirium, nostrasteve@gmail.com, idea by Sam Vilain, sam.vilain@catalyst.net.nz. Development commissioned by NZ Registry Services.

Copyright 2009, NZ Registry Services. This module is licensed under the Artistic License v2.0, which permits relicensing under other Free Software licenses.

SEE ALSO

Top

Digest::SHA, Authen::Passphrase, Crypt::SaltedHash


Crypt-Password documentation Contained in the Crypt-Password distribution.

package Crypt::Password;
use Exporter 'import';
@EXPORT = ('password');
our $VERSION = "0.05";

use Carp;

use overload
    '""' => \&crypt,
    'eq' => \&crypt,
    'nomethod' => \&crypt;

# from libc6 crypt/crypt-entry.c
our %alg_to_magic = (
    md5 => '$1$',
    sha256 => '$5$',
    sha512 => '$6$',
);

our %magic_to_alg = reverse %alg_to_magic;

sub new {
    shift;
    password(@_);
}

sub password {
    my $self = bless {}, __PACKAGE__;
    
    $self->input(shift);
    
    unless ($self->{crypted}) {
        $self->salt(shift);
        
        $self->algorithm(shift); 
        
        $self->crypt();
    }

    $self
}

sub crypt {
    my $self = shift;
    
    $self->{crypted} ||= $self->_crypt
}



sub input {
    my $self = shift;
    $self->{input} = shift;
    if ($self->_looks_crypted($self->{input})) {
        $self->{crypted} = delete $self->{input}
    }
}

sub salt {
    my $self = shift;
    my $provided = shift;
    if (defined $provided) {
        $self->{salt} = $provided
    }
    else {
        $self->{salt} ||= do {
            if ($self->{crypted}) {
                (split /\$/, $self->{crypted})[2]
            }
            else {
                $self->_invent_salt()
            }
        };
    }
}

sub algorithm {
    my $self = shift;
    $alg = shift;
    if ($alg) {
        if (exists $alg_to_magic{lc $alg}) {
            $self->{algorithm_magic} = $alg_to_magic{lc $alg};
            $self->{algorithm} = lc $alg;
        }
        else {
            $self->{algorithm_magic} = $alg;
            $self->{algorithm} = $magic_to_alg{lc $alg};
        }
    }
    elsif (!$self->{algorithm}) {
        $self->algorithm($self->_default_algorithm)
    }
    else {
        $self->{algorithm}
    }
}

sub _crypt {
    my $self = shift;
    
    defined $self->{input} || croak "no input!";
    $self->{algorithm_magic} || croak "no algorithm!";
    defined $self->{salt} || croak "invalid salt!";
    
    CORE::crypt(delete $self->{input}, $self->{algorithm_magic}.$self->{salt})
}

sub check {
    my $self = shift;
    my $plaintext = shift;
    
    CORE::crypt($plaintext, $self) eq "$self";
}



our @valid_salt = ( "a".."z", "A".."Z", "0".."9", qw(/ \ ! @ % ^), "#" );

sub _invent_salt {
    join "", map { $valid_salt[rand(@valid_salt)] } 1..8;
}

sub _looks_crypted {
    my $self = shift;
    my $string = shift;
    $string && $string =~ m{^\$\d+\$.*\$.+$}
}

sub _default_algorithm {
    "sha256"
}

1;

__END__