魔兽登陆验证算法

 

SRP6针对于网游登录服的应用



1.关于SRP的资源

SRP协议是由斯坦福大学计算机科学系的Thomas wu等开发的,英文全称是Security Remote Password(远程密码安全),经过严密的数学论证,SRP被证明是一种非常安全的算法,我们可以在获取到SRP的协议的官方文档 http://srp.stanford.edu/ .当下流行的网络游戏魔兽世界采用的就是SRP6协议.

2.原理,设计

原理具体讲解可参看上面的官方文档,设计在http://srp.stanford.edu/design.html

3.大致流程

(1)协议中用到的符号表示的含义如下:

N N = 2q + 1 , q 是一个素数,下面所有的取模运算都和这个 N 有关

g 一个 N 的模数,应该是 2 个巨大的素数乘得来

k k = H(N,G) 在 SRP6 中 k = 3

s User’s Salt

I 用户名

p 明文密码

H() 单向 hash 函数

^ 求幂运算

u 随机数

a,b 保密的临时数字

A,B 公开的临时数字

x 私有密匙(从 p 和 s 计算得来)

v 密码验证数字

其中 x = H(s,p) 和 v = g ^ x , s 是随机选择的, v 用来将来验证密码。

(2)验证流程:

         客户端                                                                    服务器

                                           帐号--------->             从数据库中取出帐号对应的密码P,随机生成N,g,s,利用公

                                                                           式v=g^x生成密码验证数字v

     收到s后计算x=H(s,P)       <-------------N,g,s

    随机生成a,计算A=g^a        A------------>            收到A后,随机生成b,计算B=v + g^b,生成u=H(A+B)

           计算u=H(A+B)            <------------B             发送B给客户端

    S = (B - g^x)^(a + ux)                                          S = (A * v ^u )^b

          K = H(S)                                                              K = H(S)

     M[1] = H(ABK)           M[1]-------->               收到M[1]之后,验证与服务器计算出的M[1]是否相同

                                                                             相同则计算M[2],不同则发送密码错误给客户端

                                           <----------M[2]                        M[2] = H(AM[1], K)

     验证M[2]

 

以上所有的运算都是以N求余,我在上边省略了,但是在具体实现时一定不要忘了.

4.示例代码

附上一个SRP6的C语言简单实现。

