Recently WhatsApp moved definitely from protocol 1.1 to 1.2. This change affects to a lot of non-official WhatsApp clients, including Wazapp, WhatsAPI and Wassapp.

In this blog post I will try to explain the changes that are needed to get the protocol 1.2 working. If you do not have good programming skills, then the following may be a bit hard to understand.

1. Dictionary

The protocol dictionary has been modified in the new protocol. The old dictionary needs to be replaced with the new one.

This is the new dictionary array:

 "",
 "",
 "",
 "",
 "",
 "account",
 "ack",
 "action",
 "active",
 "add",
 "after",
 "ib",
 "all",
 "allow",
 "apple",
 "audio",
 "auth",
 "author",
 "available",
 "bad-protocol",
 "bad-request",
 "before",
 "Bell.caf",
 "body",
 "Boing.caf",
 "cancel",
 "category",
 "challenge",
 "chat",
 "clean",
 "code",
 "composing",
 "config",
 "conflict",
 "contacts",
 "count",
 "create",
 "creation",
 "default",
 "delay",
 "delete",
 "delivered",
 "deny",
 "digest",
 "DIGEST-MD5-1",
 "DIGEST-MD5-2",
 "dirty",
 "elapsed",
 "broadcast",
 "enable",
 "encoding",
 "duplicate",
 "error",
 "event",
 "expiration",
 "expired",
 "fail",
 "failure",
 "false",
 "favorites",
 "feature",
 "features",
 "field",
 "first",
 "free",
 "from",
 "g.us",
 "get",
 "Glass.caf",
 "google",
 "group",
 "groups",
 "g_notify",
 "g_sound",
 "Harp.caf",
 "http://etherx.jabber.org/streams",
 "http://jabber.org/protocol/chatstates",
 "id",
 "image",
 "img",
 "inactive",
 "index",
 "internal-server-error",
 "invalid-mechanism",
 "ip",
 "iq",
 "item",
 "item-not-found",
 "user-not-found",
 "jabber:iq:last",
 "jabber:iq:privacy",
 "jabber:x:delay",
 "jabber:x:event",
 "jid",
 "jid-malformed",
 "kind",
 "last",
 "latitude",
 "lc",
 "leave",
 "leave-all",
 "lg",
 "list",
 "location",
 "longitude",
 "max",
 "max_groups",
 "max_participants",
 "max_subject",
 "mechanism",
 "media",
 "message",
 "message_acks",
 "method",
 "microsoft",
 "missing",
 "modify",
 "mute",
 "name",
 "nokia",
 "none",
 "not-acceptable",
 "not-allowed",
 "not-authorized",
 "notification",
 "notify",
 "off",
 "offline",
 "order",
 "owner",
 "owning",
 "paid",
 "participant",
 "participants",
 "participating",
 "password",
 "paused",
 "picture",
 "pin",
 "ping",
 "platform",
 "pop_mean_time",
 "pop_plus_minus",
 "port",
 "presence",
 "preview",
 "probe",
 "proceed",
 "prop",
 "props",
 "p_o",
 "p_t",
 "query",
 "raw",
 "reason",
 "receipt",
 "receipt_acks",
 "received",
 "registration",
 "relay",
 "remote-server-timeout",
 "remove",
 "Replaced by new connection",
 "request",
 "required",
 "resource",
 "resource-constraint",
 "response",
 "result",
 "retry",
 "rim",
 "s.whatsapp.net",
 "s.us",
 "seconds",
 "server",
 "server-error",
 "service-unavailable",
 "set",
 "show",
 "sid",
 "silent",
 "sound",
 "stamp",
 "unsubscribe",
 "stat",
 "status",
 "stream:error",
 "stream:features",
 "subject",
 "subscribe",
 "success",
 "sync",
 "system-shutdown",
 "s_o",
 "s_t",
 "t",
 "text",
 "timeout",
 "TimePassing.caf",
 "timestamp",
 "to",
 "Tri-tone.caf",
 "true",
 "type",
 "unavailable",
 "uri",
 "url",
 "urn:ietf:params:xml:ns:xmpp-sasl",
 "urn:ietf:params:xml:ns:xmpp-stanzas",
 "urn:ietf:params:xml:ns:xmpp-streams",
 "urn:xmpp:delay",
 "urn:xmpp:ping",
 "urn:xmpp:receipts",
 "urn:xmpp:whatsapp",
 "urn:xmpp:whatsapp:account",
 "urn:xmpp:whatsapp:dirty",
 "urn:xmpp:whatsapp:mms",
 "urn:xmpp:whatsapp:push",
 "user",
 "username",
 "value",
 "vcard",
 "version",
 "video",
 "w",
 "w:g",
 "w:p",
 "w:p:r",
 "w:profile:picture",
 "wait",
 "x",
 "xml-not-well-formed",
 "xmlns",
 "xmlns:stream",
 "Xylophone.caf",
 "1",
 "WAUTH-1"

