I need to implement a couple of functions which comply with the following:
function genKey: given a string q (this may be a MD5 or SHA hash) and a seed string, the function must generate a new string p
function checkKey: this function must return true if a string p was generated from string q (using the previous function)
In seudo-code the above would be something lie this:
p=genKey(q,seed) ; // generate string p from q and seed
checkKey(p,q)==true ; // if p was generated from q, then this must return true. False otherwise.
Does anyone know about existing algorithms that do such thing??
I can implement the algorithms myself if there are no known implementations for PHP, so what I'm really asking is for the procedure to accomplish this.
It sounds like you might be trying to describe a MAC.
A message authentication code takes a message digest, a secret, and message. The secret and data are hashed together, and the result is included with the message.
A message recipient who knows the secret can perform the same digest computation, and compare his MAC to the one that accompanied the received message. If they are equal, he can trust that the message was not altered.
Given your comments, I understand now that you are working with asymmetric keys, rather than a secret key, which would be used in a MAC.
However, there's still a little confusion. Normally, a private signature key is kept secret by its owner, which in this case seems to be the client. A client can cryptographically prove that they possess a private key that corresponds to a public key without disclosing the private key.
Using digital signatures, you can do something like this:
p = genKey(pvt, seed)
checkKey(pub, p)
Here, pvt is the server's private key, pub is its public key. The seed parameter is the data that gets signed. If I understand your application (which I doubt), seed should be the client identifier. Then p is a message format that bundles seed and its signature together. Your question is confusing because q is used both generating and verifying p—like a shared secret.
However, there's nothing in this scheme (or in the MAC scheme) to stop one client from using another's value of p. All you can do with such a technique is to ensure that the message content has not been altered. For example, if the message is something like "clientID=Alice,IPAddress=192.168.1.1", you can make sure that Mallory didn't substitute his own IP address for Alice's.
But if the message is just "clientID=Alice", you can't stop Alice from giving Bob her tamper-proof message (in return for splitting the cost of a license), and you can't control whether Mallory hacks into Alice's box and steals the message.
By the way, if message integrity really is all you need, and you can easily share a secret between the sender and a receiver, MACs have some nice advantages over public-key cryptography, such as much smaller message size and faster performance.
Outline the threats you are trying to defend against. Cryptography is hard. Devising untried schemes usually ends badly.
For this discussion let p == hash(q)
You could then easily use OpenSSL with php to sign 'p' with a private key and keep that signature along side 'p'.
It is then simple to use the OpenSSL verify function to check 'p' against the signature using the public key that is paired with your private key. In this case 'p' would be the data to verify.
See openssl_sign and openssl_verify for info and examples of using OpenSSL in php to sign and verify data:
You could try something like this:
$p = genKey($q,rand());
checkKey($p,$q) == true;
function genkey($q,$seed)
{
if(array_search($_SESSION['seeds'],$seed) === FALSE)
{
$_SESSION['seeds'][] = $seed;
}
return hash("sha512", $q . $seed);
}
function checkKey($p,$q)
{
$returnVal = false;
foreach($_SESSION['seeds'] AS $s)
{
if(hash("sha512", $q . $s) == $p)
{
$returnVal = true;
break;
}
}
return $returnVal;
}
Of course, I would recommend something else for storage of valid seeds, but this is just a proof of concept.
You can use either crypt(), hash(), or md5().
Edit:
I don't mind the downvote. Looking at the complexity of the other answers, I must have misinterpreted the question. But it would be nice if people would accompany their downvotes with a comment so that I at least know what is wrong with my answer.
What I gleaned from the question is that the questioner wants to implement something like:
function genKey($q, $seed) {
// assuming $seed is a properly formatted md5/sha salt
return crypt($q, $seed);
}
function checkKey($p, $q, $seed) {
return ($p == genKey($q, $seed));
}
Am I wrong in thinking that any one of PHP's one-way encryption functions can be used to accomplish this?
Okay, what you are referring to is a principle of public key cryptography, specifically creating signatures. It runs along complex mathematic principles (which you would have to implement).
In basic RSA signatures, you create a signature s by applying the following math:
s = m^d % n
Where m is the message (or string q for you), d is the private key, n is the modulus (shared between the private and public key).
Then it can be verified by this math:
m = s^e % n
Where e is the public key.
Of course the keys must be generated to a specific mathematical standard for this to work. And sometimes the keys get to enormous sizes. Again, all of this is done via integers, so you would have to convert the text into decimal and back.
For more information, and more math, check out this page.
EDIT: I thought I should mention why you would want to use public key cryptography. Basically, it protects against two things: the message is verifiable and cannot be faked.
Will the checking side have access to seed? If seed can be kept secure on the checking side (like on a web app) you could easily do something like HTTP Digest authentication does using MD5.
Pseudocode:
function genKey(q) {
p = md5sum(q . ':' . seed);
return p;
}
function checkKey(p,q) {
return md5sum(q . ':' . seed) == p;
}
If you need a second party to verify the "signature" then you'll probably just want to use PKI.
Related
Similar to the question in the "Making RSA keys from a password in python
" question, I want to to repeatedly create the same pair of RSA keys using a given password.
The question mentioned above has this code as the answer:
from Crypto.Protocol.KDF import PBKDF2
from Crypto.PublicKey import RSA
password = "swordfish" # for testing
salt = "yourAppName" # replace with random salt if you can store one
master_key = PBKDF2(password, salt, count=10000) # bigger count = better
def my_rand(n):
# kluge: use PBKDF2 with count=1 and incrementing salt as deterministic PRNG
my_rand.counter += 1
return PBKDF2(master_key, "my_rand:%d" % my_rand.counter, dkLen=n, count=1)
my_rand.counter = 0
RSA_key = RSA.generate(2048, randfunc=my_rand)
I tried to replicate this code in PHP like this:
$password = "swordfish";
$salt = "yourAppName";
$master_key = hash_pbkdf2("sha256", $password, $salt, 10, 256);
$counter = 0;
function my_rand($n) {
$counter++;
return hash_pbkdf2("sha256", $master_key, "my_rand:" . $counter, 10, 256);
}
$RSA_key = openssl_pkey_new(???);
But I now don't know how to replicate the RSA key generator using the custom random function as the PHP openssl_pkey_new function and the phpseclib both do not have an option to add a custom random function.
What do I have to do to repeatedly generate the same RSA key pair from a given password?
TL;DR: you should not generate a RSA key pair from a given password
It looks like both OpenSSL nor phpseclib have been created with this in mind. This is not surprising as there is much to be said against the solution itself. Furthermore, there are many implementation issues, especially for RSA.
If you would still go ahead to implement this ill advised scheme, then you should look up a PHP implementation for the key generation and then store that with your solution. The reason for this is that otherwise the random number generation, prime finding or RSA key generation method may change internally (when you upgrade the library) and produce a different key pair. I would not use this solution on Java even though it does let you insert your own RNG to allow for deterministic key pair generation, which is the technical term of what you're trying to do.
There are other ways of having users keep their private key secure, such as password based encryption of the key. This requires storage of the encrypted key, but at least it is common practice and has much less reason to fail.
You'll need some kind of hybrid cryptography if you want to encrypt arbitrarily sized messages - just like you'd have to with any other kind of RSA key pair, really.
Note that the situation is slightly better for Elliptic Curve cryptography. You could combine PBKDF2 with SHA-256 to create a private key, and then derive the public key by performing point multiplication of base point G with that private key value. This is much less likely to fail. You could use ECIES to encrypt with Elliptic Curves, as EC does not have a way to directly encrypt messages or keys.
However, you'd still have the problem that you can never change the password, and that you have to use a constant salt to always generate the same private key and public keys. Because of this, I would still not recommend the scheme, as it allows offline, multi-target attacks to find the password, using the public key value or a ciphertext.
To be sure that the password cannot be found it needs to be really strong - so strong that you probably have to store it somewhere.
I'm required to create a provably-fair (deterministic & seeded) cryptographically secure (CS) random number generator in PHP. We are running PHP 5 and PHP 7 isn't really an option right now. However, I found a polyfill for PHP 7's new CS functions so I've implemented that solution (https://github.com/paragonie/random_compat).
I thought that srand() could be used to seed random_int(), but now I'm not certain if that is the case. Can a CSPRNG even be seeded? If it can be seeded, will the output be deterministic (same random result, given same seed)?
Here is my code:
require_once($_SERVER['DOCUMENT_ROOT']."/lib/assets/random_compat/lib/random.php");
$seed_a = 8138707157292429635;
$seed_b = 'JuxJ1XLnBKk7gPASR80hJfq5Ey8QWEIc8Bt';
class CSPRNG{
private static $RNGseed = 0;
public function generate_seed_a(){
return random_int(0, PHP_INT_MAX);
}
public function generate_seed_b($length = 35){
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randomString = '';
for($i = 0; $i < $length; $i++){
$randomString .= $characters[random_int(0, strlen($characters) - 1)];
}
return $randomString;
}
public function seed($s = 0) {
if($s == 0){
$this->RNGseed = $this->generate_seed_a();
}else{
$this->RNGseed = $s;
}
srand($this->RNGseed);
}
public function generate_random_integer($min=0, $max=PHP_INT_MAX, $pad_zeros = true){
if($this->RNGseed == 0){
$this->seed();
}
$rnd_num = random_int($min, $max);
if($pad_zeros == true){
$num_digits = strlen((string)$max);
$format_str = "%0".$num_digits."d";
return sprintf($format_str, $rnd_num);
}else{
return $rnd_num;
}
}
public function drawing_numbers($seed_a, $num_of_balls = 6){
$this->seed($seed_a);
$draw_numbers = array();
for($i = 0; $i < $num_of_balls; $i++) {
$number = ($this->generate_random_integer(1, 49));
if(in_array($number, $draw_numbers)){
$i = $i-1;
}else{
array_push($draw_numbers, $number);
}
}
sort($draw_numbers);
return $draw_numbers;
}
}
$CSPRNG= new CSPRNG();
echo '<p>Seed A: '.$seed_a.'</p>';
echo '<p>Seed B: '.$seed_b.'</p>';
$hash = hash('sha1', $seed_a.$seed_b);
echo '<p>Hash: '.$hash.'</p>';
$drawNumbers = $CSPRNG->drawing_numbers($seed_a);
$draw_str = implode("-", $drawNumbers);
echo "<br>Drawing: $draw_str<br>";
When this code is run, the Drawing ($draw_str) should be the same on each run, but it is not.
To prove that the drawing is fair, a seed (Seed A) is chosen before the winning number is picked and shown. Another random number is generated as well (Seed B). Seed B is used as a salt and combined with Seed A and the result is hashed. This hash is shown to the user prior to the drawing. They would also be provided with the source code so that when the winning number is picked, both seeds are revealed. They can verify that the hash matches and everything was done fairly.
Duskwuff asks:
How do you intend to prove that the seed was chosen fairly? A suspicious user can easily claim that you picked a seed that would result in a favorable outcome for specific users, or that you revealed the seed to specific users ahead of time.
Before you investigate solutions, what exactly is the problem you are trying to solve? What is your threat model?
It sounds like you want SeedSpring (version 0.3.0 supports PHP 5.6).
$prng = new \ParagonIE\SeedSpring\SeedSpring('JuxJ1XLnBKk7gPAS');
$byte = $prng->getBytes(16);
\var_dump(bin2hex($byte));
This should always return:
string(32) "76482c186f7c5d1cb3f895e044e3c649"
The numbers should be unbiased, but since it's based off a pre-shared seed, it is not, by strict definition, cryptographically secure.
Keep in mind that SeedSpring was created as a toy implementation/proof of concept rather than an official Paragon Initiative Enterprises open source security solution, so feel free to fork it and tweak it to suit your purposes. (I doubt our branch will ever reach a "stable 1.0.0 release").
(Also, if you're going to accept/award the bounty to any of these answers, Aaron Toponce's answer is more correct. Encrypting the nonce with ECB mode is more performant than encrypting a long stream of NUL bytes with AES-CTR, for approximately the same security benefit. This is one of the extremely rare occasions that ECB mode is okay.)
First, you shouldn't be implementing your own userspace CSPRNG. The operating system you have PHP 5 installed on already ships a CSPRNG, and you should be using that for all your randomness, unless you know you can use it, or performance is a concern. You should be using random_int(), random_bytes(), or openssl_random_pseudo_bytes().
However, if you must implement a userspace CSPRNG, then this can be done by simply using an AES library (E.G.: libsodium), and encrypting a counter. Psuedocode would be:
Uint-128 n = 0;
while true:
output = AES-ECB(key, n);
n++;
They AES key, in this case, needs sufficient entropy to withstand a sophisticated attack, or the security of your userspace CSPRNG falls apart, of course. The key could be the bcrypt() of a user-supplied password.
Provided your counter represented as a 128-bit unsigned integer is always unique, you will always get a unique output every time the generator is "seeded" with a new counter. If it's seeded with a previously used counter, but a different key, then the output will also be different. The best case scenario, would be a changing key and a changing counter every time the generator is called.
You may be tempted to use high precision timestamp, such as using microsecond accuracy, in your counter. This is fine, except you run the risk of someone or something manipulating the system clock. As such, if the clock can be manipulated, then the CSPRNG generator can be compromised. You're best off providing a new key every time you call the generator, and start encrypting with a 128-bit zero.
Also, notice that we're using ECB mode with AES. Don't freak out. ECB has problems with maintaining structure in the ciphertext that the plaintext provides. In general terms, you should not use ECB mode. However, with 128-bits of data, you will only be encrypting a single ECB block, so there will be no leak of structured data. ECB is preferred over CTR for a userspace CSPRNG, as you don't have to keep track of a key, a counter object, and the data to be encrypted. Only a key and the data are needed. Just make sure you are never encrypting more than 128-bits of data, and you'll never need more than 1 block.
Can a CSPRNG even be seeded?
Yes, and it should always be seeded. If you look at your GNU/Linux operating system, you'll likely notice a file in /var/lib/urandom/random-seed. When the operating system shuts down, it creates that file from the CSPRNG. On next boot, this file is used to seed the kernelspace CSPRNG to prevent reusing previous state of the generator. On every shutdown, that file should change.
If it can be seeded, will the output be deterministic (same random result, given same seed)?
Yes. Provided the same seed, key, etc., the output is deterministic, so the output will be the same. If one of your variables changes, then the output will be different. This is why on each call of the generator should be rekeyed.
I am coding with openssl, and I would like to know, why the openssl_sign function, gives a diferent result than openssl_private_encrypt in a logical sense.
Specifically with openssl_sign:
$fp = fopen("i.pem", "r"); //i.pem is the private key file
$priv_key = fread($fp, 8192);
fclose($fp);
$pkeyid = openssl_get_privatekey($priv_key);
$data="f2e140eb-2b09-44ab-8504-87b25d81914c";
openssl_sign($data, $signature, $pkeyid);
$reto22 = base64_encode($signature); //this gives UNmlEfwISea9hoGfiwdM.......
Specifically with openssl_private_encrypt:
$llave_priv = file_get_contents("i.pem"); //i.pem is the private key file
$plaintext = "f2e140eb-2b09-44ab-8504-87b25d81914c";
openssl_private_encrypt($plaintext, $encrypted, $llave_priv);
$reto = base64_encode($encrypted); //this gives ugSMAsCQlIKIlQ17exIvSEqkA60.......
Why is reto22 is different than $reto? they should be the same, shouldn't they?
encrypt with priv key = sign, as far as I know
thanks for clarifying
mario
Generally speaking, Encryption in public key systems is performed with the public key (so that the private key can be used to decrypt it) while signing is done with the private key (so that the public key can be used to verify it)
Signatures with openssl involve encrypting the hash of the message. So even if the same key is used, the output will be different, because while openssl_private_encrypt does encrypt with the private key like you would in a signature scheme, it doesn't hash the message, or (possibly, not certain) perform the same padding that a signature scheme would perform.
Stick with openssl_sign, as it will be more efficient and less prone to potential side channel attacks than rolling your own signature scheme.
See this answer:
https://stackoverflow.com/a/2706636/1359088
It's helpful because it explains that openssl_sign performs a hash on the data internally before returning the signature, whereas openssl_private_encrypt requires you to perform the hash yourself. I understand conceptually why you want to openssl_sign (because encrypting is normally done with the public key, whereas signing is with the private key), but I was going crazy because SSCrypto has a method named sign that was returning the same data as openssl_private_encrypt rather than openssl_sign, and that answer above helped me to sort it out. I'm signing a message in an iPhone app, which will be verified by PHP; I'm using SSCrypto for the signing and openssl_verify to verify, but I'm testing with openssl_sign because I need the data to be identical to work.
You can use flour to make bread and you can use flour to make a roux. Yet bread isn't a roux and a roux isn't bread.
Similarly, encryption isn't signing and signing isn't encryption.
I was wondering how mobile authenticators work (like Battle.net, rift, some banks have one, etc.), so I can make one for my own site (just for fun).
I understand the basics: authenticator has code related to the phone and code related to the website. Users enters the phone code on the website. Can then generate a token related (using the phone and website code).
I'm just wondering how the tokens are created. Is there a standard algorithm for this? How does the algorithm work? Any existing PHP libraries that can do something like this (as an example)?
Have a look at Google Authenticator. There are already iPhone, Android and Blackberry apps for that and it's an established protocol.
They have implemented it as an open-source PAM module which you may be able to use with the PECL PAM package.
There is a pure PHP version but I haven't used that so can't vouch for it.
The spec isn't that complex so you could probably implement it yourself, especially if you converted the C module. The specification linked there explains its working in full detail.
Edit: I guess to answer the original question, that's an RFC, so it's somewhat standardised, and it's a fully open specification and the tools to use it are fully open-source. The protocols are known as HOTP and TOTP. The former is HMAC based on a counter (so the nth password is used) whereas the latter is time-based (so the password cycles every 30 seconds).
Concerning the Blizzad Battle.Net authenticator, you can find an open source implementation in PHP : https://github.com/krtek4/php-bma
The implementation is used to provide a online authentication service for Battle.Net : https://authenticator.me
If you want to do something like it for your website, it's pretty simple. The only thing to share between the server and client part are the secret generated by the server. So when a client is requesting for a new secret, just store it and you will be able to compute the code at any moment to compare with what is sent to you.
I implemented this once. I use a 4 digit key with a subset of characters (notice that potentially confusing characters like 0oO and l1L are removed. I used 4 characters because the potential space of 4 digits from the characters set was larger than the 6 digits of an RSA key.
Anyway, I let the user log in with their username and password. If that is correct, generate a key and send it to the phone and save it in the session and show the user the next page, which requires the key be entered. The user gets the 4 digit key from their phone and enters it into the page. Then check what they entered against the session-saved key and there you go.
Some handy features to have: make the key expire after a few minutes, but long enough that text message delays don't make it impossible. Make it expire after a few bad tries. Give the users a link to resend the key or to send a new key.
//pick a random 4 digit string
$chars = "abcdefghjkrstwxyzABCDEFGHJKRSTWXYZ23456789";
$key = "";
for($i=0;$i<4;$i++){
//here, rand is used, but any generator could be used
//to choose the characters.
$key .= $chars[rand(0,strlen($chars)-1)];
}
//save it to the session
$_SESSION['test']['KEY'] = $key;
If it were me I'd go with generating a hash based on the previously used hash and a common nonce, the tricky bit would be keeping the two systems in sync. e.g.
<?php
class otp {
var $salt;
var $previous_hash;
var $user_id;
function __construct($user_id)
{
$this->user_id=$user_id;
list($this->$salt, $this->$previous_hash)
=unserialize(file_get_contents(BASE_PATH . $user_id));
}
function authenticate($submitted_otp)
{
if (!$this->salt) {
// user does not exist
return false;
}
$new_hash=$this->previous_hash;
// allow for the sequence to get out of sync by 5 steps....
for ($x=0; $x<5; $x++) {
$new_hash=md5($this->salt, $new_hash);
if ($new_hash===$submitted_otp) {
$this->update_token($new_hash);
return true;
}
}
// none of the next N iterations of the local password match
return false;
}
function create_user($user_id, $salt, $init_hash)
{
return file_put_contents(BASE_PATH . $user_id, array($salt, $init_hash));
}
function update_token($new_hash)
{
file_put_contents(BASE_PATH . $user_id, array($this->salt, $new_hash));
}
}
Of course, in practice you probably wouldn't want to use a whole 32 char md5 hash (just, say, the first 6 characters, and applying cleansing such as changing 'S' to '5' etc).
I was looking for an effective algorithm that can give me an accurate idea of how strong a password is.
I found that several different websites use several different algorithms as I get different password strength ratings on different websites.
This has grown to my general brain dump of best practices for working with passwords in PHP/MySQL.
The ideas presented here are generally not my own, but the best of what I've found to date.
Ensure you are using SSL for all operations involving user information. All pages that involve these forms should check they are being called via HTTPS, and refuse to work otherwise.
You can eliminate most attacks by simply limiting the number of failed logins allowed.
Allow for relatively weak passwords, but store the number of failed logins per user and require a captcha or password verification by email if you exceed it. I set my max failures to 5.
Presenting login failures to the user needs to be carefully thought out as to not provide information to attackers.
A failed login due to a non existent user should return the same message as a failed login due to a bad password. Providing a different message will allow attackers to determine valid user logins.
Also make sure you return exactly the same message in the event of a failure for too many logins with a valid password, and a failure with too many logins and a bad password. Providing a different message will allow attackers to determine valid user passwords. A fair number of users when forced to reset their password will simply put it back to what it was.
Unfortunately limiting the number of logins allowed per IP address is not practical. Several providers such as AOL and most companies proxy their web requests. Imposing this limit will effectively eliminate these users.
I've found checking for dictionary words before submit to be inefficient as either you have to send a dictionary to the client in javascript, or send an ajax request per field change. I did this for a while and it worked ok, but didn't like the traffic it generated.
Checking for inherently weak passwords minus dictionary words IS practical client side with some simple javascript.
After submit, I check for dictionary words, and username containing password and vice versa server side. Very good dictionaries are readily downloadable and the testing against them is simple. One gotcha here is that to test for a dictionary word, you need to send a query against the database, which again contains the password. The way I got around this was to encrypt my dictionary before hand with a simple encryption and end positioned SALT and then test for the encrypted password. Not ideal, but better than plain text and only on the wire for people on your physical machines and subnet.
Once you are happy with the password they have picked encrypt it with PHP first, then store. The following password encryption function is not my idea either, but solves a number of problems. Encrypting within PHP prevents people on a shared server from intercepting your unencrypted passwords. Adding something per user that won't change (I use email as this is the username for my sites) and add a hash (SALT is a short constant string I change per site) increases resistance to attacks. Because the SALT is located within the password, and the password can be any length, it becomes almost impossible to attack this with a rainbow table.
Alternately it also means that people can't change their email and you can't change the SALT without invalidating everyone's password though.
EDIT: I would now recommend using PhPass instead of my roll your own function here, or just forget user logins altogether and use OpenID instead.
function password_crypt($email,$toHash) {
$password = str_split($toHash,(strlen($toHash)/2)+1);
return hash('sha256', $email.$password[0].SALT.$password[1]);
}
My Jqueryish client side password meter. Target should be a div. It's width will change between 0 and 100 and background color will change based on the classes denoted in the script. Again mostly stolen from other things I've found:
$.updatePasswordMeter = function(password,username,target) {
$.updatePasswordMeter._checkRepetition = function(pLen,str) {
res = ""
for ( i=0; i<str.length ; i++ ) {
repeated=true;
for (j=0;j < pLen && (j+i+pLen) < str.length;j++)
repeated=repeated && (str.charAt(j+i)==str.charAt(j+i+pLen));
if (j<pLen) repeated=false;
if (repeated) {
i+=pLen-1;
repeated=false;
}
else {
res+=str.charAt(i);
};
};
return res;
};
var score = 0;
var r_class = 'weak-password';
//password < 4
if (password.length < 4 || password.toLowerCase()==username.toLowerCase()) {
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
return true;
}
//password length
score += password.length * 4;
score += ( $.updatePasswordMeter._checkRepetition(1,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(2,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(3,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(4,password).length - password.length ) * 1;
//password has 3 numbers
if (password.match(/(.*[0-9].*[0-9].*[0-9])/)) score += 5;
//password has 2 symbols
if (password.match(/(.*[!,#,#,$,%,^,&,*,?,_,~].*[!,#,#,$,%,^,&,*,?,_,~])/)) score += 5;
//password has Upper and Lower chars
if (password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)) score += 10;
//password has number and chars
if (password.match(/([a-zA-Z])/) && password.match(/([0-9])/)) score += 15;
//
//password has number and symbol
if (password.match(/([!,#,#,$,%,^,&,*,?,_,~])/) && password.match(/([0-9])/)) score += 15;
//password has char and symbol
if (password.match(/([!,#,#,$,%,^,&,*,?,_,~])/) && password.match(/([a-zA-Z])/)) score += 15;
//password is just a nubers or chars
if (password.match(/^\w+$/) || password.match(/^\d+$/) ) score -= 10;
//verifing 0 < score < 100
score = score * 2;
if ( score < 0 ) score = 0;
if ( score > 100 ) score = 100;
if (score > 25 ) r_class = 'okay-password';
if (score > 50 ) r_class = 'good-password';
if (score > 75 ) r_class = 'strong-password';
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
return true;
};
Fundamentally you want to prevent to major types of attacks
Dictionary attacks
Brute force attacks
To prevent the first, you want to consider passwords containing common words weak. To prevent the second, you want to encourage passwords of reasonable length (8+ characters is common) and with a reasonably large character set (include letters, numbers, and special characters). If you consider lower case and upper case letters to be different, that increases the character set substantially. However, this creates a usability issue for some user communities so you need to balance that consideration.
A quick google search turned up solutions that account for brute force attacks (complex password) but not for dictionary attacks. PHP Password Strength Meter from this list of strength checkers runs the check server-side, so it could be extended to check a dictionary.
EDIT:
By the way... you should also limit the number of login attempts per user. This will make both types of attacks less likely. Effective but not-user-friendly is to lock an account after X bad attempts and require a password reset. More user friendly but more effort is to throttle time between login attempts. You can also require CAPTCHA after the first few login attempts (which is something that Stack Overflow requires after too many edits, or for very new users).
Basically you probably want to use Regular Expressions to validate the length and complexity of the password.
A good example doing this using javascript can be found here:
http://marketingtechblog.com/programming/javascript-password-strength/
As Daren Schwenke pointed it out, you'd better work on the security yourself and not put this in the user hands.
But it's good to provide some hints to the user of how strong his password is, because the best way to get a password is still social engenering.
So you can hack a little client side script that checks the user password strenght as a courtesy indicator, in real time. It blocks nothing, but gives him a good warm feeling when it turns green :-)
Basically what you must check is commom sense : check if the password contains letters, numbers and non alphabetical caracters, in a reasonable quantity.
You can hack your own algo very easily : just make 10 / 10 mark :
0 is a zero lenght password;
+2 for every 8 caracters in the password (15 is supposed to be a safe lenght);
+1 for the use of a letter, +2 for the use of 2 letters;
+1 for the use of a number, +2 for the use of 2 numbers;
+1 for the use of a non alphabetical caracters, +2 for 2.
You don't need to check for godlike passwords (are there capitalized letters, where are positioned the special caracters, etc), your users are not in the bank / military / secret service / monthy python movies industry, are they ?
You can code that in an hour in without crazy javascript skills.
And anyway, valid the password and move all the security code on the server side. If you can delegate authentification (e.g : open ID), even better.
Don't Roll-Your-Own!
Cryptography experts discourage roll-your-own cryptography for reasons that should be obvious.
For the very same reasons, one should not attempt to roll his own solution to the problem of measuring a password's strength; it is very much a cryptographic problem.
Don't get into the ugly business of authoring some massive regular expression for this purpose; you will likely fail to account for several factors that influence a password's overall strength.
It's a Difficult Problem
There is considerable difficulty inherent to the problem of measuring a password's strength. The more research I perform on this subject, the more I realize that this is a "unidirectional" problem; that is, one cannot measure the "difficulty" (computational cost) of cracking a password efficiently. Rather, it is more efficient to provide complexity requirements and measure the password's ability to meet them.
When we consider the problem logically, a "crackability index" doesn't make much sense, as convenient as it sounds. There are so many factors that drive the calculation, most of which relate to the computational resources devoted to the cracking process, so as to be impractical.
Imagine pitting John the Ripper (or a similar tool) against the password in question; it might take days to crack a decent password, months to crack a good password, and until the sun burns-out to crack an exceptional password. This is not a practical means by which to measure password strength.
Approaching the problem from the other direction is far more manageable: if we supply a set of complexity requirements, it's possible to judge the relative strength of a password very quickly. Obviously, the supplied complexity requirements must evolve over time, but there are far fewer variables for which to account if we approach the problem in this way.
A Viable Solution
There is a standalone utility available from Openwall entitled passwdqc (presumably, standing for Password Quality Checker). Openwall developer, Solar Designer, does appear to be a bona fide cryptography expert (his works speak for themselves), and so is qualified to author such a tool.
For my particular use-case, this is a far more attractive solution than using an ill-conceived JavaScript snippet living in some dark corner of the Web.
Establishing parameters for your particular needs is the hardest part. The implementation is the easy part.
A Practical Example
I offer a simple implementation in PHP to provide a jump-start. Standard disclaimers apply.
This example assumes that we're feeding an entire list of passwords to the PHP script. It goes without saying that if you are doing this with real passwords (e.g., those dumped out of a password manager), extreme caution should be exercised with regard to password-handling. Simply writing the unencrypted password dump to disk jeopardizes the security of your passwords!
passwords.csv:
"Title","Password"
"My Test Password","password123"
"Your Test Password","123456!!!"
"A strong password","NFYbCoHC5S7dngitqCD53tvQkAu3dais"
password-check.php:
<?php
//A few handy examples from other users:
//http://php.net/manual/en/function.str-getcsv.php#117692
$csv = array_map('str_getcsv', file('passwords.csv'), [',']);
array_walk($csv, function(&$a) use ($csv) {
$a = array_combine($csv[0], $a);
});
//Remove column header.
array_shift($csv);
//Define report column headers.
$results[] = [
'Title',
'Result',
'Exit Code',
];
$i = 1;
foreach ($csv as $p) {
$row['title'] = $p['Title'];
//If the value contains a space, it's considered a passphrase.
$isPassphrase = stristr($p['Password'], ' ') !== false ? true : false;
$cmd = 'echo ' . escapeshellarg($p['Password']) . ' | pwqcheck -1 min=32,24,22,20,16 max=128';
if ($isPassphrase) {
$cmd .= ' passphrase=3';
}
else {
$cmd .= ' passphrase=0';
}
$output = null;
$exitCode = null;
$stdOut = exec($cmd, $output, $exitCode);
//Exit code 0 represents an unacceptable password (not an error).
//Exit code 1 represents an acceptable password (it meets the criteria).
if ($exitCode === 0 || $exitCode === 1) {
$row['result'] = trim($stdOut);
$row['exitCode'] = $exitCode;
}
else {
$row['result'] = 'An error occurred while calling pwqcheck';
$row['exitCode'] = null;
}
$results[$i] = $row;
$i++;
}
$reportFile = 'report.csv';
$fp = #fopen($reportFile, 'w');
if ($fp !== false) {
foreach ($results as $p) {
fputcsv($fp, $p);
}
fclose($fp);
}
else {
die($reportFile . ' could not be opened for writing (destination is not writable or file is in use)');
}
exit;
Resultant report.csv:
Title,Result,"Exit Code"
"My Test Password","Bad passphrase (too short)",1
"Your Test Password","Bad passphrase (too short)",1
"A strong password",OK,0
Wrapping-Up
I have yet to find a more thorough solution on the Web; needless to say, I welcome any other recommendations.
Obviously, this approach is not ideal for certain use-cases (e.g., a "password strength meter" implemented "client-side"). Even so, it would be trivial to make an AJAX call to a server-side resource that returns a pass/fail response using the approach outlined above, but such an approach should assume the potential for abuse (e.g., DoS attacks) and would require secure communication between client and server, as well as acceptance of the risks associated with transmitting the un-hashed password.
I can't think of a specific algorithm to check the strengh of a password. What we do is we define several criterion and when the password respect a criteria, we add 1 to its score. When the password reach a threshold, the password is strong. Otherwise it is weak.
You can define many different level of strengh if with different throeshold, or you can define different value for a specific criteria. For example, if a password has 5 character, we add 1, but if it got 10, then we add 2.
here is a list of criterion to check for
Length (8 to 12 is ok, more is better)
Contains lowercase letter
Contains uppercase letter
The upper case letter is NOT the first one.
Contains number
Contains symbols
the last character is NOT a human like symbol (ex : . or !)
Does not look like a dictionnary word. Some wise password crack contains library of word and letter substitutes (like Library --> L1br#ry )
Hope that help.