CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Advanced:
Digest::SHA1 Problems

 



vc1048
Novice

Feb 10, 2015, 11:18 AM

Post #1 of 17 (13486 views)
Digest::SHA1 Problems Can't Post

this code was give to me by the vendor, & when using it, it comes back from the server with an error saying it does not match what was sent?? any help would be greatly appreciated. This is the complete sample code:


Code
use LWP::UserAgent; 
use Digest::SHA1 qw(sha1 sha1_hex sha1_base64);

$sourcekey = 'Your_source_key_here';
$pin = '1234';
$command = 'cc:sale';
$amount = '5.50';
$invoice = '123123';

# Generate Hash
$seed = rand();
$prehash = $command . ":" . $pin . ":" . $amount . ":" . $invoice . ":" . $seed;
$hash = 's/' . $seed . '/' . sha1_hex($prehash) . '/n';


$ua = LWP::UserAgent->new;
$res = $ua->post('https://sandbox.usaepay.com/gate.php', [
'UMkey' => $sourcekey,
'UMhash' => $hash,
'UMname' => "Example Tester",
'UMcard' => "4444555566667779",
'UMexpir' => "0113",
'UMcvv2' => "999",
'UMamount' => $amount,
'UMinvoice' => $invoice,
'UMstreet' => "1234 Main Street",
'UMzip' => "12345",
'UMcommand' => $command
]);


print "\n\nresult: ".$res->content;
print "\n";



(This post was edited by vc1048 on Feb 10, 2015, 11:51 AM)


vc1048
Novice

Feb 10, 2015, 12:00 PM

Post #2 of 17 (13465 views)
Re: [vc1048] Digest::SHA1 Problems [In reply to] Can't Post

the problem is in the hash, the server says: the hash does not match what was sent ?


Zhris
Enthusiast

Feb 10, 2015, 8:37 PM

Post #3 of 17 (13457 views)
Re: [vc1048] Digest::SHA1 Problems [In reply to] Can't Post

Hi,

This may sound trivial, but have you requested a sandbox account and have replaced the dummy values in the above script with those you are provided, i.e. source key.

The payment provider will rebuild the hash on their end after retrieving the secret pin for the supplied source key and extracting the seed from the hash parameter etc, then test that these hashes (encrypted strings) are equal to each other.

Regards,

Chris


(This post was edited by Zhris on Feb 10, 2015, 8:51 PM)


vc1048
Novice

Feb 11, 2015, 5:54 AM

Post #4 of 17 (13442 views)
Re: [Zhris] Digest::SHA1 Problems [In reply to] Can't Post

Hi,
Yes I have account & key & Pin and I put it together with my values from the cart. I also put the cart in debug mode so I can see the return, that is how i know it is the hash!

I am thinking that there is something in the way the hash is put together that maybe wrong? my other subs handling other vendors works fine, in fact this one works fine when the hash is not used (turned off) in the sand.box? I even tried using the MD5 hash that I use in the other subs & got the same result?

I have looked but can't find the error, maybe I am missing something or just getting stupid in my old age?:)


FishMonger
Veteran / Moderator

Feb 11, 2015, 7:09 AM

Post #5 of 17 (13438 views)
Re: [vc1048] Digest::SHA1 Problems [In reply to] Can't Post

I have not worked with any shopping cart transaction code, so I may not be of much help in that regard, but there are a couple things I can suggest and point out.

If that code was provided to you by the vendor, then my first thought/suggestion would be to contact them to see if they can provide any insight into why it's failing.

Is usaepay the vendor that supplied the code?

Obviously, the code isn't using the strict pragma and possibly not even the warnings pragma. Those 2 pragmas should be in every perl script.

The code looks like it was written by a php coder with limited experience in perl. Specifically I'm referring to the construction of the $prehash and $hash vars.

Here's how I'd assign those vars.

Code
my $prehash   = join(':', $command, $pin, $amount, $invoice, $seed); 
my $sha1 = sha1_hex($prehash);
my $hash = "s/$seed/$sha1/n";


I would have thought the (public) $sourcekey would have been used in the construction of the hash and a private key, held by usaepay.com, would be used to decrypt the hash after it was sent.


Zhris
Enthusiast

Feb 11, 2015, 10:32 AM

Post #6 of 17 (13426 views)
Re: [FishMonger] Digest::SHA1 Problems [In reply to] Can't Post

Hi,