2. BinTreeNodeReader and BinTreeNodeWriter

These classes are the responsible of reading and writing tree-nodes. A tree-node is basically XML data encoded with a dictionary.

The new protocol 1.2 now includes support to encrypt nodes so we have to implement a way to encrypt and decrypt nodes in the tree-node reader and writer. Also, the header of each node has been increased from 2 to 3 bytes to store some flags.

In protocol 1.1, the node header was 2 bytes and it stored the node size (node size = packet size – header size). The structure of this header in the new protocol is the following:

1st byte: First 4 bits are reserved for some flags, last 4 bits are used to store the highest bits of the node size integer.
2st and 3rd byte: They store the remaining bits of the node size integer.

All we have to know about those flags is that the first bit is used to specify if the node is encrypted or not. Nothing else.

I will show some pseudo-code to read and parse these flags and the node size:

function NextTree() {
int header = ReadInt24();
int flags = (header >> 20);
int size = ((header & 0xF0000) >> 16) | ((header & 0xFF00) >> 8) | (header & 0xFF);
bool isEncrypted = ((flags & 8) != 0); // 8 = (1 << 4) // Read node and decrypt
}

Now let’s look at how to encrypt and decrypt nodes. Protocol 1.2 uses a symmetric cipher called RC4, so that means the same key is used to encrypt and decrypt data. Right now I won’t explain how to obtain this key as this will be done during the login process. When an encrypted node is received (we will know it is encrypted checking the node flags) the RC4 cipher is used to decrypt it, and it is also used to encrypt a node that is going to be sent. Some pseudo-code will show this more clearly:

function ReceiveNode() {
int size, flags;
ReadSizeAndFlags(size, flags); // read node header and extract data
buffer = ReadNode(); // the node without its header
if ((flags & 8) != 0)
RC4.cipher(buffer, 0, size); // 0 is start offsset
}
function SendNode() {
RC4.cipher(node, 3, size - 3); // offset = 3, to skip header data // set node size
hash = HMACSHA1.compute(node, 3, size - 3).getBytes(4);
node = node + hash; // append hash
header[0] = ((node.size & 0xFF0000) >> 16);
header[1] = ((node.size & 0xFF00) >> 8);
header[2] = (node.size & 0xFF);
header[0] |= (1 << 4); // set encrypted flag
}

All nodes have to be encrypted except during the login process. This is logical as the encryption key is obtained in this step.

3. Account login

The login process has changed in two of its steps.

The first change is in the “auth” node. In the protocol 1.1 the following attributes were sent in the node:

"xmlns": "urn:ietf:params:xml:ns:xmpp-sasl"
"mechanism": "DIGEST-MD5-1"

The protocol 1.2 uses a new authentification mechanism called “WAUTH-1″, and this new mechanism requires a new attribute called “user” which is just the user’s phone number (with the country prefix).

"xmlns": "urn:ietf:params:xml:ns:xmpp-sasl"
"mechanism": "WAUTH-1"
"user": phone number

The second change is in the “response” node. The response to the challenge is completely different in the new protocol. These are the steps that need to be done to generate a proper response node for the received challenge:

  1. Generate the encryption key (or session key) using the user’s password and the received challenge. This is done using an algoritm called PBKDF2 (RFC2898). The user’s password is used as the password value for this algorithm, and the challenge data as salt. The number of iterations needed to generate the key are 16. This is the key used to encrypt and decrypt nodes during the entire session (it is also used for the HMACSHA1 algorithm),
  2. Generate the response array. It is basically a byte array that contains: the user’s phone number, the challenge data and the current unix timestamp. The response length is normally 45 or 46 bytes.
  3. Encrypt the response array using the RC4 algorithm. The HMACSHA1 hash is stored at the beginning of the response. Final response = HMACSHA1 (4 bytes) + Encrypted response (45 or 46 bytes).

Again some pseudo-code to see this more clearly:

buffer = phone_number + challenge_data + unix_timestamp;
RC4.cipher(buffer, 0, buffer.size); // encrypt response
hash = HMACSHA1.compute(buffer).getBytes(4); // get only the first 4 bytes
buffer = hash + buffer; // final response to be sent in the response node

If the login is success, a encrypted “success” node will be received. If not, a non-encrypted “failure” node will be received instead.

I hope this information is helpful!

Recently WhatsApp moved definitely from protocol 1.1 to 1.2. This change affects to a lot of non-official WhatsApp clients, including Wazapp, WhatsAPI and Wassapp.

In this blog post I will try to explain the changes that are needed to get the protocol 1.2 working. If you do not have good programming skills, then the following may be a bit hard to understand.

1. Dictionary

The protocol dictionary has been modified in the new protocol. The old dictionary needs to be replaced with the new one.

This is the new dictionary array:

 "",
 "",
 "",
 "",
 "",
 "account",
 "ack",
 "action",
 "active",
 "add",
 "after",
 "ib",
 "all",
 "allow",
 "apple",
 "audio",
 "auth",
 "author",
 "available",
 "bad-protocol",
 "bad-request",
 "before",
 "Bell.caf",
 "body",
 "Boing.caf",
 "cancel",
 "category",
 "challenge",
 "chat",
 "clean",
 "code",
 "composing",
 "config",
 "conflict",
 "contacts",
 "count",
 "create",
 "creation",
 "default",
 "delay",
 "delete",
 "delivered",
 "deny",
 "digest",
 "DIGEST-MD5-1",
 "DIGEST-MD5-2",
 "dirty",
 "elapsed",
 "broadcast",
 "enable",
 "encoding",
 "duplicate",
 "error",
 "event",
 "expiration",
 "expired",
 "fail",
 "failure",
 "false",
 "favorites",
 "feature",
 "features",
 "field",
 "first",
 "free",
 "from",
 "g.us",
 "get",
 "Glass.caf",
 "google",
 "group",
 "groups",
 "g_notify",
 "g_sound",
 "Harp.caf",
 "http://etherx.jabber.org/streams",
 "http://jabber.org/protocol/chatstates",
 "id",
 "image",
 "img",
 "inactive",
 "index",
 "internal-server-error",
 "invalid-mechanism",
 "ip",
 "iq",
 "item",
 "item-not-found",
 "user-not-found",
 "jabber:iq:last",
 "jabber:iq:privacy",
 "jabber:x:delay",
 "jabber:x:event",
 "jid",
 "jid-malformed",
 "kind",
 "last",
 "latitude",
 "lc",
 "leave",
 "leave-all",
 "lg",
 "list",
 "location",
 "longitude",
 "max",
 "max_groups",
 "max_participants",
 "max_subject",
 "mechanism",
 "media",
 "message",
 "message_acks",
 "method",
 "microsoft",
 "missing",
 "modify",
 "mute",
 "name",
 "nokia",
 "none",
 "not-acceptable",
 "not-allowed",
 "not-authorized",
 "notification",
 "notify",
 "off",
 "offline",
 "order",
 "owner",
 "owning",
 "paid",
 "participant",
 "participants",
 "participating",
 "password",
 "paused",
 "picture",
 "pin",
 "ping",
 "platform",
 "pop_mean_time",
 "pop_plus_minus",
 "port",
 "presence",
 "preview",
 "probe",
 "proceed",
 "prop",
 "props",
 "p_o",
 "p_t",
 "query",
 "raw",
 "reason",
 "receipt",
 "receipt_acks",
 "received",
 "registration",
 "relay",
 "remote-server-timeout",
 "remove",
 "Replaced by new connection",
 "request",
 "required",
 "resource",
 "resource-constraint",
 "response",
 "result",
 "retry",
 "rim",
 "s.whatsapp.net",
 "s.us",
 "seconds",
 "server",
 "server-error",
 "service-unavailable",
 "set",
 "show",
 "sid",
 "silent",
 "sound",
 "stamp",
 "unsubscribe",
 "stat",
 "status",
 "stream:error",
 "stream:features",
 "subject",
 "subscribe",
 "success",
 "sync",
 "system-shutdown",
 "s_o",
 "s_t",
 "t",
 "text",
 "timeout",
 "TimePassing.caf",
 "timestamp",
 "to",
 "Tri-tone.caf",
 "true",
 "type",
 "unavailable",
 "uri",
 "url",
 "urn:ietf:params:xml:ns:xmpp-sasl",
 "urn:ietf:params:xml:ns:xmpp-stanzas",
 "urn:ietf:params:xml:ns:xmpp-streams",
 "urn:xmpp:delay",
 "urn:xmpp:ping",
 "urn:xmpp:receipts",
 "urn:xmpp:whatsapp",
 "urn:xmpp:whatsapp:account",
 "urn:xmpp:whatsapp:dirty",
 "urn:xmpp:whatsapp:mms",
 "urn:xmpp:whatsapp:push",
 "user",
 "username",
 "value",
 "vcard",
 "version",
 "video",
 "w",
 "w:g",
 "w:p",
 "w:p:r",
 "w:profile:picture",
 "wait",
 "x",
 "xml-not-well-formed",
 "xmlns",
 "xmlns:stream",
 "Xylophone.caf",
 "1",
 "WAUTH-1"

