Mbed TLS 编码规范

原文 https://tls.mbed.org/kb/development/mbedtls-coding-standards



介绍 (Intro)

本文档描述了mbed TLS代码中使用的有关代码格式、命名约定、API约定、编码风格、文件结构和默认内容的首选项。当然,在某些情况下,我们会因为某些原因而偏离本文档。

代码格式 (Code Formatting)

mbed TLS源代码文件使用4个空格进行缩进,没有首选的最大行长度为80个字符的制表符。
每个代码语句都应该在自己的行上。应该避免使用下面这样的语句。

if( a == 1 ) { b = 1; do_function( b ); }
if( a == 1 ) do_function( a );

空格布局 (Space placement)

mbed TLS在整个代码中使用非标准空格布局,其中函数名和开始圆括号之间没有空格,所有圆括号与内容之间用一个空格分隔。

if( ( ret = demo_function( a, b, c ) ) != 0 )

函数定义也是如此

int demo_function( int a, const unsigned char *value, size_t len )

这个规则有几个例外:定义和强制转换的预处理指令,以及类似函数的宏的参数。

#if defined(MBEDTLS_HAVE_TIME)
timestamp = (uint32_t) time( NULL );

大括号放置/块声明 (Braces placement / block declaration)

大括号应该单独一行并与原始块保持同样的缩进。

if( do >= 1 )
{
    if( do == 1 )
    {
        [code block here]
    }
    else
    {
        [alternate code block]
    }
}

如果一个块只有一行源代码并且块判断条件只有一行,可以省略大括号。

if( do >= 1 )
    a = 2;

但是当块判断条件大于一行,则大括号不能省略。

if( do >= 1 &&
    this_big_statement_deserved_its_own_line == another_big_part )
{
    a = 2;
}

关联行/多行格式化和缩进(Related lines / Multi-line formatting and indentation)

应该格式化多个相关的源代码行,使其在视觉上易于阅读。例如:

#define GET_UINT32_LE( n, b, i )                        \
{                                                       \
    (n) = ( (uint32_t) (b)[(i)    ]       )             \
        | ( (uint32_t) (b)[(i) + 1] <<  8 )             \
        | ( (uint32_t) (b)[(i) + 2] << 16 )             \
        | ( (uint32_t) (b)[(i) + 3] << 24 );            \
}

if( my_super_var == second_super_var &&
    this_check_will_do != the_other_value )

do_function( ctx, this_is_a_value, value_b,
                  the_special_var );

void this_is_a_function( context_struct *ctx, size_t length,
                         unsigned char *result );

返回和 sizeof 的括号 (Extra parentheses for return and sizeof)

在mbed TLS返回语句中使用括号来包含返回值。

return( 0 );

类似地,sizeof 表达式总是使用括号,即使这不是必须的;

memset( buf, 0, sizeof( buf ) );

预编译指令 (Precompiler directives)

当使用预编译指令启用/禁用部分代码时,使用 #if 而不是 #ifdef 。如果到开始指令的距离大于几行或包含其他指令,要在 #endif 指令后添加注释。

#if define(MBEDTLS_HAVE_FEATURE)
[ten lines of code or other directives]
#endif /* MBEDTLS_HAVE_FEATURE */

命名约定 (Naming conventions)

命名空间 (Namespacing)

所有公共名称(函数、变量、类型、枚举常量、宏)都必须以 MBEDTLS_mbedtls_ 开头,通常后跟它们所属的模块的名称(如果适用的话,还包括子模块),后跟描述性部分。宏和枚举常量用大写加下划线; 其他名称用小写加下划线。例如:

mbedtls_x509_crt_parse_file()
mbedtls_aes_setkey_decrypt()

内部命名方式 (Local names)

MBEDTLS_mbedtls_ 前缀外,不在公共头文件中的静态函数和宏可以直接从函数名开始。
函数参数和局部变量不需要命名空间。它们应该使用描述性名称,除非它们非常短或者用于简单的循环,或者是约定俗成的名称(例如 p 表示指向缓冲区中当前位置的指针)。

长度和大小 (Lengths and sizes)

默认情况下,所有长度和大小都以字节(或数组的元素数量)为单位。如果名称引用以位为单位的长度或大小(这通常是 key 大小的情况),则名称必须显式包含位,例如mbedtls_pk_get_bitlen() 以位为单位返回键的大小,而 mbedtls_pk_get_len() 以字节为单位返回大小。此外,如果 key 的大小是以位或字节为单位的,文档应该总是明确地提到。

API约定 (API conventions)

模块上下文 (Module contexts)

如果模块使用上下文结构传递其状态,则模块应该包含 init()free() 函数,并在其前面加上模块或上下文名称。init() 函数必须始终返回 void 。如果必须执行一些可能会失败的初始化(例如分配内存),那么应该在一个单独的函数中执行,通常称为 setup() ,除非能找到一个更具描述性的名称。free 函数的作用是:释放上下文中分配的内存,但不释放上下文本身;它必须将上下文中或子结构中的任何数据归零。

mbedtls_cipher_context_t ctx;
mbedtls_cipher_init( &ctx );
ret = mbedtls_cipher_setup( &ctx, ... );
/* Check ret, goto cleanup on error */
/* Do things, goto cleanup on error */
cleanup:
mbedtls_cipher_free( &ctx );

init()setup() 部分分开的目的是,如果有多个上下文,可以首先调用所有的 init() 函数,然后将所有上下文都准备好传递给 free() 函数,以防其中一个 setup() 函数或其他地方发生错误。

返回类型 (Return type)

大多数函数应该返回 int ,更确切地说应该返回 0 ,否则返回一个负值错误代码。每个模块都定义了自己的错误代码,分配方案见 error.h 。例外情况:

  • 不可能失败的函数应该返回 void (例如mbedtls_cipher_init() )或直接返回请求的信息(例如 mbedtls_mpi_get_bit() )。
  • 查找某些信息的函数应该返回指向该信息的指针,如果没有找到则返回 NULL
  • 有些函数可能会使返回值复杂化,例如 mbedtls_asn1_write_len() 返回在成功或负错误代码上写入的长度。这模拟了一些标准函数的行为,比如 write()read() ,只是没有与errno 等价的函数:返回代码应该足够具体。
  • 一些内部函数可能在错误时返回 -1 ,而不是特定的错误代码;然后,如果要将错误传播回用户,则由调用函数选择更合适的错误代码。
  • 如果函数的名称明确表示布尔值(例如,名称包含“has”、“is”或“can”),则返回 0 表示 false,返回 1 表示 true。名称必须清楚:例如,如果存在对 foobar 的支持,mbdtls_has_foobar_support() 将返回 1 ;相反,如果支持 foobar (成功),mbedtls_check_foobar_support() 将返回 0 ;如果不支持 foobar,则返回 -1 或更具体的错误代码。所有名为 check 的函数必须遵循这条规则,并返回 0 来表示可接受/有效/存在/等等。为了避免混合使用 == 0!= 0 测试,通常应该优先选择检查名称。
  • 如果两个参数相等,则调用 cmp 的函数必须返回 0 ,如果有意义,则应该返回 -11,以指示哪个参数更大。

限制使用 in-out 参数 (Limited use of in-out parameters)

函数应该避免长度的 in-out 参数(输入缓存大小/写入的长度),因为它们可能会降低可读性。例如:

mbedtls_write_thing( void *thing, unsigned char *buf, size_t *len ); // no
mbedtls_write_thing( void *thing, unsigned char *buf, size_t buflen,
                     size_t *outlen ); // yes

然而,In-out 参数可以用于接收指向某个缓冲区的指针的函数,并在从该缓冲区解析或写入缓冲区后更新该指针。例如:

mbedtls_asn1_get_int( unsigned char **p,
                      const unsigned char *end,
                      int *value );

在这种情况下,end 参数应该始终指向入口时缓冲区的前面。
此外,上下文通常是 in-out 参数,这是可以的。

Const 校正 (Const correctness)

函数声明时应牢记 const 校正。作为指针且不被函数更改的参数应该标记为 const 指针。

int do_calc_length( const unsigned char *str )

编码风格 (Coding style)

Restricted C99

代码使用了C99 ISO标准的 Restricted 版本。C99(与C89 / ANSI C相比)的特点是:

  • 使用 stdint.h 头文件固定宽度类型(如 uint32_t )。
  • 增加 描述 bool 类型的头文件 stdbool.h
  • inline 关键字(尽管我们使用 __inline 替换了一些不支持它的编译器,比如armcc 5)。
  • 带有 // 的一行注释
  • 有界函数 snprintf()vsnprintf()
  • long longuint64_t 类型
  • 字符串最大长度超过了C89编译器需要支持的长度,
  • 在枚举列表中以逗号结尾。

因此,必须在代码块的顶部定义变量。在 mbed TLS 中,大多数变量甚至在函数块的顶部定义。
将来,我们可能会开始使用更多的C99(甚至C11)特性,因为对这些标准的支持将在通用编译器中扩展。

正确的参数和变量类型 (Proper argument and variable typing)