[cpp]  view plain  copy
  1. // Srp6CDemo.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include <iostream>  
  5. #include <stdio.h>  
  6. #include <tchar.h>  
  7. #include <stdlib.h>  
  8. #include <memory.h>  
  9. #include <string.h>  
  10.   
  11. using namespace std;  
  12.   
  13. // Multiple-precision modular arithmetic  
  14. // package to support SRP-6 server and clients  
  15.   
  16. // Author: Karl Malbrain, malbrain@yahoo.com  
  17.   
  18. // configure package for 1024 bit operations  
  19.   
  20. #define SIZE (1024 / 32)  
  21.   
  22. #ifdef unix  
  23. #define __int64 long long  
  24. #else  
  25. typedef unsigned int uint;  
  26. typedef unsigned long ulong;  
  27. #endif  
  28.   
  29. typedef unsigned char uchar;  
  30. typedef unsigned __int64 uint64;  
  31.   
  32. //  1024 bit modulus taken from IETF  
  33. //  draft-ietf-tls-srp-10  
  34.   
  35. unsigned long DHmod[SIZE] = {  
  36.     0xEEAF0AB9, 0xADB38DD6, 0x9C33F80A, 0xFA8FC5E8,  
  37.     0x60726187, 0x75FF3C0B, 0x9EA2314C, 0x9C256576,  
  38.     0xD674DF74, 0x96EA81D3, 0x383B4813, 0xD692C6E0,  
  39.     0xE0D5D8E2, 0x50B98BE4, 0x8E495C1D, 0x6089DAD1,  
  40.     0x5DC7D7B4, 0x6154D6B6, 0xCE8EF4AD, 0x69B15D49,  
  41.     0x82559B29, 0x7BCF1885, 0xC529F566, 0x660E57EC,  
  42.     0x68EDBC3C, 0x05726CC0, 0x2FD4CBF4, 0x976EAA9A,  
  43.     0xFD5138FE, 0x8376435B, 0x9FC61D2F, 0xC0EB06E3  
  44. };  
  45.   
  46. // return 1 if multiple precision number is zero  
  47. // used to verify A & B are non-zero  
  48.   
  49. int mpzero (ulong *a)  
  50. {  
  51.     int idx = SIZE;  
  52.   
  53.     while( idx-- )  
  54.         if( *a++ )  
  55.             return 0;  
  56.   
  57.     return 1;  
  58. }  
  59.   
  60. // return remainder of double sized product u/DHmod in u  
  61.   
  62. void mpmod (ulong *u)  
  63. {  
  64.     int i, j, n = SIZE, m = SIZE * 2;  
  65.     uint64 quot, rem, nxt, prod;  
  66.     __int64 cry;  
  67.   
  68.     while( m )  
  69.         if( !u[0] )  
  70.             m--, u++;  
  71.         else  
  72.             break;  
  73.   
  74.     nxt = 0;  
  75.   
  76.     for( i = 0; i <= m - n; i++ ) {  
  77.         nxt <<= 32;  
  78.         nxt |= u[i];  
  79.   
  80.         rem = nxt % DHmod[0];  
  81.         quot = nxt / DHmod[0];  
  82.   
  83.         while( quot == 0x100000000 || rem < 0x100000000 && (quot * DHmod[1]) > (rem << 32 | u[i + 1]) )  
  84.             quot -= 1, rem += DHmod[0];  
  85.   
  86.         prod = cry = 0;  
  87.   
  88.         if( quot ) for( j = n; j--; ) {  
  89.             prod += quot * DHmod[j];  
  90.             cry += (uint64)u[i + j] - (ulong)prod;  
  91.             u[i + j] = (ulong)cry;  
  92.             prod >>= 32;  
  93.             cry >>= 32;  
  94.         }  
  95.   
  96.         nxt = u[i];  
  97.   
  98.         if( !i )  
  99.             if( cry - prod )  
  100.                 quot++;  
  101.             else  
  102.                 continue;  
  103.         else if( u[i - 1] += cry - prod )  
  104.             quot++;  
  105.         else  
  106.             continue;  
  107.   
  108.         cry = 0;  
  109.   
  110.         for( j = n; j--; ) {  
  111.             cry += (uint64)u[i + j] + DHmod[j];  
  112.             u[i + j] = (ulong)cry;  
  113.             cry >>= 32;  
  114.         }  
  115.   
  116.         if( i )  
  117.             u[i - 1] += (ulong)cry;  
  118.   
  119.         nxt = u[i];  
  120.     }  
  121. }  
  122.   
  123. //  multiply two SIZE numbers into double SIZED result  
  124.   
  125. void mpmult (ulong *dest, ulong *what, ulong *by)  
  126. {  
  127.     int m, n = SIZE;  
  128.     uint64 fact;  
  129.     uint64 cry;  
  130.   
  131.     memset (dest, 0, SIZE * 2 * sizeof(ulong));  
  132.   
  133.     while( n-- )  
  134.     {  
  135.         cry = 0;  
  136.   
  137.         if( fact = what[n] )   
  138.             for( m = SIZE; m--; ) {  
  139.                 cry += fact * by[m] + dest[n + m + 1];  
  140.                 dest[n + m + 1] = (ulong)cry;  
  141.                 cry >>= 32;  
  142.             }  
  143.   
  144.             dest[n] = (ulong)cry;  
  145.     }  
  146. }  
  147.   
  148. //  exponentiate SIZE base by SIZE exponent  
  149. //  to SIZE result, modulo DHmod  
  150.   
  151. void mpexp (ulong *result, ulong *base, ulong *exponent)  
  152. {  
  153.     ulong prod[SIZE * 2], term[SIZE];  
  154.     int idx = SIZE * 32;  
  155.   
  156.     memset (result, 0, SIZE * sizeof(ulong));  
  157.     memcpy (term, base, SIZE * sizeof(ulong));  
  158.     result[SIZE - 1] = 1;  
  159.   
  160.     while( idx )  
  161.         if( *exponent )  
  162.             break;  
  163.         else  
  164.             exponent++, idx -= 32;  
  165.   
  166.     while( idx-- ) {  
  167.         if( exponent[idx / 32] & (1 << (31 - (idx & 0x1f))) ) {  
  168.             mpmult (prod, result, term);  
  169.             mpmod(prod);  
  170.             memcpy (result, prod + SIZE, SIZE * sizeof(ulong));  
  171.         }  
  172.         mpmult (prod, term, term);  
  173.         mpmod(prod);  
  174.         memcpy (term, prod + SIZE, SIZE * sizeof(ulong));  
  175.     }  
  176. }  
  177.   
  178. // multiply a by b (single precision multiplier)  
  179. // and add to dest  
  180.   
  181. void mpmpyadd (ulong *dest, ulong *a, ulong b)  
  182. {  
  183.     ulong result[SIZE * 2];  
  184.     __int64 cry = 0;  
  185.     int idx = SIZE;  
  186.   
  187.     while( idx-- ) {  
  188.         cry += dest[idx];  
  189.         cry += a[idx] * (uint64)b;  
  190.         result[idx + SIZE] = cry;  
  191.         result[idx] = 0;  
  192.         cry >>= 32;  
  193.     }  
  194.   
  195.     //  normalize result  
  196.   
  197.     result[SIZE - 1] = cry;  
  198.     mpmod (result);  
  199.     memcpy (dest, result + SIZE, SIZE * sizeof(ulong));  
  200. }  
  201.   
  202. // multiply a by b (single precision multiplier)  
  203. // and subtract from dest  
  204.   
  205. void mpmpysub (ulong *dest, ulong *a, ulong b)  
  206. {  
  207.     ulong result[SIZE * 2];  
  208.     __int64 cry = 0;  
  209.     int idx = SIZE;  
  210.   
  211.     // multiply a by b  
  212.   
  213.     while( idx-- ) {  
  214.         cry += a[idx] * (uint64)b;  
  215.         result[idx + SIZE] = cry;  
  216.         result[idx] = 0;  
  217.         cry >>= 32;  
  218.     }  
  219.   
  220.     //  normalize result  
  221.   
  222.     result[SIZE - 1] = cry;  
  223.     mpmod (result);  
  224.   
  225.     //  subtract result from dest  
  226.   
  227.   
  228.     for( cry = 0, idx = SIZE; idx--; ) {  
  229.         cry += dest[idx];  
  230.         cry -= result[idx + SIZE];  
  231.         dest[idx] = cry;  
  232.         cry >>= 32;  
  233.     }  
  234.   
  235.     //  normalize result  
  236.   
  237.     if( cry < 0 )  
  238.         for( cry = 0, idx = SIZE; idx--; ) {  
  239.             cry += dest[idx];  
  240.             cry += DHmod[idx];  
  241.             dest[idx] = cry;  
  242.             cry >>= 32;  
  243.         }  
  244. }  
  245.   
  246. //  SHA 256 routines  
  247. //  taken from Wikipedia pseudo code  
  248.   
  249. //2^32 times the cube root of the first 64 primes 2..311  
  250.   
  251. static ulong k[64] = {  
  252.     0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,  
  253.     0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,  
  254.     0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,  
  255.     0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,  
  256.     0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,  
  257.     0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,  
  258.     0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,  
  259.     0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,  
  260.     0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,  
  261.     0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,  
  262.     0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };  
  263.   
  264.     //  store 64 bit integer  
  265.   
  266.     void putlonglong (uint64 what, uchar *where)  
  267.     {  
  268.         *where++ = what >> 56;  
  269.         *where++ = what >> 48;  
  270.         *where++ = what >> 40;  
  271.         *where++ = what >> 32;  
  272.         *where++ = what >> 24;  
  273.         *where++ = what >> 16;  
  274.         *where++ = what >> 8;  
  275.         *where++ = what;  
  276.     }  
  277.   
  278.     //  store 32 bit integer  
  279.   
  280.     void putlong (ulong what, uchar *where)  
  281.     {  
  282.         *where++ = what >> 24;  
  283.         *where++ = what >> 16;  
  284.         *where++ = what >> 8;  
  285.         *where++ = what;  
  286.     }  
  287.   
  288.     //  retrieve 32 bit integer  
  289.   
  290.     ulong getlong (uchar *where)  
  291.     {  
  292.         ulong ans;  
  293.   
  294.         ans = *where++ << 24;  
  295.         ans |= *where++ << 16;  
  296.         ans |= *where++ << 8;  
  297.         ans |= *where++;  
  298.         return ans;  
  299.     }  
  300.   
  301.     //  right rotate bits  
  302.   
  303.     ulong rotate (ulong what, int bits)  
  304.     {  
  305.         return (what >> bits) | (what << (32 - bits));  
  306.     }  
  307.   
  308.     //  right shift bits  
  309.   
  310.     ulong shift (ulong what, int bits)  
  311.     {  
  312.         return what >> bits;  
  313.     }  
  314.   
  315.     //  private structure for SHA  
  316.   
  317.     typedef struct {  
  318.         uchar buff[512/8];  // buffer, digest when full  
  319.         ulong h[256/32];    // state variable of digest  
  320.         uint64 length;      // number of bytes in digest  
  321.         int next;           // next buffer available  
  322.     } SHA256;  
  323.   
  324.     //  start new SHA run  
  325.   
  326.     void sha256_begin (SHA256 *sha)  
  327.     {  
  328.         sha->length = 0;  
  329.         sha->next = 0;  
  330.   
  331.         // 2^32 times the square root of the first 8 primes 2..19  
  332.         sha->h[0] = 0x6a09e667;  
  333.         sha->h[1] = 0xbb67ae85;  
  334.         sha->h[2] = 0x3c6ef372;  
  335.         sha->h[3] = 0xa54ff53a;  
  336.         sha->h[4] = 0x510e527f;  
  337.         sha->h[5] = 0x9b05688c;  
  338.         sha->h[6] = 0x1f83d9ab;  
  339.         sha->h[7] = 0x5be0cd19;  
  340.     }  
  341.   
  342.     //  digest SHA buffer contents  
  343.     //  to state variable  
  344.   
  345.     void sha256_digest (SHA256 *sha)  
  346.     {  
  347.         ulong nxt, s0, s1, maj, t0, t1, ch;  
  348.         ulong a,b,c,d,e,f,g,h;  
  349.         ulong w[64];  
  350.         int i;  
  351.   
  352.         sha->next = 0;  
  353.   
  354.         for( i = 0; i < 16; i++ )  
  355.             w[i] = getlong (sha->buff + i * sizeof(ulong));  
  356.   
  357.         for( i = 16; i < 64; i++ ) {  
  358.             s0 = rotate(w[i-15], 7) ^ rotate(w[i-15], 18) ^ shift(w[i-15], 3);  
  359.             s1 = rotate(w[i-2], 17) ^ rotate(w[i-2], 19) ^ shift (w[i-2], 10);  
  360.             w[i] = w[i-16] + s0 + w[i-7] + s1;  
  361.         }  
  362.   
  363.         a = sha->h[0];  
  364.         b = sha->h[1];  
  365.         c = sha->h[2];  
  366.         d = sha->h[3];  
  367.         e = sha->h[4];  
  368.         f = sha->h[5];  
  369.         g = sha->h[6];  
  370.         h = sha->h[7];  
  371.   
  372.         for( i = 0; i < 64; i++ ) {  
  373.             s0 = rotate (a, 2) ^ rotate (a, 13) ^ rotate (a, 22);  
  374.             maj = (a & b) ^ (b & c) ^ (c & a);  
  375.             t0 = s0 + maj;  
  376.             s1 = rotate (e, 6) ^ rotate (e, 11) ^ rotate (e, 25);  
  377.             ch = (e & f) ^ (~e & g);  
  378.             t1 = h + s1 + ch + k[i] + w[i];  
  379.   
  380.             h = g;  
  381.             g = f;  
  382.             f = e;  
  383.             e = d + t1;  
  384.             d = c;  
  385.             c = b;  
  386.             b = a;  
  387.             a = t0 + t1;  
  388.         }  
  389.   
  390.         sha->h[0] += a;  
  391.         sha->h[1] += b;  
  392.         sha->h[2] += c;  
  393.         sha->h[3] += d;  
  394.         sha->h[4] += e;  
  395.         sha->h[5] += f;  
  396.         sha->h[6] += g;  
  397.         sha->h[7] += h;  
  398.     }  
  399.   
  400.     //  add to current SHA buffer  
  401.     //  digest when full  
  402.   
  403.     void sha256_next (SHA256 *sha, uchar *what, int len)  
  404.     {  
  405.         while( len-- ) {  
  406.             sha->length++;  
  407.             sha->buff[sha->next] = *what++;  
  408.             if( ++sha->next == 512/8 )  
  409.                 sha256_digest (sha);  
  410.         }  
  411.     }  
  412.   
  413.     //  finish SHA run, output 256 bit result  
  414.   
  415.     void sha256_finish (SHA256 *sha, uchar *out)  
  416.     {  
  417.         int idx;  
  418.   
  419.         // trailing bit pad  
  420.   
  421.         sha->buff[sha->next] = 0x80;  
  422.   
  423.         if( ++sha->next == 512/8 )  
  424.             sha256_digest (sha);  
  425.   
  426.         // pad with zeroes until almost full  
  427.         // leaving room for length, below  
  428.   
  429.         while( sha->next != 448/8 ) {  
  430.             sha->buff[sha->next] = 0;  
  431.             if( ++sha->next == 512/8 )  
  432.                 sha256_digest (sha);  
  433.         }  
  434.   
  435.         // n.b. length doesn't include padding from above  
  436.   
  437.         putlonglong (sha->length * 8, sha->buff + 448/8);  
  438.         sha->next += sizeof(uint64); // must be full now  
  439.   
  440.         sha256_digest (sha);  
  441.   
  442.         // output the result, big endian  
  443.   
  444.         for( idx = 0; idx < 256/32; idx++ )  
  445.             putlong (sha->h[idx], out + idx * sizeof(ulong));  
  446.     }  
  447.   
  448. //#ifdef STANDALONE  
  449.   
  450.     //  proof-of-concept implementation of SHA-6   
  451.     //  calculate shared secret S three ways using  
  452.     //  variables known to client, server, both  
  453.   
  454.     //  AES key can be taken from a HASH of S  
  455.   
  456.     void prt (ulong *x)  
  457.     {  
  458.         char buff[SIZE*8 + 1];  
  459.         int idx = 0;  
  460.   
  461.         while( idx < SIZE )  
  462.             sprintf (buff + idx * 8,"%.8x", x[idx]), idx++;  
  463.   
  464.         for( idx = 0; idx < SIZE*8; idx++ )  
  465.             if( buff[idx] > '0' )  
  466.                 break;  
  467.   
  468.         printf ("%s/n", buff + idx);  
  469.     }  
  470.   
  471.     ulong lrand ()  
  472.     {  
  473.         static ulong seed = 0xdeadbeef;  
  474.   
  475.         return seed = (seed + 23 ) * 65537;  
  476.     }  
  477.   
  478.     #define HASH (256/8)  
  479.   
  480.     int _tmain(int argc, _TCHAR* argv[])  
  481.     {  
  482.         ulong S2[SIZE], s1[SIZE], s2[SIZE], s3[SIZE];  
  483.         ulong x[SIZE], g[SIZE], v[SIZE], u[SIZE];  
  484.         ulong S1[SIZE], t[SIZE];  
  485.         ulong A[SIZE], a[SIZE];  
  486.         ulong B[SIZE], b[SIZE];  
  487.         ulong prod[SIZE * 2];  
  488.   
  489.         uchar M1[HASH], M2[HASH];  
  490.         SHA256 sha[1];  
  491.         ulong salt[1];  
  492.   
  493.         char *user = "my user_id";  
  494.         char *pwd = "my password";  
  495.         int i;  
  496.   
  497.         // select random numbers a, b  
  498.         // n.b. use a cryptographically secure  
  499.         // source of random numbers in any  
  500.         // actual implementation of the SRP-6  
  501.         // protocol  
  502.   
  503.         for( i = 0; i < SIZE; i++ ) {  
  504.             a[i] = lrand();  
  505.             b[i] = lrand();  
  506.         }  
  507.   
  508.         // step zero client  
  509.         *salt = lrand();  
  510.   
  511.         // step two client  
  512.         sha256_begin( sha );  
  513.         sha256_next( sha, (uchar *)salt, sizeof(ulong));  
  514.         sha256_next( sha, (uchar *)user, strlen(user) );  
  515.         sha256_next( sha, (uchar *)pwd, strlen(pwd) );  
  516.   
  517.         memset (x, 0, sizeof(x));  
  518.         sha256_finish( sha, (uchar *)(x + SIZE) - HASH);  
  519.   
  520.         memset (g, 0, sizeof(g));  
  521.         g[SIZE - 1] = 2;  
  522.         mpexp (v, g, x);  
  523.   
  524.         // step three client  
  525.         mpexp (A, g, a);  
  526.   
  527.         // step three server  
  528.         mpexp (B, g, b);  
  529.         mpmpyadd (B, v, 3);  
  530.   
  531.         memset (u, 0, sizeof(u));  
  532.         sha256_begin( sha );  
  533.         sha256_next( sha, (uchar *)A, SIZE * sizeof(ulong));  
  534.         sha256_next( sha, (uchar *)B, SIZE * sizeof(ulong));  
  535.         sha256_finish( sha, (uchar *)(u + SIZE) - HASH);  
  536.   
  537.         // step four/five client -- first result  
  538.         sha256_begin( sha );  
  539.         sha256_next( sha, (uchar *)A, SIZE * sizeof(ulong));  
  540.         sha256_next( sha, (uchar *)B, SIZE * sizeof(ulong));  
  541.   
  542.         mpmpysub (B, v, 3); // re-use v as g^x  
  543.         mpexp (s1, B, a);  
  544.         mpexp (s2, B, u);  
  545.         mpexp (s3, s2, x);  
  546.         mpmult (prod, s1, s3);  
  547.         mpmod(prod);        // normalize S version one  
  548.         prt (prod + SIZE);  
  549.   
  550.         sha256_next( sha, (uchar *)(prod + SIZE), SIZE * sizeof(ulong));  
  551.         sha256_finish( sha, M1);  
  552.   
  553.         // step five server -- second result  
  554.         mpexp (t, v, u);  
  555.         mpmult(prod, A, t);  
  556.         mpmod(prod);  
  557.   
  558.         mpexp (S2, prod + SIZE, b); // compute S version two  
  559.         prt (S2);  
  560.   
  561.         sha256_begin( sha );  
  562.         sha256_next( sha, (uchar *)A, SIZE * sizeof(ulong));  
  563.         sha256_next( sha, (uchar *)B, SIZE * sizeof(ulong));  
  564.         sha256_next( sha, (uchar *)S2, SIZE * sizeof(ulong));  
  565.         sha256_finish( sha, M2);  
  566.   
  567.         // generic calc of S -- third result  
  568.         mpexp (s1, g, a);  
  569.         mpexp (t, s1, b);  
  570.   
  571.         mpexp (s1, g, x);  
  572.         mpexp (s2, s1, u);  
  573.         mpexp (s3, s2, b);  
  574.   
  575.         mpmult (prod, t, s3);  
  576.         mpmod(prod);  
  577.         prt (prod + SIZE);  
  578.   
  579.         int r;  
  580.         cin>>r;  
  581.     }  
  582. //#endif  
  