It appears the code is supplied by the vendor ( http://wiki.usaepay.com/developer/perl ). In the past I have found Perl versions supplied by payment providers to be inadequate, I suppose they attempt to support multiple languages but their developers aren't necessarily savvy in them all, as you stated "The code looks like it was written by a php coder with limited experience in perl". Perhaps it would be worth the OP's time to try using the Business::OnlinePayment::USAePay module if they have no luck from contacting usaepay.

As far as I know, the payment provider does not have to decrypt the hash on their end and using a hashing algorithm that cannot easily be decrypted would be most secure in keeping the private key a secret. In this case it appears the pin is the private key which the payment provider can obtain from their database via the source key. All data other than the pin is posted unencrypted via the UM* parameters, therefore available without decryption. They must do something very roughly along the lines of:


Code
my $key     = param('UMkey'); 
my $hash = param('UMhash');
my $command = param('UMcommand');
my $amount = param('UMamount');
my $invoice = param('UMinvoice');

my ($seed, $sha1) = $hash =~ m{^s/(.+?)/(.+?)/n$};

my $pin = fetch_pin_from_db($key);

my $prehash_b = join ':', $command, $pin, $amount, $invoice, $seed;

my $sha1_b = sha1_hex($prehash_b);

if ($sha1 eq $sha1_b) {
# valid
} else {
# invalid
}



Regards,

Chris


(This post was edited by Zhris on Feb 11, 2015, 10:45 AM)


vc1048
Novice

Feb 11, 2015, 11:13 AM

Post #7 of 17 (13413 views)
Re: [FishMonger] Digest::SHA1 Problems [In reply to] Can't Post

Hi, I tried that and still a mismatch?

I called there so called tech support & she keeps referring me back to the sample:( so I told her I need to speak to a tech because something is not working right!

I think it is because there script is PHP & mine is Perl and it is handling the hash differently: ie mine is sending it from Perl & theirs is constructing the hash in PHP & trying to match mine in Perl?

I don't know if that makes sense or not?


vc1048
Novice

Feb 11, 2015, 11:20 AM

Post #8 of 17 (13408 views)
Re: [Zhris] Digest::SHA1 Problems [In reply to] Can't Post

The only problem with that is they check the Hash on there end and only say whether it is valid or not! They should send their hash so I can match it on my end!

I do a MD5 Hash match on my Authorize.net sub and it works fine!


Zhris
Enthusiast

Feb 11, 2015, 11:40 AM

Post #9 of 17 (13403 views)
Re: [vc1048] Digest::SHA1 Problems [In reply to] Can't Post

Hi,


Quote
I tried that and still a mismatch?


The code Fishmonger provided showed a more modern approach to the techniques used in the sample script, effectively they do the same thing therefore wasn't meant to be a fix.


Quote
I called there so called tech support & she keeps referring me back to the sample:( so I told her I need to speak to a tech because something is not working right!


Asking to talk to a tech was the right thing to do in my view. I have suffered from similar issues in the past and they were able to tell me in exact detail why the hashes didn't match (in my case it was because of carriage returns in line endings i.e. \r\n is not equal to \n).


Quote
I think it is because there script is PHP & mine is Perl and it is handling the hash differently: ie mine is sending it from Perl & theirs is constructing the hash in PHP & trying to match mine in Perl?


Regardless of the scripting languages you use to construct the hash and they use to reconstruct the hash this "shouldn't" be a concern. They will rebuild the strings in a similar manner and use the same hashing algorithm etc.


Quote
The only problem with that is they check the Hash on there end and only say whether it is valid or not! They should send their hash so I can match it on my end!


This would not be a good idea in terms of security. If they sent the hash back to an attacker, the attacker could write a script which speedily iterates through a list of pins until the hashes match, and therefore have discovered your secret pin.

In your position I would do the following:
- Double check that the information such as source key you are providing are indeed correct against your sandbox account. If for example you are reading these in from a file then make sure the line endings have been removed etc.
- Attempt to talk to tech as you have already tried doing, who can perhaps decipher exactly why yours and their hashes don't match.
- Try using a different approach, I pointed to an sdk module for usaepay in my previous post, it appears to use a similar technique to the third provided by usaepay on their Perl page.

Inevitably it is difficult to provide definitive help without being able to test your exact code, with your exact credentials. Could you possibly provide the exact error message being returned.

Regards,

Chris


vc1048
Novice

Feb 11, 2015, 11:50 AM

Post #10 of 17 (13398 views)
Re: [Zhris] Digest::SHA1 Problems [In reply to] Can't Post

here is the error:
<code>
Returned Response Report

Response_Header: UMversion=2.9&UMresult=E&UMstatus=Error&UMauthCode=000000&UMerror=Transaction%20authentication%20failed&UMerrorcode=10118&UMfiller=filled
Response_Code 0:UMfiller = filled
Response_Code 1:UMerrorcode = 10118
Response_Code 2:UMauthCode = 000000
Response_Code 3:UMresult = E
Response_Code 4:UMerror = Transaction authentication failed
Response_Code 5:UMversion = 2.9
Response_Code 6:UMstatus = Error

End report!
</code>

the error code:
10118 Transaction authentication failed. The UMmd5hash did not match the hash that was calculated for the transaction.


(This post was edited by vc1048 on Feb 11, 2015, 11:53 AM)


Zhris
Enthusiast

Feb 11, 2015, 12:05 PM

Post #11 of 17 (13391 views)
Re: [vc1048] Digest::SHA1 Problems [In reply to] Can't Post

Alright, that does indeed confirm that it is reporting the hashes do not match as per http://wiki.usaepay.com/developer/errorcodes. Are you hardcoding the pin, command, amount, invoice for testing purposes as they have done in the sample script, or are you fetching them from elsewhere, could you provide the exact code you are using ( obviously anonymise any private information you don't want us to know ).

Regards,

Chris


(This post was edited by Zhris on Feb 11, 2015, 12:05 PM)


vc1048
Novice

Feb 11, 2015, 12:33 PM

Post #12 of 17 (13385 views)
Re: [Zhris] Digest::SHA1 Problems [In reply to] Can't Post

This is the whole sub:


Code
sub usaepay_cc 
{
my ($postdata, );
my $invoice_description =~ s/\s/_/g;
my $UMpin = '2345';
my $UMcommand_type = '_66w9ht2bJ0b5WGMiI34GSqvpxfQKHVN';
my $tota = '158.10';
my $inv_num = $inv_num; # generated by this program

# Generate Hash
#$seed = time;
#$prehash = $UMcommand_type . ":" . $UMpin . ":" . $tota . ":" . $inv_num . ":" . $seed;
#$hash = 's/' . $seed . '/' . sha1_hex($prehash) . '/n';

my $seed = time;
my $prehash = join(':', $UMcommand_type, $UMpin, $tota, $inv_num, $seed);
my $sha1 = sha1_hex($prehash);
my $hash = "s/$seed/$sha1/n";


if ($usaepay_test_on eq 'Yes') { $postdata = "&UMtestmode=true"; }else{ $postdata = "&UMtestmode=false"; }

if ($UMaddcustomer eq 'Yes') {

$postdata .= "&UMkey=$UMkey&UMhash=$hash&UMamount=$tota&UMdescription=$invoice_description&UMname=$bfname%20$blname&UMstreet=$badd&UMcity=$bcity&UMstate=$bstate&UMzip=$bzip&UMcustemail=$bemail&UMinvoice=$inv_num&UMcard=$FORM{'cardnumber'}&UMexpir=$expmon$expyear&UMcvv2=$FORM{'isunumb'}&UMcommand_type=$UMcommand_type";

}else {

$postdata .= "&UMkey=$UMkey&UMhash=$hash&UMamount=$tota&UMdescription=$invoice_description&UMname=$bfname%20$blname&UMstreet=$badd&UMcity=$bcity&UMstate=$bstate&UMzip=$bzip&UMcustemail=$bemail&UMinvoice=$inv_num&UMcard=$FORM{'cardnumber'}&UMexpir=$expmon$expyear&UMcvv2=$FORM{'isunumb'}&UMcommand_type=$UMcommand_type";
}
$msglength=length($postdata);
$count=0;

if ($use_proxy eq "Yes") {

# PROXY SERVER IP ADDESS AND PORT NUMBER
$ENV{'HTTPS_PROXY'} = "$proxy_server";

# PROXY_BASIC_AUTH if needed
$ENV{'HTTPS_PROXY_USERNAME'} = "$proxy_username";
$ENV{'HTTPS_PROXY_PASSWORD'} = "$proxy_password";

# DEBUGGING SWITCH / LOW LEVEL SSL DIAGNOSTICS
$ENV{'HTTPS_DEBUG'} = '1';
}

my $ua = LWP::UserAgent->new;
$ua->timeout(20);
$ua->env_proxy if ($use_proxy eq "Yes");
#$req = HTTP::Request->new(POST => "https://$usaepayhost:$usaepayport/$usaepayscript");
my $req = HTTP::Request->new(POST => "https://sandbox.usaepay.com/gate.php");
$req->header(MIME_Version => '1.0');
$req->header('Accept' => 'text/plain');
$req->content_type('application/x-www-form-urlencoded');
$req->content_length($msglength);
$req->content($postdata);
$res = $ua->request($req);

my $reply_data = $res->content();

## break out the response and put it in an array
@pairs = split (/&/, $reply_data);

foreach $pair (@pairs) {
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$resp{$name} .= "\0" if (defined($resp{$name}));
$resp{$name} .= $value;
}

if (!($invnum)) { $invnum = $inv_num; }

#$resp[0] =~ s/(.*)\s(.*)$/$2/;

## Assign responce variables
$auth_code = "$resp{'UMstatus'} / $resp{'UMauthCode '}";
$trasnsid = $resp{'UMrefNum '};

## Get responce code messages
$rtnavs = $resp{'UMavsResult'};
$rtncvv = "xxx";
$reason = $resp{'UMstatus'};
$rtncvv_response = "UMcvv2Result ";

## Check for Debug
if($verbose == 1) {
$data_item = 0;
$reportz = "<font color=red>Response_Header:</font> $reply_data<br>";
foreach my $key (keys %resp) {
$reportz .= "<font color=red>Response_Code $data_item:</font>$key = $resp{$key}<br>";
$data_item++;
}
}

## Check for approvel or decline
if ($resp{'UMstatus'} eq "Approved") {
$results = 'Approved';
&usaepay_ord();
}
elsif ($resp{'UMstatus'} eq "Declined") {
$results = 'Declined';
&usaepay_ord();
}
elsif ($resp{'UMstatus'} eq "Declined") {
$results = 'Declined';
&usaepay_ord();
}
elsif ($resp{'UMstatus'} eq "Verification") {
$results = 'Card holder Verification Required: $resp{\'UMacsurl\'}';
&usaepay_ord();
}else {
$results = 'Declined with Errors';
&usaepay_ord();
}


} # end of sub



Zhris
Enthusiast

Feb 11, 2015, 1:08 PM

Post #13 of 17 (13370 views)
Re: [vc1048] Digest::SHA1 Problems [In reply to] Can't Post

Hi,

Had a look over your code and it appears mostly fine except for some outdated approaches. I just wanted to point out a couple of potential issues:

- As per the sample code, for now generate your seed using rand not time. I'm concerned that there is a possibility that the payment provider will validate this in some manner and may not consider the seed generated by time as acceptable. You never know.

- I noticed you are sending a content type of application/x-www-form-urlencoded but it doesn't appear as far as I can tell that you have comprehensively url encoded all the values in your post data. You can use the URI::Escape module to do this or supply the data as a hash to URI::Query and call the stringify method on the object etc etc etc. There is a possibility that special characters in your values are breaking your post data.

If I spot anything else I will raise it.

Regards,

Chris


(This post was edited by Zhris on Feb 11, 2015, 1:12 PM)


vc1048
Novice

Feb 11, 2015, 2:18 PM

Post #14 of 17 (13357 views)
Re: [Zhris] Digest::SHA1 Problems [In reply to] Can't Post

I have been code since 1997 & maybe I am a little rusty,

how would you recode my sub (just out of curiosity) :)


Zhris
Enthusiast

Feb 11, 2015, 3:20 PM

Post #15 of 17 (13348 views)
Re: [vc1048] Digest::SHA1 Problems [In reply to] Can't Post

I would:

- Firstly consider if using Business::OnlinePayment::USAePay would be the most appropriate solution rather than reinventing parts of the wheel.

- Use strict and warnings and fix all the resultant errors.

- Not use any globally scoped variables and pass any lexically scoped variables derived from outside the subroutine to the subroutine. I try to keep subroutines completely self contained except for rare cases.

- Build the request parameters into a hash of array data structure then convert this to a URI escaped query string last thing ( preferably via URI::Query ).

- Check the response was successful before continuing via LWP::UserAgent's is_success method as per the modules synopsis.

- Use CGI to parse response data. There are more modern approaches that aren't as low level as CGI such as Plack, but I'm familiar with CGI and have spent years developing my own set of CGI wrapper modules that take care of the many issues CGI has.

- Keep HTML seperate from code using a template module such as Template.

- Not necessarily call &usaepay_ord from &usaepay_cc, instead return the necessary variables back to the caller and call it from there.

- Not call subroutines using the & prefix unless I desire the side effects.

- Reduce code duplication with lookup hashes and not repeat notations per condition block i.e.:


Code
## Check for approvel or decline  
if ($resp{'UMstatus'} eq "Approved") {
$results = 'Approved';
&usaepay_ord();
}
elsif ($resp{'UMstatus'} eq "Declined") {
$results = 'Declined';
&usaepay_ord();
}
elsif ($resp{'UMstatus'} eq "Declined") {
$results = 'Declined';
&usaepay_ord();
}
elsif ($resp{'UMstatus'} eq "Verification") {
$results = 'Card holder Verification Required: $resp{\'UMacsurl\'}';
&usaepay_ord();
}
else {
$results = 'Declined with Errors';
&usaepay_ord();
}


my %lookup =
(
default => 'Declined with Errors',
Approved => 'Approved',
Declined => 'Declined',
Verification => "Card holder Verification Required: $resp{UMacsurl}",
);

my $status = $resp{UMstatus} // '';
my $results = $lookup{$status} // $lookup{default};
usaepay_ord( $results, ... )



Did you manage to look into the issues I outlined in my previous post, particularly the uri encoding one?

Regards,

Chris


(This post was edited by Zhris on Feb 11, 2015, 5:47 PM)


FishMonger
Veteran / Moderator

Feb 11, 2015, 4:31 PM

Post #16 of 17 (13334 views)
Re: [vc1048] Digest::SHA1 Problems [In reply to] Can't Post

I was about to post a detailed recommendation on how I'd recode your sub but, while I was driving home from work, Chris beat be to it.

I will add one thing, your code indentation/formatting is inconsistent which makes it more difficult to read and maintain. One of the first things I'd do would be to use Perl::Tidy to clean it up so that I can more easily see what it does and what needs to be changed.

Chris already mentioned using a hash structure to build your query string and here's part of the reason why. When I saw your if/else block that assigns $postdata, I found it difficult to see the differences in the assignment. When I copied those 2 300+ character strings were exactly the same, which means using an if/else block for that assignment doesn't make any sense.


Zhris
Enthusiast

Feb 12, 2015, 12:55 AM

Post #17 of 17 (13305 views)
Re: [vc1048] Digest::SHA1 Problems [In reply to] Can't Post

Hi,

As a direct consequence of what Fishmonger pointed out, I had missed that you are also posting the parameter UMcommand_type, whereas I believe you meant to use UMcommand as per the sample. Inevitably, since the command is part of the hash, and you don't provide this value, the payment provider will not be able to generate a matching hash. Fixing this and URI encoding the values to be on the safe side will hopefully fix your issue.

Untested:


Code
if ($usaepay_test_on eq 'Yes') { $postdata = "&UMtestmode=true"; }else{ $postdata = "&UMtestmode=false"; }  

if ($UMaddcustomer eq 'Yes') {
$postdata .= "&UMkey=$UMkey&UMhash=$hash&UMamount=$tota&UMdescription=$invoice_description&UMname=$bfname%20$blname&UMstreet=$badd&UMcity=$bcity&UMstate=$bstate&UMzip=$bzip&UMcustemail=$bemail&UMinvoice=$inv_num&UMcard=$FORM{'cardnumber'}&UMexpir=$expmon$expyear&UMcvv2=$FORM{'isunumb'}&UMcommand_type=$UMcommand_type";
}else {
$postdata .= "&UMkey=$UMkey&UMhash=$hash&UMamount=$tota&UMdescription=$invoice_description&UMname=$bfname%20$blname&UMstreet=$badd&UMcity=$bcity&UMstate=$bstate&UMzip=$bzip&UMcustemail=$bemail&UMinvoice=$inv_num&UMcard=$FORM{'cardnumber'}&UMexpir=$expmon$expyear&UMcvv2=$FORM{'isunumb'}&UMcommand_type=$UMcommand_type";
}


use URI::Query;

my $postdata = URI::Query->new(
UMtestmode => ( $usaepay_test_on eq 'Yes' ) ? 'true' : 'false',
UMkey => $UMkey,
UMhash => $hash,
UMamount => $tota,
UMdescription => $invoice_description,
UMname => "$bfname $blname",
UMstreet => $badd,
UMcity => $bcity,
UMstate => $bstate,
UMzip => $bzip,
UMcustemail => $bemail,
UMinvoice => $inv_num,
UMcard => $FORM{'cardnumber'},
UMexpir => "$expmon$expyear",
UMcvv2 => $FORM{'isunumb'},
UMcommand => $UMcommand_type,
)->stringify;



Regards,

Chris


(This post was edited by Zhris on Feb 12, 2015, 1:09 AM)

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives