W801(W80X_SDK_v1.00.10)https请求mbedtls报错0x7780问题解决记录

事情的起因是想通过W801实现检测到gpio电平变化后,通过telegram发送bot通知消息,结果在调用自己的tg api反代(caddy v2.6.4 h1:2hwYqiRwk1tf3VruhMpLcYTg+11fCdr8S3jhNAdnPy8=)接口时,串口日志提示标题上的ssl握手0x7780错误。

代码是直接抄的SDK示例中的demo\wm_https_demo.c代码,报错的函数是在握手时,也就是调用HTTPWrapperSSLConnect的时候,如下:

                    wm_printf("step 1: ssl connect to...\r\n");
                    ret = HTTPWrapperSSLConnect(&ssl_p, fd, (const struct sockaddr *)&server, sizeof(server), HTTPS_DEMO_SERVER);
                    if (ret < 0)
                    {
                        wm_printf("https connect error\r\n");
                        close(fd);
                        break;
                    }

开始的时候只是能看到串口输出step 1: ssl connect to…,然后报了个0x7780的错误,接下来https connect error,请求失败返回,研究了下内部使用的mbedtls库,发现其实还有更详细的调试日志,要在SDK的\src\app\mbedtls\include\mbedtls\config.h中打开“define MBEDTLS_DEBUG_C”这行宏定义(并且需要确保makefile项目的lib目标重新构建,如果是参考我之前的CLion IDE用法,就是在构建的configuration里选择lib,然后rebuild),打开后就能看到类似:https://forums.mbed.com/t/error-0x7780-during-handshake/6883 这里提到的错误信息:

ssl_tls.c:6754: |2| => handshake
ssl_cli.c:3384: |2| client state: 0
ssl_tls.c:2471: |2| => flush output
ssl_tls.c:2483: |2| <= flush output
ssl_cli.c:3384: |2| client state: 1
ssl_tls.c:2471: |2| => flush output
ssl_tls.c:2483: |2| <= flush output
ssl_cli.c:0770: |2| => write client hello
ssl_cli.c:0808: |3| client hello, max version: [3:3]
ssl_cli.c:0703: |3| client hello, current time: 3
ssl_cli.c:0817: |3| dumping 'client hello, random bytes' (32 bytes)
ssl_cli.c:0817: |3| 0000:  00 00 00 00 76 a0 12 da 58 6f 48 3c 14 72 c3 aa  ....v...XoH<.r..
ssl_cli.c:0817: |3| 0010:  22 ac 98 8a 5b 1b 3c 77 9f cb 78 19 16 55 0d 6c  "...[.<w..x..U.l
ssl_cli.c:0870: |3| client hello, session id len.: 0
ssl_cli.c:0871: |3| dumping 'client hello, session id' (0 bytes)
ssl_cli.c:0918: |3| client hello, add ciphersuite: c02c
ssl_cli.c:0918: |3| client hello, add ciphersuite: c02b
ssl_cli.c:0925: |3| client hello, got 2 ciphersuites (excluding SCSVs)
ssl_cli.c:0934: |3| adding EMPTY_RENEGOTIATION_INFO_SCSV
ssl_cli.c:0983: |3| client hello, compress len.: 1
ssl_cli.c:0985: |3| client hello, compress alg.: 0
ssl_cli.c:0186: |3| client hello, adding signature_algorithms extension
ssl_cli.c:0271: |3| client hello, adding supported_elliptic_curves extension
ssl_cli.c:0336: |3| client hello, adding supported_point_formats extension
ssl_cli.c:1059: |3| client hello, total extension length: 30
ssl_tls.c:2764: |2| => write record
ssl_tls.c:2910: |3| output record: msgtype = 22, version = [3:1], msglen = 81
ssl_tls.c:2471: |2| => flush output
ssl_tls.c:2490: |2| message length: 86, out_left: 86
ssl_tls.c:2496: |2| ssl->f_send() returned 86 (-0xffffffaa)
ssl_tls.c:2523: |2| <= flush output
ssl_tls.c:2922: |2| <= write record
ssl_cli.c:1085: |2| <= write client hello
ssl_cli.c:3384: |2| client state: 2
ssl_tls.c:2471: |2| => flush output
ssl_tls.c:2483: |2| <= flush output
ssl_cli.c:1478: |2| => parse server hello
ssl_tls.c:3809: |2| => read record
ssl_tls.c:2252: |2| => fetch input
ssl_tls.c:2413: |2| in_left: 0, nb_want: 5
ssl_tls.c:2437: |2| in_left: 0, nb_want: 5
ssl_tls.c:2438: |2| ssl->f_recv(_timeout)() returned 5 (-0xfffffffb)
ssl_tls.c:2458: |2| <= fetch input
ssl_tls.c:3561: |3| input record: msgtype = 21, version = [3:3], msglen = 2
ssl_tls.c:2252: |2| => fetch input
ssl_tls.c:2413: |2| in_left: 5, nb_want: 7
ssl_tls.c:2437: |2| in_left: 5, nb_want: 7
ssl_tls.c:2438: |2| ssl->f_recv(_timeout)() returned 2 (-0xfffffffe)
ssl_tls.c:2458: |2| <= fetch input
ssl_tls.c:4100: |2| got an alert message, type: [2:80]
ssl_tls.c:4108: |1| is a fatal alert message (msg 80)
ssl_tls.c:3831: |1| mbedtls_ssl_handle_message_type() returned -30592 (-0x7780)
ssl_cli.c:1485: |1| mbedtls_ssl_read_record() returned -30592 (-0x7780)
ssl_tls.c:6764: |2| <= handshake
ssl_tls.c:7542: |2| => free
ssl_tls.c:7607: |2| <= free
ssl_tls.c:6754: |2| => handshake
ssl_cli.c:3384: |2| client state: 0
ssl_tls.c:2471: |2| => flush output
ssl_tls.c:2483: |2| <= flush output
ssl_cli.c:3384: |2| client state: 1
ssl_tls.c:2471: |2| => flush output
ssl_tls.c:2483: |2| <= flush output
ssl_cli.c:0770: |2| => write client hello
ssl_cli.c:0808: |3| client hello, max version: [3:3]
ssl_cli.c:0703: |3| client hello, current time: 3
ssl_cli.c:0817: |3| dumping 'client hello, random bytes' (32 bytes)
ssl_cli.c:0817: |3| 0000:  00 00 00 00 11 0f 97 2f f9 d5 13 21 14 1a 34 a1  ......./...!..4.
ssl_cli.c:0817: |3| 0010:  a7 a3 94 6e e3 14 ab 22 98 15 53 98 b8 89 49 92  ...n..."..S...I.
ssl_cli.c:0870: |3| client hello, session id len.: 0
ssl_cli.c:0871: |3| dumping 'client hello, session id' (0 bytes)
ssl_cli.c:0918: |3| client hello, add ciphersuite: c02c
ssl_cli.c:0918: |3| client hello, add ciphersuite: c02b
ssl_cli.c:0925: |3| client hello, got 2 ciphersuites (excluding SCSVs)
ssl_cli.c:0934: |3| adding EMPTY_RENEGOTIATION_INFO_SCSV
ssl_cli.c:0983: |3| client hello, compress len.: 1
ssl_cli.c:0985: |3| client hello, compress alg.: 0
ssl_cli.c:0186: |3| client hello, adding signature_algorithms extension
ssl_cli.c:0271: |3| client hello, adding supported_elliptic_curves extension
ssl_cli.c:0336: |3| client hello, adding supported_point_formats extension
ssl_cli.c:1059: |3| client hello, total extension length: 30
ssl_tls.c:2764: |2| => write record
ssl_tls.c:2910: |3| output record: msgtype = 22, version = [3:1], msglen = 81
ssl_tls.c:2913: |4| dumping 'output record sent to network' (86 bytes)
ssl_tls.c:2913: |4| 0000:  16 03 01 00 51 01 00 00 4d 03 03 00 00 00 00 11  ....Q...M.......
ssl_tls.c:2913: |4| 0010:  0f 97 2f f9 d5 13 21 14 1a 34 a1 a7 a3 94 6e e3  ../...!..4....n.
ssl_tls.c:2913: |4| 0020:  14 ab 22 98 15 53 98 b8 89 49 92 00 00 06 c0 2c  .."..S...I.....,
ssl_tls.c:2913: |4| 0030:  c0 2b 00 ff 01 00 00 1e 00 0d 00 0a 00 08 06 03  .+..............
ssl_tls.c:2913: |4| 0040:  05 03 04 03 03 03 00 0a 00 06 00 04 00 18 00 17  ................
ssl_tls.c:2913: |4| 0050:  00 0b 00 02 01 00                                ......
ssl_tls.c:2471: |2| => flush output
ssl_tls.c:2490: |2| message length: 86, out_left: 86
ssl_tls.c:2496: |2| ssl->f_send() returned 86 (-0xffffffaa)
ssl_tls.c:2523: |2| <= flush output
ssl_tls.c:2922: |2| <= write record
ssl_cli.c:1085: |2| <= write client hello
ssl_cli.c:3384: |2| client state: 2
ssl_tls.c:2471: |2| => flush output
ssl_tls.c:2483: |2| <= flush output
ssl_cli.c:1478: |2| => parse server hello
ssl_tls.c:3809: |2| => read record
ssl_tls.c:2252: |2| => fetch input
ssl_tls.c:2413: |2| in_left: 0, nb_want: 5
ssl_tls.c:2437: |2| in_left: 0, nb_want: 5
ssl_tls.c:2438: |2| ssl->f_recv(_timeout)() returned 5 (-0xfffffffb)
ssl_tls.c:2458: |2| <= fetch input
ssl_tls.c:3552: |4| dumping 'input record header' (5 bytes)
ssl_tls.c:3552: |4| 0000:  15 03 03 00 02                                   .....
ssl_tls.c:3561: |3| input record: msgtype = 21, version = [3:3], msglen = 2
ssl_tls.c:2252: |2| => fetch input
ssl_tls.c:2413: |2| in_left: 5, nb_want: 7
ssl_tls.c:2437: |2| in_left: 5, nb_want: 7
ssl_tls.c:2438: |2| ssl->f_recv(_timeout)() returned 2 (-0xfffffffe)
ssl_tls.c:2458: |2| <= fetch input
ssl_tls.c:3738: |4| dumping 'input record from network' (7 bytes)
ssl_tls.c:3738: |4| 0000:  15 03 03 00 02 02 50                             ......P
ssl_tls.c:4100: |2| got an alert message, type: [2:80]
ssl_tls.c:4108: |1| is a fatal alert message (msg 80)
ssl_tls.c:3831: |1| mbedtls_ssl_handle_message_type() returned -30592 (-0x7780)
ssl_cli.c:1485: |1| mbedtls_ssl_read_record() returned -30592 (-0x7780)
ssl_tls.c:6764: |2| <= handshake
ssl_tls.c:7542: |2| => free
ssl_tls.c:7607: |2| <= free