函数参数和变量应该正确键入。特别是 int 和 size 字段应该能够以独立于平台的方式保持它们的最大长度。对于缓冲区长度,这几乎总是意味着使用 size_t
对于不应该为负的值,使用无符号变量。在使用无符号变量构建循环时,一定要保持该类型。

关于 goto (Goto)

函数中允许使用 goto ,用于出现错误在函数返回之前进行清理。它还可以用来退出嵌套循环。在其他情况下,应该避免使用 goto

尽早退出/防止嵌套 (Exit early / prevent nesting)

结构化函数,以便尽早退出或转到退出代码。这可以防止代码块嵌套,并提高代码的可读性。

外部函数依赖关系 (External function dependencies)

mbed TLS 代码应该尽量减少使用外部函数。标准的 libc 函数是允许的,但应该在这里记录 What external dependencies does mbed TLS rely on?

通过预编译指令最小化代码 (Minimize code based on precompiler directives)

为了能够最小化代码大小和外部依赖,模块和模块功能的可用性由 config.h 中的预编译指令控制。每个模块应该至少定义自己的模块,以便启用/禁用该模块。使用模块头的其他文件应该只包含模块实际可用时的头文件。
由于使用 mbed TLS 的系统通常没有文件系统,因此应该在 MBEDTLS_FS_IO 指令中包含专门使用文件系统的函数。

尽量减少宏的使用(Minimize use of macros)

应该避免使用宏,除非可读性实际上随着宏的使用而提高,或者如果不使用宏的话代码大小将受到极大的影响。
下面的定义确实让使用它的代码更容易阅读。

#define GET_UINT32_LE( n, b, i )                        \
{                                                       \
    (n) = ( (uint32_t) (b)[(i)    ]       )             \
        | ( (uint32_t) (b)[(i) + 1] <<  8 )             \
        | ( (uint32_t) (b)[(i) + 2] << 16 )             \
        | ( (uint32_t) (b)[(i) + 3] << 24 );            \
}

及时清除安全相关内存 (Clear security relevant memory after use)

包含安全相关信息的内存在使用后和释放重用之前应该置零。函数 mbedtls_zeroize() 就是用于此目的,以防止不必要的编译器优化。

关于释放内存 (Clear / free what you made)

分配堆内存块的模块还负责稍后释放堆内存块,除非在头文件中的函数定义中有明确的说明。

模块自测 (Module self_test())

每个模块都应该有一个 self-test 函数(在检查 MBEDTLS_SELF_TEST 之间)。这个函数应该测试基本模块的完整性,但是应该避免执行耗时的测试。

Doxygen (Doxygen doc formatting)

头文件应该用 doxygen 风格的代码注释。使用 ‘’ 字符作为分隔符。

/**
 * \brief        A useless function just being present for documentation purposes.
 *               When calling this function, do not expect something to happen.
 *
 * \note         This function has no influence on code security.
 *
 * \param buf    Buffer to ignore
 * \param len    Length of buffer
 *
 * \return       0 if succesfully ignored, a module specific error code otherwise.
 */

接口间解耦合 (Loose coupling interfaces)

每个模块都应该与外部模块和函数保持松散耦合。在开发人员可能希望用本地版本替换部分代码的情况下,使用灵活的函数指针优于使用硬函数调用。

文件结构 (Generic file structure)

头文件 (Header files)

头文件的结构如下:

  • License Part (GPL)
  • Header file define for MBEDTLS_ {MODULE_NAME} _H
    #ifndef MBEDTLS_AES_H
    #define MBEDTLS_AES_H
    
  • Includes
  • Public defines (Generic and error codes) and portability code
  • C++ wrapper for C code
    #ifdef __cplusplus
    extern "C" {
    #endif
    
  • Public structures
  • Function declarations
  • C++ end wrapper
    #ifdef __cplusplus
    }
    #endif
    Header file end define
    #endif /* MBEDTLS_AES_H */
    

源文件 (Source files)

源文件的结构如下:

  • License Part (GPL)
  • Comments on possible standard documents used
  • Config include and precompiler directive for module
    #if !defined(MBEDTLS_CONFIG_FILE)
    #include "mbedtls/config.h"
    #else
    #include MBEDTLS_CONFIG_FILE
    #endif
    #if defined(MBEDTLS_AES_C)
    
  • Includes
  • Private local defines and portability code
  • Static variables
  • Function definitions
  • Precompiler end directive for module
    #endif /* MBEDTLS_AES_C */
    
发布了105 篇原创文章 · 获赞 230 · 访问量 36万+

猜你喜欢

转载自blog.csdn.net/aggresss/article/details/90311252
TLS