2. BinTreeNodeReader and BinTreeNodeWriter

These classes are the responsible of reading and writing tree-nodes. A tree-node is basically XML data encoded with a dictionary.

The new protocol 1.2 now includes support to encrypt nodes so we have to implement a way to encrypt and decrypt nodes in the tree-node reader and writer. Also, the header of each node has been increased from 2 to 3 bytes to store some flags.

In protocol 1.1, the node header was 2 bytes and it stored the node size (node size = packet size – header size). The structure of this header in the new protocol is the following:

1st byte: First 4 bits are reserved for some flags, last 4 bits are used to store the highest bits of the node size integer.
2st and 3rd byte: They store the remaining bits of the node size integer.

All we have to know about those flags is that the first bit is used to specify if the node is encrypted or not. Nothing else.

I will show some pseudo-code to read and parse these flags and the node size:

function NextTree() {
int header = ReadInt24();
int flags = (header >> 20);
int size = ((header & 0xF0000) >> 16) | ((header & 0xFF00) >> 8) | (header & 0xFF);
bool isEncrypted = ((flags & 8) != 0); // 8 = (1 << 4) // Read node and decrypt
}

Now let’s look at how to encrypt and decrypt nodes. Protocol 1.2 uses a symmetric cipher called RC4, so that means the same key is used to encrypt and decrypt data. Right now I won’t explain how to obtain this key as this will be done during the login process. When an encrypted node is received (we will know it is encrypted checking the node flags) the RC4 cipher is used to decrypt it, and it is also used to encrypt a node that is going to be sent. Some pseudo-code will show this more clearly:

function ReceiveNode() {
int size, flags;
ReadSizeAndFlags(size, flags); // read node header and extract data
buffer = ReadNode(); // the node without its header
if ((flags & 8) != 0)
RC4.cipher(buffer, 0, size); // 0 is start offsset
}
function SendNode() {
RC4.cipher(node, 3, size - 3); // offset = 3, to skip header data // set node size
hash = HMACSHA1.compute(node, 3, size - 3).getBytes(4);
node = node + hash; // append hash
header[0] = ((node.size & 0xFF0000) >> 16);
header[1] = ((node.size & 0xFF00) >> 8);
header[2] = (node.size & 0xFF);
header[0] |= (1 << 4); // set encrypted flag
}

