Wow, Mangos登录时的SRP6认证

以Mangos代码为参考, 解析SRP6的原理和实现.
(转载请注明来源于金庆的专栏)

SRP全称Secure Remote Password(安全远程密码),是一个开源认证协议。

SRP简化后的原理是:
1. 服务器不保存密码或密码的散列值, 防止字典攻击. 
   而只是保存验证因子(verifier).
2. 客户端和服务器可以各自计算出一个会话秘钥(session key), 其值相同. 防止窃听.

参考:
Wow 服务器解析 ( http://www.cppblog.com/Jedimaster/archive/2006/10/14/13674.aspx )
SRP Protocol Design ( http://srp.stanford.edu/design.html )
魔兽世界服务器端编写参考资料 ( http://www.asstudio.de/wow/wow.htm )
RFC2954中文翻译 ( http://www.cnpaf.net/rfc/rfc2945.txt )
SRP是什么意思?_百度知道 ( http://zhidao.baidu.com/question/59783252.html )
源码 mangos/src/realmd/AuthSocket.cpp

== 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;        
    };

时间: 2024-09-20 02:34:25

Wow, Mangos登录时的SRP6认证的相关文章

ios-在用新浪微博做第三方登录时,官方给的SDKDemo为什么不能用啊

问题描述 在用新浪微博做第三方登录时,官方给的SDKDemo为什么不能用啊 在请求微博认证时,跳到登录页面会闪回,怎么设置,是因为ios版本问题吗 解决方案 新浪微博的第三方登录第三方登录之新浪微博 解决方案二: 在用新浪微博做第三方登录时,官方给的SDKDemo为什么不能用啊 联系一下新浪微博的技术人员,让他们帮你看看具体的原因 解决方案三: 因为没设https的原因吧,要加入白名单和允许访问http

Unbuntu管理员密码初次登录时验证错误

Unbuntu管理员密码, 默认情况下是没有的, 需要设置,初次使用su登录时, 会报错: su: Authentication failure 其实并不是验证错误, 是没有设定初值; 使用: sudo passwd, 再输入登录新密码即可设定新值,使用su, 即可以登录管理员(root)用户; 如下图所示: 更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/OS/Linux/

tomcat-平台项目开发,与子独立系统登录时遇到的问题

问题描述 平台项目开发,与子独立系统登录时遇到的问题 最近在做个平台项目,遇到一个问题,请教下这里的高手,我尽量简单说下:公司里在做一个平台项目,简单理解就是有一个统一的入口管理端,底下有几个独立的子系统,这几个系统(平台和子系统)用的都是统一的框架,所以JAR包都是一样的.因为要求系统是从平台登录,然后直接免登录跳转到其他子系统,我用的是cookie+ehcache.现在就出现一个问题:如果我把每个系统里相同的jar包放在各自的项目里的lib文件夹下,那么统一登录跳转子系统就没有问题,但是我把

j2ee 登录-je22系统,联网登录时,很慢

问题描述 je22系统,联网登录时,很慢 j2ee系统,联网登录时,很慢,但是在内网登录很快,可以那方面入手.大神们,给点指导吧 解决方案 大神,正解. 是DNS解析域名的速度都很慢 解决方案二: 用fiddler或者IE F12之类的工具调试,看时间花在了哪里.初步猜测,是dns解析浪费了时间,你确认下我的判断是否正确. 解决方案三: 第一次发问题,就有大神的迅速的回复. 非常感谢.

PHP读取CURL模拟登录时生成Cookie文件的方法_php技巧

本文实例讲述了PHP读取CURL模拟登录时生成Cookie文件的方法.分享给大家供大家参考.具体实现方法如下: 在使用PHP中的CURL模拟登录时会保存一个Cookie文件,例如下面的代码 复制代码 代码如下: $login_url = 'XXX';    $post_fields['email'] = 'XXXX';  $post_fields['password'] = 'XXXX';  $post_fields['origURL'] = 'XXX';  $post_fields['doma

如果您想确保Windows 10在新用户登录时不安装内置应用程序,则必须删除所有配置的应用程序。

原文 如果您想确保Windows 10在新用户登录时不安装内置应用程序,则必须删除所有配置的应用程序. 本文的内容 已安装与配置的应用程序 删除配置的应用程序 安装与配置的应用程序^ 在介绍如何删除所有内置应用程序之前,我必须解释安装和配置的应用程序之间的区别.这种差异仅适用于Microsoft现在称为Windows应用程序的新的触摸优化应用程序. 您可能已经注意到,每当用户首次在Windows 10计算机上登录时,Windows将开始安装该特定用户的应用程序.这些应用程序是配置的应用程序.同样

securecrt-SecureCRT使用秘钥登录时如何保存密码短语

问题描述 SecureCRT使用秘钥登录时如何保存密码短语 使用securecrt来登录服务器,配置的秘钥需要输入密码短语,所以每次打开securecrt都需要输入一个密码短语.有没有什么方式可以保存下这个密码短语不用重新输入?就像navicat中,使用ssh秘钥登录时可以保存密码短语. 解决方案 试试免费的XShell 解决方案二: XShell我刚刚试了,可以.那写脚本登录服务器时可以保存密码么?我们用的是putty进行连接和上传的.

ora 03114-oracle登录时出现“ORA-03114:未连接到ORACLE”求各位大神相助!!

问题描述 oracle登录时出现"ORA-03114:未连接到ORACLE"求各位大神相助!! oracle登录时出现"ORA-03114:未连接到ORACLE"求各位大神相助!!重装前正常登录,重装C盘后不能登录,重新安装了BDE7,oracel10.0.1并有oracle10.2.4补丁 解决方案 重装系统了?Oracle也得重装. 解决方案二: oracle服务开了吗

销毁单例-iOS退出登录问题,如何在退出登录时清空已加载的界面?

问题描述 iOS退出登录问题,如何在退出登录时清空已加载的界面? 请问有没有人做过退出登录,如何在退出登录时清空已经加载的页面?比如登录进来后进入一个tabBar(单例),如何在退出登录时将这个单例也销毁掉? 解决方案 你说的是应该是从主页面退出到登录注册界面吧?说实话我没这样做过,因为没意义啊退一步,如果你把真的把单例销毁了,登进去又要重新生成-- 解决方案二: 可以设置监听,在退出登录时发送监听,但是也感觉你这个操作没有必要,再下次用户登录的时候会请求心得数据将原来的数据覆盖,若是退出也会自