5.具体应用

a)Wow登录时的SRP6认证

Wow 的服务器有两部分组成: Logon Server (以下简称 LS )和 Realm Server (以下简称 RS )。 LS 接受来自 Wow 客户端的连接,主要有以下几步完成:

检查客户端版本区域等信息,检察账号密码

开始 / 继续传送 Patch (如果有)

与客户端进行 SRP6 的加密会话,把生成的密匙写入数据库

根据客户端请求发送 Realms 列表

当客户端选择好 Realms 后,客户端就从 LS 断开,连接到 RS 上:

认证,使用刚才生成的客户端密匙

如通过,进行游戏循环的交互

RS 和 LS 使用相同的数据库, SRP6 密匙被 LS 生成并写入 DB 后还要由 RS 读取出来进行下一步的认证。

 

Logon Server 详解

基本的连接过程如下:

客户端准备连接,发送 CMD_AUTH_LOGON_CHALLENGE 数据包,包含了所有登陆所需要的数据比如用户名密码等

服务端返回 CMD_AUTH_LOGON_CHALLENGE 数据包,填充字段包括有效验证,以及计算好的服务端 SRP6 数据

如果有效,客户端发送 CMD_AUTH_LOGON_PROOF 数据包,并把自己计算的 SRP6 数据填充进去

