Using T2A with Webhooks

A users interaction on a website and in particular eCommerce website can be described as a series of events. Whenever a user does something like sign in, register, request a password reset link, add an item to their basket etc. an event occurs. Webhooks are a way of responding to these events.

Age verification

With age verification becoming increasingly more regulated, it is important to make sure your website, if it sells age restricted products, is designed to check a customer is over 18. This can be achieved with webhooks and the T2A method age_verification. Most eCommerce solutions (e.g. WooCommerce) will provide webhooks that you can use. For WooCommerce you could set your Topic as “Customer created”, “Order created”, “Order updated” and then run the T2A age verification to check the customer information against our extensive UK people data sources to see if the user is over 18. Alternatively, you could select Action as your topic field and create a webhook that fires after a particular WooCommerce action occurs e.g. “woocommerce_after_checkout_validation”.

Identity Verification

Another use for webhooks is to check customer information to prevent fraudulent card transactions. Stripe, Braintree and other merchant account providers will provide webhook interaction which you can use by checking customer input against our person_verify method. This could be a first step in flagging a transaction that needs further investigation. Our person_verify method checks a name and address against our 48+ million data set of UK people and indicates whether they exist in our data.

Contact Appending

As well as verification you could use webhooks and T2A to improve your customer database.
We have substantial information on UK people and businesses. Using a “Customer created” webhook you could use our person_search method to enhance the customer record. The person_search method returns telephone numbers, mobile numbers and links the person to associated companies if they are a company director. Alternatively, if you are dealing with a UK business you could get further information on the company using our business_search, company_details, company_credit_report and director_details methods. Our business data includes telephone numbers, company website, company appointments, credit reports and information on directors. This appended customer information could help with marketing, profiling, order fulfillment and other customer service requirements.

Alternatively, if you don’t need to update customer records on the fly during webhooks you can take advantage of the bulk telephone number appending API method.

TPS Checking

The last use case in this post is the tps_full method. Again using a “Customer created” webhook you could check if the customers telephone number is on the TPS or Corporate TPS register and flag the record accordingly. You could also periodically check all your records with the bulk tps checking method tps_bulk (though this would not be during a webhook).

Web Service Problems and TLS 1.0


By the end of June 2018, many secure sites will have changed the way in which they allow their users to establish and use a connection to themselves.

We have noticed that a number of web services have recently implemented the same security changes, and we’ve had problems connecting to a couple.

Here’s the background, and how we ensured that we are still able to communicate with those web services.

Payment Card Industry (PCI)

The industry body which regulates the payment card industry has stipulated that sites taking credit or debt card payments must remove access via SSL or early TLS by 30th June 2018, to prevent future compromising of card details and personal information, during sessions conducted under these old protocols and now insecure protocols.

In practical terms this means that users of older browsers such as Internet Explorer 6-9, or older versions of Safari, will be unable to connect to any sites that take card payments, or at least the relevant portion of those sites.

Windows Servers and SChannel

We use Windows Web Servers.

A feature of Windows (including Windows 10 etc) is that all secure communications is handled via a component known as SChannel. In order to, for example, prevent a web server from accepting TLS 1.0 connections, SChannel must be re-configured; this is done via Windows Registry settings (but see below).

A factor to consider is that any change to SChannel affects incoming and outgoing traffic that use it.


Use IISCrypto to Change SChannel

We recommend the use of the free IISCrypto tool, as a simple means to configure SChannel on your web servers.

The image below shows that TLS 1.0 has been disabled in the protocol section.

Problem 1 – Web Service Accepts Only TLS 1.1 or 1.2

Several web services that we use from Windows 2008 R2 servers upgraded their security to remove not only SSL 3 (which was done some time ago) but also TLS 1.0

Using .Net 4.5,it is necessary to make a minor change to a SOAP service or an invocation of the .Net class HttpWebRequest.