All nodes have to be encrypted except during the login process. This is logical as the encryption key is obtained in this step.

3. Account login

The login process has changed in two of its steps.

The first change is in the “auth” node. In the protocol 1.1 the following attributes were sent in the node:

"xmlns": "urn:ietf:params:xml:ns:xmpp-sasl"
"mechanism": "DIGEST-MD5-1"

The protocol 1.2 uses a new authentification mechanism called “WAUTH-1″, and this new mechanism requires a new attribute called “user” which is just the user’s phone number (with the country prefix).

"xmlns": "urn:ietf:params:xml:ns:xmpp-sasl"
"mechanism": "WAUTH-1"
"user": phone number

The second change is in the “response” node. The response to the challenge is completely different in the new protocol. These are the steps that need to be done to generate a proper response node for the received challenge:

  1. Generate the encryption key (or session key) using the user’s password and the received challenge. This is done using an algoritm called PBKDF2 (RFC2898). The user’s password is used as the password value for this algorithm, and the challenge data as salt. The number of iterations needed to generate the key are 16. This is the key used to encrypt and decrypt nodes during the entire session (it is also used for the HMACSHA1 algorithm),
  2. Generate the response array. It is basically a byte array that contains: the user’s phone number, the challenge data and the current unix timestamp. The response length is normally 45 or 46 bytes.
  3. Encrypt the response array using the RC4 algorithm. The HMACSHA1 hash is stored at the beginning of the response. Final response = HMACSHA1 (4 bytes) + Encrypted response (45 or 46 bytes).

Again some pseudo-code to see this more clearly:

buffer = phone_number + challenge_data + unix_timestamp;
RC4.cipher(buffer, 0, buffer.size); // encrypt response
hash = HMACSHA1.compute(buffer).getBytes(4); // get only the first 4 bytes
buffer = hash + buffer; // final response to be sent in the response node

If the login is success, a encrypted “success” node will be received. If not, a non-encrypted “failure” node will be received instead.

I hope this information is helpful!

  • ahm322

    Thank you for this

    I am trying to make a program and i am stuck in the login process

    After sending Auth I receive the challenge Data which is only 20 Bytes is that right?

    Then I prepare the response but I only have 45 Bytes in total. I Understand from your explanation that I should have 49 or 50 Bytes in total (4 HMACSHA1 + 45 or 46 encrypted response)

    Could you please give more details about the 45 Bytes in the response, and what it should be?

    in my case I have the following:

    4 Bytes HMACSHA1
    11 Bytes Mobile Number
    20 Bytes Challenge
    10 Bytes UnixTime

    Total 45 Bytes. Please tell me what I am missing

  • JJ

    Any idea what is the nokia parameter used for? or the platforms?

  • Tommy

    Hi, all

    I tried to use the following url just for fun:
    - https://r.whatsapp.net/v1/exist.php?…
    - https://r.whatsapp.net/v1/exist.php?cc=$countrycode&in=$phonenumber&udid=$password

    it seems doesn’t function!!!

    So, analyzing the traffic with wireshark, i see the first packet sent to the server from the client that is divided in two parts, the first (FIXED) containt the model of the phone and the telephone number, the second is a “VARIABLE” part that maybe work for the encryption of the session.

    What i have seen is that this second part (variable), change every time a new session from the client to the server START.

    Any idea about the encryption?

    THANKS ALL.

    • http://twitter.com/LowlevelStudios Lowlevel Studios

      V1 API URLs don’t work if the phone number has been used with latest WhatsApp versions. There are now some new URLs to work with the new registration protocol but I don’t have too much information about them (I recommend you to look at Yowsup’s code).

  • NERCANO

    I need some one make this idea via php lang supporting my sql