服务端进行验证,发送回 CMD_AUTH_LOGON_PROOF ,包含了 SRP6 验证的结果

如果一切正常,客户端发送 CMD_REALM_LIST 数据包,请求发送有效的 Realm

服务器回复 CMD_REALM_LIST 数据报,并填充过客户端需要的 Realm 数据

客户端的 Realm 列表每隔 3-4 秒就会从服务器端刷新一次。

  这个 SPR6 是一种什么样的加密手段呢?以前我也没有用过,看得最多的是 MD5SHA 等 hash 算法。 SPR 算法吸取了 EKE 类型算法的优点进行了改进,非常适合于网络的认证服务,如果我没有记错, J2EE 包含了这个算法的实现。下面简单介绍一下 SRP6a 运作机制,原文见这里。

N     N = 2q + 1 , q 是一个素数,下面所有的取模运算都和这个 N 有关

g     一个 N 的模数,应该是 2 个巨大的素数乘得来

k     k = H(N,G) 在 SRP6 中 k = 3

s      User’s Salt

I      用户名

p     明文密码

H()  单向 hash 函数

^      求幂运算

u     随机数

a,b   保密的临时数字

A,B  公开的临时数字

x     私有密匙(从 p 和 s 计算得来)

v     密码验证数字

其中 x  =  H(s,p) 和 v = g ^ x , s 是随机选择的, v 用来将来验证密码。

主机将 { I,s,v } 存入数据库。认证的过程如下:

 

客户向主机发送 I , A = g ^ a ( a 是一个随机数)

主机向客户发送 s , B = kv + g^b (发送 salt , b 是一个随机数字)

双方同时计算 u = H(A,B)

客户计算机算 x = H(s,p) (开始 hash 密码), S = ((B - kg^x) ^ (a + ux) ) , K = H(S) ,(开始计算会话 Key )

主机计算 S = (Av^u)^b , K = H(S) ,也生成会话 Key

 

为了完成认证,双方交换 Key ,各自进行如下的计算:

客户接收到来自主机的 key 后,计算 H(A,M,K)

同理,主机计算 M = H(H(N) xor H(g), H(I), s, A, B, K) ,验证是否合自己储存的数值匹配。至此完成验证过程。

 

      Realm Server 详解

从 LS 断开后,开始和 RS 认证:

连接到 RS ,向服务器发送 SMSG_AUTH_CHALLENGE 数据包,包含上次所用的随机种子

服务器发送回 SMSG_AUTH_CHALLENG 。客户端从服务器端发送回来的种子和 SRP6 数据中产生随机种子,生成 SHA1 字符串,用这些数据生成 CMSG_AUITH_SESSION 数据包,发送给服务端。

需要注意的是,这个过程是没有经过加密的。当服务端收到认证回复后,通过客户端产生的种子也生成一个 SHA1 串和来自客户端的进行对比,如果相同,一切 OK 。

 

下面看一下对账号创建的角色等操作进行分析。一个账号最多可以建 50 个角色吧,我还没有玩过,只是看了一下 Manual 。

 客户端发送一个CMSG_CHAR_ENUM数据包请求接受角色

服务端发送回包含所有角色信息的 CMSG_CHAR_ENUM 数据包

这里客户端可以对这些角色进行操作了, CMSG_CHAR_CREATE , CMSG_CHAR_DELETE , CMSG_CHAR_PLAYER_LOGIN

角色登陆完成后,服务器发送回 SMSG_CHAR_DATA 数据包

 

在游戏循环中是如何操作的呢?

如果玩家立刻退出游戏,那么客户端发送 CMSG_PLAYER_LOGOUT ,服务器回复 SMSG_LOGOUT_COMPLETE

如果玩家选择稍后退出游戏,发送 CMSG_LOGOUT_REQUEST 。服务端回复 SMSG_LOGOUT_RESPONSE 。如果玩家在倒计时阶段退出,发送 CMSG_PLAYER_LOGOUT ,那么玩家的角色依旧等倒计时完成后再退出。

如果玩家中断了退出继续游戏,发送 CMSG_LOGOUT_CANCEL ,服务器回复 SMSG_LOGOUT_CANCEL_ACK 。

b)Mangos登录时的SRP6认证

1. 客户端发送用户名和版本信息

    struct AUTH_LOGON_CHALLENGE_C
    {
        uint8   cmd;
        uint8   error;
        uint16  size;
        uint8   gamename[4];
        uint8   version1;
        uint8   version2;
        uint8   version3;
        uint16  build;
        uint8   platform[4];
        uint8   os[4];
        uint8   country[4];
        uint32  timezone_bias;
        uint32  ip;
        uint8   I_len;
        uint8   I[1];
    };

     大部份信息用来决定是否封阻该用户登录.
     SRP6相关的只有I, 为用户名. 
     SRP6相关的字段都是按协议中的符号定义的.


1.1 _SetVSFields(rI)设置v, s字段

从数据库中获取密码散列值rI(字段名sha_pass_hash), 应该是密码p, 
x = H(s, p)
v = g^x (密码学中的计算一般都是在最后对大质数N取模: v = g.ModExp(x, N);)
这个应该是验证因子v.
然后v, s存入数据库. x为临时值, 用后丢弃.

salt值s是在连接时设置的随机值.
/// Accept the connection and set the s random value for SRP6
void AuthSocket::OnAccept()
{
    s.SetRand(s_BYTE_SIZE * 8);
}
s是32字节长, s_BYTE_SIZE = 32.