The C# snippet below illustrates the necessary configuration to System.Net.ServicePointManager:-

 //send using TLS 1.2
 System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
 //invoke the soap service
 ExtSoap.ExtSoap_Int.ResponseType1 response = client.Invoke(request);

Problem 2 – Our Server Cannot Communicate with a TLS 1.0

This is an unlikely scenario, but we did encounter it briefly early in 2018.

We made a change to SChannel, preventing incoming connections using TLS 1.0 on a web server. That same web server then attempted to communicate with an external web service that did not, at that time, allow a secure connection over any protocol above TLS 1.0. Since the SChannel’s TLS 1.0 capability had been disabled, this also prevents outgoing communications over TLS 1.0.

Our solution in the above instance was to move the invocation onto another server that did not have TLS 1.0 disabled, whilst the external web service was upgraded.

Problem 3 – Cannot Connect to External Web Service

We were using HttpWebRequest to connect to an external web service over TLS 1.2. A web browser (Chrome or Firefox), running on a Windows Server was able to connect to the external web service, but any attempt to do so using the above class instance, failed, with an unhelpful “connection closed” error.

Further investigation showed that the external web service had replaced their server certificate with an Elliptic Curve certificate.

Because .Net uses SChannel to achieve a secure connection (unlike the web browsers listed above) it was necessary to explictly enabable those ciphers in SChannel using IISCrypto.

The shot below shows IISCrypto runnig on the affected server, whch is now able to communicate with the offending web service securely.

Note the Cipher suites that have ECDHE, now enabled and prioritised.

Setting Cipher Suites in SChannel



How to recover file contents after Notepad++ crash

Notepad++ is generally a pleasure to use but it does very occasionally crash and empty whatever file you happened to be editing at the time too… Here’s where you can find a backup version to recover your file if this ever happens to you too:


Note: At the time of writing I’m running Notepad++ v6.7.8.2 on Windows 7 Professional, but you’ve not got anything to lose by trying this for other versions of Notepad++/Windows.

Thanks to Indrajit on Stack Overflow for posting the solution originally!

Preventing card fraud when accepting Card Not Present (CNP) transactions

If your business accepts Card Not Present card payments (e.g. an eCommerce website) you are probably aware of the built in checks provider by your merchant services provider:  *AVS, CVV, MasterCard SecureCode / Verified by Visa and Fraud Screening.

*Brief description of built in checks

  • Address Verification Checks   – checks the numerical characters of the transactions billing address and postcode against the details held by the card issuer. (This is not widely used by non-UK cards)
  • Card Verification Value (CVV) – Checks the transactions inputted CVV against the value held by the card issuer.
  • MasterCard SecureCode / Verified by Visa  – services created by the Card Schemes to protect you and your customers.
  • Fraud Screening – your merchant provider provides a score indicating the likelihood a transaction is fraudulent, they also highlight anomalies with the transaction (e.g. transaction billing address country does not match value held by the card issuer).

There is however additional checks you can make to help avoid a fraudulent transaction.

  1. Check the customer emails address – proceed with caution with free email address like Yahoo, Hotmail or Gmail as these are more likely to result in fraud. Subscription email addresses like ‘BTConnect’ or ‘Virginmedia’ are usually safer. Or if the email address is the domain of a company website go to that domain and see if it is an established website, if it’s just a parking page the transaction is less safe. You should also check the name in the email address. Does it make sense when comparing it to the card holder name?  Checking the email address should be a part of your overall checking as many of your honest customers may use free email addresses.
  2. Is the order too good to be true? Be aware if you have an order that is a higher value than your normal orders. Also be aware if you get several orders from the same customer in a short space of time. Have a look at your statistics, how frequently do you get orders from the same customer, if you best honest customer is buying from your website once a month and someone purchases from you 3 days in a row, something might be fishy.
  3. Is it unusual in another way? Is there anything else you can think of that doesn’t match your normal customers.
  4. Check the IP address of the transaction and see where it originates from. Compare this with the billing address.  Be aware that fraudsters can use proxy IP addresses.
  5. Where possible ask customers for a land line telephone number which can be checked using Directory Enquiries (unless they are ex-directory). You can also check the supplied name and address details against the details on the Edited Electoral Roll. This is not a guarantee as it is possible to opt out of having your details published. Try people search OR the T2A API search for a person and find a residential telephone number methods.
  6.  Have a look at all the transactions occurring on your website not just the successful ones. Was the customer declined several times before they were successful? You shouldn’t immediately think its fraud as sometimes people mistype things, but you should investigate further.
  7. When you do get a fraudulent transaction or charge-back – investigate the details of the transaction and see if there are any clues to help further improve your fraud screening.

