Brad Fitzpatrick (bradfitz) wrote in lj_dev,
Brad Fitzpatrick

challenge/response for protocol handlers

I just committed a new protocol method (for both XML-RPC and "Flat" flavors) called "getchallenge" to get a challenge token to be later used in other protocol modes instead of sending the clear-text "password" or the just-as-bad (replayable) "hpassword" (md5'ed password).

Here are the flat results of the getchallenge method: (it takes no parameters or auth)


Ignore "auth_scheme = c0" for now. It might be used in the future if we implement other auth schemes or change or auth scheme. In that case we'd add a new capabilities exchange: client would say, "I know c0 and c1". Server would then say, "Use c1, it's the best." But it's just c0 for now, and always will be as long as you don't specify what you know. (if you want to be paranoid, you can make your client check for "c0")

(expire_time - server_time) is how long you have to use that challenge. (currently always 60 seconds) You'll currently get an "invalid password" error if you challenge expires, but we might change that to "challenge stale" in the future. In any case, get the challenge right before you do any other action. Getting a challenge is incredibly fast.

The real meat is "challenge". Don't try to dissect or understand it. It's an opaque cookie. Read the LJ source if you care. (cgi-bin/, sub get_challenge)

Now, to use it send "user" (flat) or "username" (xml-rpc), then:

key "auth_method" with value "challenge"
key "auth_challenge" with the challenge you got, and:
key "auth_response" of MD5_hex(Challenge + MD5_hex(Password))

Don't send "password" or "hpassword". (that's the whole point).

The challenge can only be used once. You'll get "invalid password" errors if it's used more than once.

This code is live on the test server: It'll be live on the real site in the next day or two, if not sooner.

Enjoy! Any questions?

# Perl Sample Code....
use strict;
use Fcntl;
use XMLRPC::Lite;
use Data::Dumper;
use Digest::MD5 qw(md5_hex);

my $xmlrpc = new XMLRPC::Lite;
my $get_chal = xmlrpc_call("LJ.XMLRPC.getchallenge");
my $chal = $get_chal->{'challenge'};

my $user = "brad";
my $pass = "test";
print "chal: $chal\n";

my $response = md5_hex($chal . md5_hex($pass));

my $login = xmlrpc_call('LJ.XMLRPC.login', {
'username' => $user,
'auth_method' => 'challenge',
'auth_challenge' => $chal,
'auth_response' => $response,

print Dumper($login);

sub xmlrpc_call {
my ($method, $req) = @_;
my $res = $xmlrpc->call($method, $req);
if ($res->fault) {
print STDERR "Error:\n".
" String: " . $res->faultstring . "\n" .
" Code: " . $res->faultcode . "\n";
exit 1;
return $res->result;

  • Post a new comment


    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded