1. 摘要

本文主要列举实际测试的 mbedTLS 在各种配置参数下,SSL 握手的内存开销和相应的测试方法。

2. 测试方法

测试版本:ESP8266_RTOS_SDK - c7b64043

测试思路是原子级的测试内存的开销,也就是说每次只要有内存分配就统计剩余的内存。代码修改方法如下:

  1. 找到 malloc的具体实现函数pvPortMalloc,位于 ESP8266_RTOS_SDK/third_party/freertos/heap_4.c

在里面添加全局统计变量:

size_t s_mem_mark;
  1. 添加复位 API 和 获取 API:
size_t get_mem_mark(void)
{
    return s_mem_mark;
}

void reset_mem_mark(void)
{
    s_mem_mark = (size_t)-1;
}
  1. 添加统计功能,在 pvPortMalloc函数的返回结果(return pvReturn)之前添加如下代码:
if (pvReturn) {
    extern size_t system_get_free_heap_size(void);

    size_t mem_size = system_get_free_heap_size();
    if (mem_size < s_mem_mark)
        s_mem_mark = mem_size;
}

测试代码代码修改如下:

  1. 找到 ESP8266_RTOS_SDK/examples/openssl_demo/programs/openssl_demo.c,添加全局API 声明:

extern size_t get_mem_mark(void);
extern void reset_mem_mark(void);
extern size_t system_get_free_heap_size(void);
```

  1. os_printf("create SSL context ......"); 之前添加:
reset_mem_mark();
os_printf("start heap %d\n", system_get_free_heap_size());
  1. 在以下代码
ret = SSL_connect(ssl);
 if (!ret) {
     os_printf("failed, return [-0x%x]\n", -ret);
     goto failed7;
 }
 os_printf("OK\n");

下方添加:

os_printf("min heap %d\n", get_mem_mark());
 os_printf("end heap %d\n", system_get_free_heap_size());

 while (1);

SSL server 代码部分也做类似的修改。

通过以上的修改,可以统计出 SSL 握手之前的内存,握手中系统剩余的最小内存和握手结束以后的内存。为了测试方便可以用2个 ESP8266 模组进行测试,1个做 server,1个做 client,通过 user_config.h配置 WIFI 和连接参数进行测试。

3. 测试数据

本章主要列举各种配置参数和具体测试数据。

3.1 client 模式

本节具体列举了 client 模式下非认证,本地认证,双向认证模式下,配置各种大小的 fragment 和证书所消耗的内存。

3.1.1 非认证 Fragment 测试

测试非认证模式下各 fragment大小和内存开销的关系,限定测试条件如下:

  • RSA2048 加密
  • 秘钥大小 1704 Bytes
  • 证书大小 1261 Bytes
3.1.1.1 数据

Fragment/B

开始

最小

最大

最大消耗/B

最后消耗/B

2048

41440

25496

29712

15944

11728

3072

41440

23448

27664

17992

13776

4096

41440

21400

25616

20040

15824

5120

41288

19352

23568

21936

17720

6144

41552

17304

21520

24248

20032

7168

41440

15256

19472

26184

21968

8192

41288

13208

17416

28080

23872

3.1.2 本地认证 Fragment 测试

测试本地认证模式下各 fragment大小和内存开销的关系,限定测试条件如下:

  • RSA2048 加密
  • 秘钥大小 1704 Bytes
  • 证书大小 1261 Bytes
  • 认证证书大小 1261 Bytes
3.1.2.1 数据

Fragment/B

开始

最小

最大

最大消耗/B

最后消耗/B

2048

41440

23666

27044

17774

14396

3072

41440

20938

25000

20502

16440

4096

41440

18882

22944

22558

18496

5120

41288

16686

20588

24602

20700

6144

41552

14910

18996

26642

22556

7168

41440

12754

16824

28686

24616

8192

41288

10578

14616

30710

26672

3.1.3 双向认证 Fragment 测试

测试双向认证模式下各 fragment大小和内存开销的关系,限定测试条件如下:

  • RSA2048 加密
  • 秘钥大小 1704 Bytes
  • 证书大小 1261 Bytes
  • CA证书大小 1261 Bytes
3.1.3.1 数据

Fragment/B

开始

最小

最大

最大消耗/B

最后消耗/B

2048

41440

20718

23472

20722

17968

3072

41440

17342

21424

24098

20016

4096

41440

14854

19036

26586

22404

5120

41288

13086

17172

28202

24116

6144

41552

10942

15328

30610

26224

7168

41440

9118

13192

32322

28248

8192

41288

6914

10968

34374

30320

3.1.4 非认证证书测试

测试非认证模式下各证书加秘钥总大小和内存开销的关系,限定测试条件如下:

  • fragment 为 5KB
3.1.4.1.数据

server 使用 RSA2048 — RSA8192 的秘钥和生成的证书。

证书和秘钥/B

开始

最小

最大

最大消耗/B

最后消耗/B

2936

41440

19774

23820

21666

17620

4065

41440

17734

20369

23706

21071

5195

41440

15334

19384

26106

22056

6324

41288

13342

18184

27946

23104

7449

41552

10178

17440

31374

24112

3.1.5 单向认证证书测试

测试单向认证模式下各证书加秘钥总大小和内存开销的关系,限定测试条件如下:

  • fragment 为 5KB
3.1.5.1 数据

server 使用 RSA2048 - RSA8192 的秘钥和生成的证书。

证书和秘钥/B

开始

最小

最大

最大消耗/B

最后消耗/B

4197

41440

16726

21124

24714

20316

5679

41440

13410

16940

28030

24500

7155

41440

10606

15564

30834

25876

8633

41288

6990

13568

34298

27720

3.1.6 双向认证证书测试

测试双向认证模式下各证书加秘钥大小和内存开销的关系,限定测试条件如下:

  • fragment 为 5KB
3.1.6.1 数据

server 使用 RSA2048 — RSA8192 的秘钥和生成的证书,以下表格首项为 client 和 server 端认证证书,证书和秘钥的总和。

证书和秘钥/B

开始

最小

最大

最大消耗/B

最后消耗/B

8394

41440

13198

17272

28242

24168

11358

41440

8370

12240

33070

29200

14310

41440

6590

9840

34850

31600

3.2 server 模式

本节具体列举了 server 模式下非认证模式,双向认证模式和配置各种大小的证书所消耗的内存。

3.2.1 非认证 Fragment 测试

测试非认证模式下各 fragment大小和内存开销的关系,限定测试条件如下:

  • RSA2048 加密
  • 秘钥大小 1704 Bytes
  • 证书大小 1261 Bytes
3.2.1.1 数据

Fragment/B

开始

最小

最大

最大消耗/B

最后消耗/B

2048

41440

14942

23288

26498

18152

3072

41440

12909

21275

28531

20165

4096

41440

10877

19265

30563

22175

5120

41288

8694

17103

32594

24185

6144

41552

6927

15357

34625

26195

3.2.2 双向认证 Fragment 测试

测试双向认证模式下各 fragment大小和内存开销的关系,限定测试条件如下:

  • RSA2048 加密
  • 秘钥大小 1704 Bytes
  • 证书大小 1261 Bytes
3.2.2.1 数据

Fragment/B

开始

最小

最大

最大消耗/B

最后消耗/B

2048

41440

11182

17868

30258

23572

3072

41440

8727

15784

32713

25656

4096

41440

6275

13702

35165

27738

3.2.3 非认证证书测试

测试非认证模式下各证书加秘钥大小和内存开销的关系,限定测试条件如下:

  • fragment 为 3KB
3.2.3.1 数据

server 使用 RSA2048 — RSA8192 的秘钥和生成的证书。,以下首项为 server 端证书和秘钥的总和。

证书和秘钥/B

开始

最小

最大

最大消耗/B

最后消耗/B

2965

41440

12994

21468

28446

19972

4097

41440

8607

19690

32833

21750

3.2.4 双向认证证书测试

测试双向认证模式下各证书加秘钥大小和内存开销的关系,限定测试条件如下:

  • fragment 为 3KB
3.2.4.1 数据

server 使用 RSA2048 — RSA8192 的秘钥和生成的证书。,以下首项为 server 端证书和秘钥的总和。

证书和秘钥/B

开始

最小

最大

最大消耗/B

最后消耗/B

4226

41440

6710

15620

34730

25820

4. 帮助

  • cert.sh用于生成测试使用的证书,openssl.cnf为生成证书的配置文件,可以通过修改 cert.sh中的全局变量 KEY_BITS来起到修改证书的大小的作用,直接修改其他参数也能实现,但是感觉意义不大
  • 如果手头只有1个 ESP8266 模组,可以使用 openssl 命令来创建 client 和 server 进行测试,参考链接如下:
    client:
    server:

#5. 附件
cert.sh

#!/bin/bash

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

ROOT_SUBJECT="/C=C1/ST=JS1/L=WX1/O=ESP1/OU=ESP1/CN=Server1 CA/emailAddress=ESP1"
LEVEL2_SUBJECT="/C=C2/ST=JS22/L=WX22/O=ESP22/OU=ESP22/CN=Server22 CA/emailAddress=ESP22"
LEVEL3_SUBJECT="/C=C3/ST=JS333/L=WX333/O=ESP333/OU=ESP333/CN=Server333 CA/emailAddress=ESP333"

SERVER_CERT_NAME="RootCA.crt"
SERVER_KEY_NAME="root-key.key"

CLIENT_CERT_NAME="RootCA.crt"
CLIENT_KEY_NAME="root-key.key"

KEY_BITS="4096"

echo "create root CA key"
openssl genrsa -out root-key.key $KEY_BITS
echo ----------------------
echo "create root cert request"
openssl req -new -key root-key.key -out root-req.csr -text -subj $ROOT_SUBJECT
echo ----------------------
echo "create root self sign cert"
openssl x509 -req -in root-req.csr -out RootCA.crt -sha1 -signkey root-key.key -days 3650 -text -extfile openssl.cnf -extensions v3_ca

echo "create 2 level cert key"
openssl genrsa -out root-mid.key $KEY_BITS
echo ----------------------
echo "create 2 level cert csr"
openssl req -new -key root-mid.key -out root-mid.csr -text -subj $LEVEL2_SUBJECT
echo ----------------------
echo "sign with root-crt"
openssl x509 -req -in root-mid.csr -CA RootCA.crt -CAkey root-key.key -CAcreateserial -days 3650 -out RootMid.crt -text -extfile openssl.cnf -extensions v3_ca

echo "create 3 level cert key"
openssl genrsa -out server.key $KEY_BITS
echo "create 3 level cert csr"
openssl req -new -key server.key -out server.csr -text -subj $LEVEL3_SUBJECT
echo "sign with level2 cert-crt"
openssl x509 -req -in server.csr -CA RootMid.crt -CAkey root-mid.key -CAcreateserial -days 3560 -out Server.crt -text -extfile openssl.cnf -extensions v3_ca

echo ""
echo ----------------------
echo "server uses certification " $SERVER_CERT_NAME " and key " $SERVER_KEY_NAME
echo "client uses certification " $CLIENT_CERT_NAME " and key " $CLIENT_KEY_NAME

rm *.csr *.srl

IFS=$SAVEIFS

openssl.cnf

################################################################ 
# openssl example configuration file. 
# This is mostly used for generation of certificate requests. 
################################################################# 
[ ca ] 
default_ca= CA_default          # The default ca section 
################################################################# 

[ CA_default ] 

dir=~/tmp/cert                   # Where everything is kept 
certs=$dir                       # Where the issued certs are kept 
crl_dir= $dir/crl                # Where the issued crl are kept 
database= $dir/index.txt         # database index file 
new_certs_dir= $dir/new_certs    # default place for new certs 
certificate=$dir/CA/OrbixCA      # The CA certificate 
serial= $dir/serial              # The current serial number 
crl= $dir/crl.pem                # The current CRL 
private_key= $dir/CA/OrbixCA.pk  # The private key 
RANDFILE= $dir/.rand             # private random number file 
default_days= 365                # how long to certify for 
default_crl_days= 30             # how long before next CRL 
default_md= md5                  # which message digest to use 
preserve= no                     # keep passed DN ordering 

# A few different ways of specifying how closely the request should 
# conform to the details of the CA 

policy= policy_match            # For the CA policy 

[ policy_match ]  
countryName= match 
stateOrProvinceName= match 
organizationName= match 
organizationalUnitName= optional 
commonName= supplied 
emailAddress= optional 

# For the `anything' policy 
# At this point in time, you must list all acceptable `object' 
# types 

[ policy_anything ] 
countryName = optional 
stateOrProvinceName= optional 
localityName= optional 
organizationName = optional 
organizationalUnitName = optional 
commonName= supplied 
emailAddress= optional 

[ req ] 
default_bits = 1024 
default_keyfile= privkey.pem 
distinguished_name = req_distinguished_name 
attributes = req_attributes 
x509_extensions = v3_ca

[ req_distinguished_name ] 
countryName= Country Name (2 letter code) 
countryName_min= 2 
countryName_max = 2 
stateOrProvinceName= State or Province Name (full name) 
localityName = Locality Name (eg, city) 
organizationName = Organization Name (eg, company) 
organizationalUnitName  = Organizational Unit Name (eg, section) 
commonName = Common Name (eg. YOUR name) 
commonName_max = 64 
emailAddress = Email Address 
emailAddress_max = 40 

[ req_attributes ] 
challengePassword = A challenge password 
challengePassword_min = 4 
challengePassword_max = 20 
unstructuredName= An optional company name

[ v3_ca ]
basicConstraints = CA:true