These checks can help you when you are reviewing your transactions but you may want to consider building your own solution that uses a combination of these checks against each transaction before they are submitted to protect yourself further.

Form validation made super easy with jQuery

We spend a lot of time coding both client and server-side form valdilation.

This is the routine I now always use for client-side validation, which has been refined over the years to become what I’d consider to be some pretty tight code, whilst providing good quality feedback to the user to help them complete forms quickly and with minimal confusion.

Hopefully you can learn a trick or two that you can use yourself, by looking at the code…

[code lang=”javascript”]
function errorBefore(error, $insertBefore) {
$(‘<p class="error">’ + error + ‘</p>’).insertBefore($insertBefore);

$(function () {

$("#my-form").submit(function (e) {

// remove any errors from previous form submission
$(‘.error’, $(this)).remove();

// get jQuery reference to form fields
var $name = $(‘#name’);
var $email = $(‘#email’);

// validate name input
if (!$name.val()) {
errorBefore(‘Please enter your name.’, $name);

// validate email input
if (!$email.val()) {
errorBefore(‘Please enter your email.’, $email);

// check for errors
if ($(‘.error’, $(this)).length) {

// find first error and focus on form field it relates to
$(‘.error’, $(this)).first().next(‘input, select, textarea’).focus();

// stop form submission



Reverse Geocoding for iOS using the API

In this tutorial, we’ll be creating a simple iPhone app capable of reverse geocoding – the process of finding the nearest address from a set of location coordinates. In this case, our location coordinates represent the user’s current location and will be provided natively by the CoreLocation framework (more on this in a bit).

On route we’ll learn how to make requests to a web service using NSURLConnection and how to process the JSON response we get back from it, using SBJson (a JSON parsing framework) which is available at

Open Xcode (I’m using version 4.2 but instructions shouldn’t vary too much for earlier/later versions) and create a new Xcode project. Choose Single View Application as the template for your new project and name your product something appropriate. Leave the 3 checkboxes unchecked and click next and then create in the following dialog.

Grab the Frameworks etc

(Note: There are more detailed instructions on the respective github pages telling you how to successfully get the frameworks into Xcode if you get stuck with either of these steps).

Head over to and download the latest .ZIP file. Open up the .ZIP and select all the files in the Classes folder then drag them into your project, creating a new group for them to keep things tidy.

We’re also going to use a lovely, easy to use, loading indicator which you can find at Create another new group and drag the MBProgressHUD.h and MBProgressHUD.m files into it.

Lastly – We need to add the CoreLocation framework to our project. In the project navigator, select your project – Select your target and then the ‘Build Phases’ tab. Open the ‘Link Binaries with Libraries’ expander and click the ‘+’ button. Search for the CoreLocation framework and click ‘Add’.

The User Interface

Open up your .xib file and drag on a Label and a button, then open up your view controller’s header file and add an IBOutlet for your label and an IBAction for your button.

[sourcecode language=”objc”]

@interface ViewController : UIViewController {

UILabel *addressLabel;


@property (nonatomic, retain) IBOutlet UILabel *addressLabel;

-(IBAction) buttonClick;



Go back to the Interface Builder for your .xib file, right click the File’s Owner icon (the semi-transparent yellow box to the left of the editing area). Drag the addressLabel outlet onto your label and the buttonClick received action onto your button, selecting touch up inside as the type of interaction.

Back to the Header File

We need to add a few things in our header file before we can get on with coding the beefy bits of our application. Shortly but sweetly:

  • Import the CoreLocation and MBProgressHUD.h header files.
  • Have your view controller implement the CLLocationManagerDelegate delegate.
  • Add instance variables for responseData (NSMutableData), locationManager (CLLocationManager), currentLat (CLLocationDegrees), currentLon (CLLocationDegrees) and HUD (MBProgressHUD).

Your finished header file should look like this:

[sourcecode language=”objc”]

#import ;
#import ;
#import "MBProgressHUD.h"

@interface ViewController : UIViewController {

NSMutableData *responseData;

CLLocationManager *locationManager;

CLLocationDegrees currentLat;

CLLocationDegrees currentLon;

UILabel *addressLabel;

MBProgressHUD *HUD;


@property (nonatomic, retain) IBOutlet UILabel *addressLabel;

-(IBAction) buttonClick;



The Beef Of The Code

Include the SBJson.h header file in your implementation file, and synthesize the addressLabel property we created earlier.

SQL, NULL and the 42nd President

Virtually anyone who has written a SQL query will have encountered NULL column items. All of the text books repeat the same sermon:-

NULL is not equal to anything, not even itself.

..which of course means that if a field is not set (i.e. is NULL) it will be ignored by a query such as:-

select name,'good' from player where score >= 60
select name,'poor' from player where score < 60 ;

At first glance the above query would appear to return all players, poor and good. If however a player’s score value is not set, the query will not return that player. If the query is modified thus:-

select name,'good' from player where score >= 60
select name,'poor' from player where score < 60
select name,'unknown' from player where score is NULL

..all players are returned. Note the use of IS NULL to ensure that rows with an undefined score are returned.

Name Rating
Jason good
Phineas poor
Medea unknown

However it is also in a sense correct to say:-

NULL is not not equal to anything


Consider the following simple table, holding the name, year of coming to office, and current status of the President of the United Status (or POTUS):-

`name` varchar(255),
`year` SMALLINT,
`status` varchar(20),

We then populate the table with the holders of that particular job over the past century:-

insert into potus (name,year,status) VALUES
('Barack Obama', 2009,'current');

insert into potus (name,year,status) VALUES
('George W Bush', 2001,'former');

insert into potus (name,year) VALUES
('Bill Clinton', 1993);

insert into potus (name,year,status) VALUES
('George H Bush', 1989,'former');

insert into potus (name,year,status) VALUES
('Ronald Reagan', 1981,'deceased');

insert into potus (name,year,status) VALUES
('Jimmy Carter', 1977,'former');

insert into potus (name,year,status) VALUES
('Gerald Ford', 1974,'deceased');

insert into potus (name,year,status) VALUES
('Richard Nixon', 1969,'deceased');

insert into potus (name,year,status) VALUES
('Lyndon Johnson', 1963,'deceased');

insert into potus (name,year,status) VALUES
('John Kennedy', 1961,'deceased');

insert into potus (name,year,status) VALUES
('Dwight Eisenhower', 1953,'deceased');

insert into potus (name,year,status) VALUES
('Harry S Truman', 1945,'deceased');

insert into potus (name,year,status) VALUES
('Franklin Roosevelt',1933,'deceased');

insert into potus (name,year,status) VALUES
('Herbert Hoover', 1929,'deceased');

insert into potus (name,year,status) VALUES
('Calvin Coolidge', 1923,'deceased');

insert into potus (name,year,status) VALUES
('Warren Harding', 1921,'deceased');

insert into potus (name,year,status) VALUES
('Woodrow Wilson', 1913,'deceased');

Keen observers will note that an error was made when inserting the 42nd President, a Mr Clinton; his current status was not inserted into the table, and is thus NULL.

The following query thus, as you would expect, fails to return Mr Clinton, given that his status is not equal to ‘current’ or ‘former’:-

select name from potus where status IN ('current','former');

However you may think that this query, to return all presidents who are not deceased, would return Mr Clinton:-

select name, year from potus
where status !='deceased'
order by year desc

… but it does not. Mr Clinton’s status is NULL, and so it is not not equal to ‘deceased’.
NULL will not work with any regular comparitor (equals, not equals, less than etc).

The query produces:-

Name Year
Barack Obama 2009
George W Bush 2001
George H Bush 1989
Jimmy Carter 1977

The following query returns any live presidents, plus any whose health is undefined:-

select name,year from potus where
(status !='deceased' or status is NULL)
order by year desc

Name Year
Barack Obama 2009
George W Bush 2001
Bill Clinton 1993
George H Bush 1989
Jimmy Carter 1977

No more undefined index using PHP’s $_GET and $_POST

It’s very annoying having to check whether $_GET['somevalue'] is set before checking its value – but if you don’t, your code is going to throw a a lot of Notice: Undefined index warnings and that’s never good.

I’ve written a quick class to make every PHP developer’s life a bit easier. It’s based on CodeIgniter‘s $this->input class. You can use it as follows and never have to worry about whether the index exists or not… It will just get set to NULL if it doesn’t.

[code lang=”php”]
class Input {
function get($name) {
return isset($_GET[$name]) ? $_GET[$name] : null;

function post($name) {
return isset($_POST[$name]) ? $_POST[$name] : null;

function get_post($name) {
return $this->get($name) ? $this->get($name) : $this->post($name);
$input = new Input;

$page = $input->get(‘page’); // // look in $_GET
$page = $input->post(‘page’); // // look in $_POST
$page = $input->get_post(‘page’); // look in both $_GET and $_POST


MD5 C++ Class


The MD5 algorithm produces a 128 bit hash of a byte array (often text).

Don’t Use MD5

MD5 is now considered a “broken” hash and should now no longer be used in high security situations.

OK, If You Must Use MD5…

If you still wish to use MD5, for example to hash user passwords, always add a salt before hashing, to prevent a dictionary attack.

The MD5 class was derived from various C++ examples. The class is thread safe (an instance must be created for each thread) and uses no memory allocation.

In the MD5.h file, note the definition of an unsigned 32 bit integer; you may need to modify this.

typedef unsigned int MD5_UINT32;

There are 6 public functions, all named Compute, in two groups:-

  • to return the MD5 hash as an ASCII (8 bit) string
  • to return the MD5 hash as a wide (UTF-16) string

For each type above there is a Compute function that accepts a “wide” (UTF-16) string; there is an optional parameter to specify if the string should be converted to UTF-8 before hashing. If the string is known to be 8-bit (Unicode 0xff or less), set this to the default “false”.

Use the CMD5 class (files MD5.cpp and MD5.h). The CMD5 class also uses the CUnicode class (file Unicode.h) which has a single static public function.

To use, create an instance of CMD5. Do not share that instance with other threads. Creation of the instance on the stack is recommended. Each public method returns an “unsafe” pointer to either an ASCII or UTF-16 string which is contained in the class instance and is only safe to be used whilst the class remains in scope, or before it is deleted or re-used.

The classes may be downloaded here (6.1Kb zip).

The code below demonstrates the use of all 6 public functions.

[sourcecode language=”cpp”]

#include "md5.h"

//create an instance of MD5 on the stack
//which we will re-use for each example
CMD5 mx;

unsigned char test_array[5] = { 1, 2, 3, 5, 7 };

const wchar_t* res1 = mx.Compute("test string");
// res1 is safe to use until mx is re-used
// normally this would be copied to a safe location
// or used immediately

// version using ‘wide’ source string. Specify whether to
// convert the string to utf-8
// (needed if any characters above u+ff are present)
const wchar_t* res2 = mx.Compute(L"test string", false);
// version using byte array
const wchar_t* res3 = mx.Compute(test_array,5);

//methods returning 8 bit string MD5
const char* res4 = mx.Compute_8("test string");
const char* res5 = mx.Compute_8(L"test string", false);
const char* res6 = mx.Compute_8(test_array, 5);


MD5 for sencha touch

There’s no native MD5 functionality in JavaScript, but Joseph Meyers has written his own function, which accoring to this stackoverflow thread is the fastest one in town!

It didn’t take much work, but I’ve converted it into a utility class, ready to drop into your Sencha Touch projects.

[code lang=”javascript”]
Ext.define(‘MyApp.util.Crypto’, {
singleton : true,
alternateClassName : [‘Crypto’],

constructor: function(config) {

md5cycle: function(x, k) {
var me = this;
var a = x[0], b = x[1], c = x[2], d = x[3];

a = me.ff(a, b, c, d, k[0], 7, -680876936);
d = me.ff(d, a, b, c, k[1], 12, -389564586);
c = me.ff(c, d, a, b, k[2], 17,  606105819);
b = me.ff(b, c, d, a, k[3], 22, -1044525330);
a = me.ff(a, b, c, d, k[4], 7, -176418897);
d = me.ff(d, a, b, c, k[5], 12,  1200080426);
c = me.ff(c, d, a, b, k[6], 17, -1473231341);
b = me.ff(b, c, d, a, k[7], 22, -45705983);
a = me.ff(a, b, c, d, k[8], 7,  1770035416);
d = me.ff(d, a, b, c, k[9], 12, -1958414417);
c = me.ff(c, d, a, b, k[10], 17, -42063);
b = me.ff(b, c, d, a, k[11], 22, -1990404162);
a = me.ff(a, b, c, d, k[12], 7,  1804603682);
d = me.ff(d, a, b, c, k[13], 12, -40341101);
c = me.ff(c, d, a, b, k[14], 17, -1502002290);
b = me.ff(b, c, d, a, k[15], 22,  1236535329);

a =, b, c, d, k[1], 5, -165796510);
d =, a, b, c, k[6], 9, -1069501632);
c =, d, a, b, k[11], 14,  643717713);
b =, c, d, a, k[0], 20, -373897302);
a =, b, c, d, k[5], 5, -701558691);
d =, a, b, c, k[10], 9,  38016083);
c =, d, a, b, k[15], 14, -660478335);
b =, c, d, a, k[4], 20, -405537848);
a =, b, c, d, k[9], 5,  568446438);
d =, a, b, c, k[14], 9, -1019803690);
c =, d, a, b, k[3], 14, -187363961);
b =, c, d, a, k[8], 20,  1163531501);
a =, b, c, d, k[13], 5, -1444681467);
d =, a, b, c, k[2], 9, -51403784);
c =, d, a, b, k[7], 14,  1735328473);
b =, c, d, a, k[12], 20, -1926607734);

a = me.hh(a, b, c, d, k[5], 4, -378558);
d = me.hh(d, a, b, c, k[8], 11, -2022574463);
c = me.hh(c, d, a, b, k[11], 16,  1839030562);
b = me.hh(b, c, d, a, k[14], 23, -35309556);
a = me.hh(a, b, c, d, k[1], 4, -1530992060);
d = me.hh(d, a, b, c, k[4], 11,  1272893353);
c = me.hh(c, d, a, b, k[7], 16, -155497632);
b = me.hh(b, c, d, a, k[10], 23, -1094730640);
a = me.hh(a, b, c, d, k[13], 4,  681279174);
d = me.hh(d, a, b, c, k[0], 11, -358537222);
c = me.hh(c, d, a, b, k[3], 16, -722521979);
b = me.hh(b, c, d, a, k[6], 23,  76029189);
a = me.hh(a, b, c, d, k[9], 4, -640364487);
d = me.hh(d, a, b, c, k[12], 11, -421815835);
c = me.hh(c, d, a, b, k[15], 16,  530742520);
b = me.hh(b, c, d, a, k[2], 23, -995338651);

a = me.ii(a, b, c, d, k[0], 6, -198630844);
d = me.ii(d, a, b, c, k[7], 10,  1126891415);
c = me.ii(c, d, a, b, k[14], 15, -1416354905);
b = me.ii(b, c, d, a, k[5], 21, -57434055);
a = me.ii(a, b, c, d, k[12], 6,  1700485571);
d = me.ii(d, a, b, c, k[3], 10, -1894986606);
c = me.ii(c, d, a, b, k[10], 15, -1051523);
b = me.ii(b, c, d, a, k[1], 21, -2054922799);
a = me.ii(a, b, c, d, k[8], 6,  1873313359);
d = me.ii(d, a, b, c, k[15], 10, -30611744);
c = me.ii(c, d, a, b, k[6], 15, -1560198380);
b = me.ii(b, c, d, a, k[13], 21,  1309151649);
a = me.ii(a, b, c, d, k[4], 6, -145523070);
d = me.ii(d, a, b, c, k[11], 10, -1120210379);
c = me.ii(c, d, a, b, k[2], 15,  718787259);
b = me.ii(b, c, d, a, k[9], 21, -343485551);

x[0] = me.add32(a, x[0]);
x[1] = me.add32(b, x[1]);
x[2] = me.add32(c, x[2]);
x[3] = me.add32(d, x[3]);


cmn: function(q, a, b, x, s, t) {
var me = this;
a = me.add32(me.add32(a, q), me.add32(x, t));
return me.add32((a << s) | (a >>> (32 – s)), b);

ff: function (a, b, c, d, x, s, t) {
return this.cmn((b & c) | ((~b) & d), a, b, x, s, t);

gg: function(a, b, c, d, x, s, t) {
return this.cmn((b & d) | (c & (~d)), a, b, x, s, t);

hh: function(a, b, c, d, x, s, t) {
return this.cmn(b^c^d, a, b, x, s, t);

ii: function(a, b, c, d, x, s, t) {
return this.cmn(c^(b | (~d)), a, b, x, s, t);

md51: function(s) {
var me = this;
txt = ”;
var n = s.length,
state = [1732584193, -271733879, -1732584194, 271733878],
for (i = 64; i <= s.length; i += 64) {
me.md5cycle(state, me.md5blk(s.substring(i – 64, i)));
s = s.substring(i – 64);
var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for (i = 0; i < s.length; i++)
tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
me.md5cycle(state, tail);
for (i = 0; i < 16; i++)
tail[i] = 0;
tail[14] = n * 8;
me.md5cycle(state, tail);
return state;

md5blk: function(s) {
var md5blks = [],
for (i = 0; i < 64; i += 4) {
md5blks[i >> 2] = s.charCodeAt(i)
+ (s.charCodeAt(i + 1) << 8 )
+ (s.charCodeAt(i + 2) << 16)
+ (s.charCodeAt(i + 3) << 24);
return md5blks;

hex_chr: ‘0123456789abcdef’.split(”),

rhex: function(n) {
var s = ”,
j = 0;
for (; j < 4; j++)
s += this.hex_chr[(n >> (j * 8 + 4)) & 0x0F]
+ this.hex_chr[(n >> (j * 8)) & 0x0F];
return s;

hex: function(x) {
for (var i = 0; i < x.length; i++)
x[i] = this.rhex(x[i]);
return x.join(”);

md5: function(s) {
return this.hex(this.md51(s));

add32: function(a, b) {
return (a + b) & 0xFFFFFFFF;



Include in app.js

[code lang=”javascript”]

requires: [ ‘MyApp.util.Crypto’]


Use like this:

[code lang=”javascript”]