#!/usr/bin/perl

# This script is contributed by Vadim Vygonets to aid in debugging CRAM-MD5 
# authentication. It prompts for three data values: a user name, a password,
# and the challenge as sent out by an SMTP server. The challenge is a base-64
# string. It should be copied (cut-and-pasted) literally as the third data 
# item. The output of the program is the base-64 string that is to be returned
# as the response to the challenge. Using the example in RFC 2195:
#
# User: tim
# Password: tanstaaftanstaaf
# Challenge: PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+
# dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw
#
# The last line is what you you would send back to the server.


# Copyright (c) 2002
#	Vadim Vygonets <vadik-exim at vygo.net>.  All rights reserved.
# Public domain is OK with me.

use MIME::Base64;
use DIGEST::MD5;

# Prompt for values...
print "User: ";
chop($user = <>);
print "Password: ";
chop($passwd = <>);
print "Challenge: ";
chop($chal = <>);
# Strip "334" SMTP server response or "+" POP server response.
$chal =~ s/^(?:334|\+) //;

$context = new Digest::MD5;

# If the length of the password is greater than 64, use the MD5
# hash of the password instead (RFC 2104, chapter 2, verse 1).
if (length($passwd) > 64) {
	$context->add($passwd);
	$passwd = $context->digest();
	$context->reset();
}

# Turn passwd into array of chars.
@passwd = unpack("C*", pack("a64", $passwd));

# XOR passwd with ipad and opad.
for ($i = 0; $i < 64; $i++) {
	$pass_ipad[$i] = $passwd[$i] ^ 0x36;
	$pass_opad[$i] = $passwd[$i] ^ 0x5C;
}

# Append challenge to (passwd XOR ipad) and MD5 the result.
$context->add(pack("C64", @pass_ipad), decode_base64($chal));
$digest = $context->digest();
$context->reset();

# Append the result to (passwd XOR opad) and MD5 the result.
$context->add(pack("C64", @pass_opad), $digest);
$digest = $context->digest();

# Print username, whitespace, and hexadecimal representation of
# the resulting digest, BASE64-encoded.
print encode_base64($user . " " . unpack("H*", $digest));

# End