通过论坛里的信息判断,应该是ssl握手时的加密套件不匹配问题导致的,这点我后来又参考SDK的demo中的调用,请求了一下www.tencent.com:443,结果果然可以收到正确的响应,于是怀疑caddy版本比较新,跟tls版本跟的也比较紧,不兼容旧版本,随后又尝试了下直连api.telegram.org,也是一样的7780错误。接着开始研究ssl握手的机制,找到了这么个厉害的网站:https://www.ssllabs.com/ssltest,这个网站可以扫描出目标https服务器的tls版本支持和兼容的加密套件设置情况(cipher suite,就是那些RSA,MD5,XXX之类的,在mbedtls的源码src/app/mbedtls/include/mbedtls/ssl_ciphersuites.h里也可以看到支持的各种套件),还有一些常见终端的握手测试情况。于是扫了下tx,又扫了下tg和自己的caddy反代,发现tx支持的tls版本比较多,包括1.0,1.1,而tg和caddy v2则只支持1.2以上(这样的设置test给出的评分也比较高A+以上),这是tg的结果:





可以看到,要求的套件只能是1.2以上,并且允许的也不是很多,这样一开始我以为是W801 SDK内置的mbedtls版本太低,不支持1.2,但是查了下发现用的是2.7.10版本(src/app/mbedtls/include/mbedtls/version.h),查了下关键代码,发现是支持tls 1.2的,为了确认下,我直接从mbedtls的github下载了2.7.10的源码,在windows上测试一下(没有CKLink,就算有在单片机上调试也不方便),2.7.10版有段时间了,带的VS工程还是2010版的,但是代码兼容性还不错,用2022直接升级一次编过。通过跑ssl_client1.c这段能测试握手的测试程序,我惊讶的发现,tg和我的caddy都没问题!这就好办了,掏出代码比对神器,开始对比W801 SDK中的mbedtls和原版到底有哪些区别,花了很长时间一点一点比对,跑测试,最后发现在src/app/mbedtls/include/mbedtls/config.h中,可能是由于嵌入式硬件资源受限,很多suite和功能都被闭掉了,打开#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED后,测试tg的api可以正常握手发收http请求响应了,本来以为这就算结了,结果试了下自己的caddy,发现还是报7780,于是又过了一段比对测试的时间,最后发现连caddy握手报错的原因并不是因为suite不匹配,而是这个:#define MBEDTLS_SSL_SERVER_NAME_INDICATION,在caddy配反代的时候,配置中写了如下的内容:

"tls_connection_policies": [{
                                                "match": {
                                                        "sni": [
                                                                "xxx.xxx.com"
                                                        ]
                                                }
                                        }],

于是乎服务端开启了所谓的SNI验证,而W801 SDK中的mbedtls没开启这个SNI,导致握手时客户端发的信息里没有有效SNI,caddy无法完成match,进而出现7780握手报错的问题,打开上面提到的define,问题解决!

PS:补充记录一个情况,W801 SDK中的Makefile主程序和依赖库是分开单独构建的,也就是说,修改了mbedtls的源码后直接make并不会自动重编译,而是需要主动调用下make lib来触发下依赖库的重构建,然后再make,生成的固件fls就可以了!

博主友情提示:

如您在评论中需要提及如QQ号、电子邮件地址或其他隐私敏感信息,欢迎使用>>博主专用加密工具v3<<处理后发布,原文只有博主可以看到。