安全大质数N, 及其生成元g, 是固定的:
    N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
    g.SetDword(7);

RFC2945:
   For
   maximum security, N should be a safe prime (i.e. a number of the form
   N = 2q + 1, where q is also prime).  Also, g should be a generator
   modulo N (see [SRP] for details), which means that for any X where 0
   < X < N, there exists a value x for which g^x % N == X.

为了最大化安全性,N可以是一个安全的素数
(也就是,一个类似于N=2q + 1形式的数,同时q是个素数)。
而且,g将是一个以N为模的生成元,
意味着,对任何X,有0 < X < N,存在一个值x,使得g^x % N == X。

Mangos保存了密码p, 是错误的. 服务器不应该保存密码或其散列值.
应该在创建用户时, 由客户端取s值, 计算v, 将{I, s, v}传输到服务器并保存.
登录时, 特定用户的s, v应该是固定的, 从数据库读取, 而不是每次登录时随机.


1.2 取b值, 计算B
    b.SetRand(19 * 8);
    BigNumber gmod=g.ModExp(b, N);
    B = ((v * 3) + gmod) % N;

b为19字节长的随机数. 不知为何是19字节长, 不是16或32? 
b是服务器的临时秘钥, B为临时公钥.
B = kv + g^b
在SRP6中k=3, 而在最新的SRP6a中, k=H(N, g).


1.3 服务端返回 CMD_AUTH_LOGON_CHALLENGE 数据包
返回的数据结构没有用struct定义, 只是用ByteBuffer依次填入数据.

    ByteBuffer pkt;
    
    pkt << (uint8) AUTH_LOGON_CHALLENGE;
    pkt << (uint8) 0x00;
    pkt << (uint8)REALM_AUTH_SUCCESS;
    pkt.append(B.AsByteArray(32), 32);   // 32 bytes
    pkt << (uint8)1;
    pkt.append(g.AsByteArray(), 1);
    pkt << (uint8)32;
    pkt.append(N.AsByteArray(), 32);
    pkt.append(s.AsByteArray(), s.GetNumBytes());   // 32 bytes
    pkt.append(unk3.AsByteArray(), 16);
    pkt << (uint8)0;                    // Added in 1.12.x client branch
    
    SendBuf((char const*)pkt.contents(), pkt.size());

B, g, N, s 是服务器发给客户端的SRP6参数.
unk3是个16字节长的随机数, 不知道干什么用的. (unknown3?)

按SRP6的协议, 应该是客户端先发送自己的用户名和公钥(I, A), 但在Mangos中,
是服务器在没有收到A时就发送盐值和自己的公钥(s, B).
这个次序应该无关紧要. 这样做的原因是服务器要先发送N, g到客户端, 这样可少一次消息交互.
客户端计算公钥A时要用到N, g: A = g^a (隐含对N取模).

2. 客户端发送 CMD_AUTH_LOGON_PROOF 数据包请求验证
    struct AUTH_LOGON_PROOF_C
    {
        uint8   cmd;
        uint8   A[32];
        uint8   M1[20];
        uint8   crc_hash[20];
        uint8   number_of_keys;
        uint8   unk;  // Added in 1.12.x client branch
    };

A, M1有用


2.1 计算u, S, K
    u = sha(A, B);
    S = (A * (v.ModExp(u, N))).ModExp(b, N);
    K = H(S);
其中K分奇偶位分别计算, 应该不是SRP的方法, 不知是否会降低散列效果.


2.2 计算M并与M1比较验证
    M = sha(sha(N) xor sha(g), sha(I), s, A, B, K)


2.3 M1验证通过后计算M2, 用于客户端验证
    M2 = sha(A, M, K)


2.4 服务端发回 CMD_AUTH_LOGON_PROOF
包含了 SRP6 验证的结果 M2

    struct AUTH_LOGON_PROOF_S
    {                        
        uint8   cmd;         
        uint8   error;       
        uint8   M2[20];      
        uint32  unk1;        
        uint32  unk2;        
        uint16  unk3;        
    };

 

 

>>扩展阅读:http://wenku.baidu.com/view/af081cf69e3143323968937c.html

                        http://blog.csdn.net/jq0123/archive/2009/04/10/4062020.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值