基于SM4的FPE算法测试

FPE简介

FPE,Format-Preserving Encryption,格式保留加密,也称为保形加密。是一种特殊的对称加密算法。FPE可以保证加密后的密文格式与加密前的明文格式完全相同。
FPE常用于数据脱密领域,可以对敏感数据(如手机号码,银行卡号等)进行加密存储,可以有效降低黑客入侵导致敏感信息泄露。另外,由于FPE可以保持加密后数据的格式不变,在一定程度上可以替代传统基于掩码的数据遮蔽方案。
本文测试基于FF1算法的FPE格式加密,标准FF1算法使用的是AES作为随机序列加密函数,本文使用国密SM4代替AES。
FF1(基于AES)源码:

FF1算法原理

FF1加密函数的输入是一个长度为n的字符串X和一个长度为t的tweak,经过10轮迭代输出是一个长度为n的字符串。
具体的FF1流程可以阅读NIST的标准规范
FF1的加密流程如下:
在这里插入图片描述
在这里插入图片描述
FF1的加密流程概括如下:
1 根据radix确定字符集合,例如radix=10,则集合为[0,9],如果是16,则集合是[0,9][A,F],如果是36,则集合是[0,9][a,z]
2 根据明文X部分数据、混淆数据生成字符序列,
3 然后通过加密函数,使用加密密钥key进行加密,转换符合集合的数据序列。
4 然后替换X中部分内容。
5 按照步骤2到5,循环迭代10次。
6 最终得到输出的密文序列。

FPE加密源码

void FF1_encrypt(const unsigned int *in, unsigned int *out, SM4_KEY *sm4_enc_ctx, const unsigned char *tweak, const unsigned int radix, size_t inlen, size_t tweaklen)
{
    BIGNUM *bnum = BN_new(),
           *y = BN_new(),
           *c = BN_new(),
           *anum = BN_new(),
           *qpow_u = BN_new(),
           *qpow_v = BN_new();
    BN_CTX *ctx = BN_CTX_new();

    union {
        long one;
        char little;
    } is_endian = { 1 };

    memcpy(out, in, inlen << 2);
    //步骤1
    int u = floor2(inlen, 1);
    int v = inlen - u;

    //步骤2
    unsigned int *A = out, *B = out + u;

    //计算 radix^u 和 radix^v  用于后续的步骤6vi.
    pow_uv(qpow_u, qpow_v, radix, u, v, ctx);

    //步骤3
    unsigned int temp = (unsigned int)ceil(v * log2(radix));
    const int b = ceil2(temp, 3);
    //步骤4
    const int d = 4 * ceil2(b, 2) + 4;

    
    //计算 pad长度, 用于后续的步骤6i. 代表填充0的长度
    int pad = ( (-tweaklen - b - 1) % 16 + 16 ) % 16;

    //初始化Q,P
    int Qlen = tweaklen + pad + 1 + b;
    unsigned char P[16];
    unsigned char *Q = (unsigned char *)OPENSSL_malloc(Qlen), *Bytes = (unsigned char *)OPENSSL_malloc(b);

    //步骤5, 设置 P初值,通过各种参数长度来生成
    P[0] = 0x1;
    P[1] = 0x2;
    P[2] = 0x1;
    P[7] = u % 256;
    if (is_endian.little) {
        temp = (radix << 8) | 10;
        P[3] = (temp >> 24) & 0xff;
        P[4] = (temp >> 16) & 0xff;
        P[5] = (temp >> 8) & 0xff;
        P[6] = temp & 0xff;
        P[8] = (inlen >> 24) & 0xff;
        P[9] = (inlen >> 16) & 0xff;
        P[10] = (inlen >> 8) & 0xff;
        P[11] = inlen & 0xff;
        P[12] = (tweaklen >> 24) & 0xff;
        P[13] = (tweaklen >> 16) & 0xff;
        P[14] = (tweaklen >> 8) & 0xff;
        P[15] = tweaklen & 0xff;
    } else {
        *( (unsigned int *)(P + 3) ) = (radix << 8) | 10;
        *( (unsigned int *)(P + 8) ) = inlen;
        *( (unsigned int *)(P + 12) ) = tweaklen;
    }

    // 设置Q初值,用于步骤6i.
    memcpy(Q, tweak, tweaklen);
    memset(Q + tweaklen, 0x00, pad);
    assert(tweaklen + pad - 1 <= Qlen);

    unsigned char R[16];
    int cnt = ceil2(d, 4) - 1;
    int Slen = 16 + cnt * 16;
    unsigned char *S = (unsigned char *)OPENSSL_malloc(Slen);
    for (int i = 0; i < FF1_ROUNDS; ++i) {
        // 步骤6v
        int m = (i & 1)? v: u;

        // 步骤6i
        Q[tweaklen + pad] = i & 0xff;
        str2num(bnum, B, radix, inlen - m, ctx);
        int BytesLen = BN_bn2bin(bnum, Bytes);
        memset(Q + Qlen - b, 0x00, b);

        int qtmp = Qlen - BytesLen;
        memcpy(Q + qtmp, Bytes, BytesLen);

        // 步骤6ii PRF(P || Q), P is always 16 bytes long
        SM4_encrypt(P, R, sm4_enc_ctx);
        int count = Qlen / 16;
        unsigned char Ri[16];
        unsigned char *Qi = Q;
        for (int cc = 0; cc < count; ++cc) {
            for (int j = 0; j < 16; ++j)    Ri[j] = Qi[j] ^ R[j];
            SM4_encrypt(Ri, R, sm4_enc_ctx);
            Qi += 16;
        }
        // 步骤6iii 
        unsigned char tmp[16], SS[16];
        memset(S, 0x00, Slen);
        assert(Slen >= 16);
        memcpy(S, R, 16);
        for (int j = 1; j <= cnt; ++j) {
            memset(tmp, 0x00, 16);

            if (is_endian.little) {
                // convert to big endian
                // full unroll
                tmp[15] = j & 0xff;
                tmp[14] = (j >> 8) & 0xff;
                tmp[13] = (j >> 16) & 0xff;
                tmp[12] = (j >> 24) & 0xff;
            } else *( (unsigned int *)tmp + 3 ) = j;

            for (int k = 0; k < 16; ++k)    tmp[k] ^= R[k];
            SM4_encrypt(tmp, SS, sm4_enc_ctx);
            assert((S + 16 * j)[0] == 0x00);
            assert(16 + 16 * j <= Slen);
            memcpy(S + 16 * j, SS, 16);
        }

        // 步骤6iv
        BN_bin2bn(S, d, y);
        // 步骤6vi
        // (num(A, radix, m) + y) % qpow(radix, m);
        str2num(anum, A, radix, m, ctx);
        // anum = (anum + y) mod qpow_uv
        // 步骤6vii
        if (m == u)    BN_mod_add(c, anum, y, qpow_u, ctx);
        else    BN_mod_add(c, anum, y, qpow_v, ctx);

        // swap A and B
        assert(A != B);
        // 步骤6viii
        A = (unsigned int *)( (uintptr_t)A ^ (uintptr_t)B );
        B = (unsigned int *)( (uintptr_t)B ^ (uintptr_t)A );
        A = (unsigned int *)( (uintptr_t)A ^ (uintptr_t)B );
        num2str(c, B, radix, m, ctx);

    }

    // free the space
    BN_clear_free(anum);
    BN_clear_free(bnum);
    BN_clear_free(c);
    BN_clear_free(y);
    BN_clear_free(qpow_u);
    BN_clear_free(qpow_v);
    BN_CTX_free(ctx);
    OPENSSL_free(Bytes);
    OPENSSL_free(Q);
    OPENSSL_free(S);
    return;
}

测试数据

enc_func=sm4
userkey:
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, 
X[]="3216"
tweak[]="1329999"
步骤1:
u = 2
v = 2
步骤2:
A:
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
B:
0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 
步骤3:
b = 1
步骤4:
d = 8
步骤5:
P:
0x01, 0x02, 0x01, 0x00, 0x00, 0x0a, 0x0a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 
##############################################################
loop 0
##############################################################
步骤6i:
Q:
0x31, 0x33, 0x32, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 
步骤6ii:
R:
0x43, 0xa1, 0xeb, 0x82, 0xda, 0xbb, 0xe2, 0xf8, 0x41, 0xcf, 0x1c, 0x1f, 0x12, 0x2c, 0xc4, 0x99, 
步骤6ii:
S:
0x43, 0xa1, 0xeb, 0x82, 0xda, 0xbb, 0xe2, 0xf8, 0x41, 0xcf, 0x1c, 0x1f, 0x12, 0x2c, 0xc4, 0x99, 
步骤6iv:
y:
0x43, 0xa1, 0xeb, 0x82, 0xda, 0xbb, 0xe2, 0xf8, 
步骤6vi:
c:
0x18, 
步骤6vii:
C:
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
##############################################################
loop 1
##############################################################
步骤6i:
Q:
0x31, 0x33, 0x32, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 
步骤6ii:
R:
0xf9, 0x76, 0x78, 0xd2, 0xe5, 0xda, 0x6f, 0xf3, 0x52, 0x0c, 0xf6, 0x0c, 0x80, 0x61, 0x8b, 0x3b, 
步骤6ii:
S:
0xf9, 0x76, 0x78, 0xd2, 0xe5, 0xda, 0x6f, 0xf3, 0x52, 0x0c, 0xf6, 0x0c, 0x80, 0x61, 0x8b, 0x3b, 
步骤6iv:
y:
0xf9, 0x76, 0x78, 0xd2, 0xe5, 0xda, 0x6f, 0xf3, 
步骤6vi:
c:
0x27, 
步骤6vii:
C:
0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 
##############################################################
loop 2
##############################################################
步骤6i:
Q:
0x31, 0x33, 0x32, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x27, 
步骤6ii:
R:
0x37, 0x75, 0x89, 0x40, 0xe5, 0x39, 0x62, 0x54, 0xbb, 0x0e, 0x82, 0x8a, 0x28, 0xfa, 0xa3, 0x3f, 
步骤6ii:
S:
0x37, 0x75, 0x89, 0x40, 0xe5, 0x39, 0x62, 0x54, 0xbb, 0x0e, 0x82, 0x8a, 0x28, 0xfa, 0xa3, 0x3f, 
步骤6iv:
y:
0x37, 0x75, 0x89, 0x40, 0xe5, 0x39, 0x62, 0x54, 
步骤6vi:
c:
步骤6vii:
C:
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
##############################################################
loop 3
##############################################################
步骤6i:
Q:
0x31, 0x33, 0x32, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 
步骤6ii:
R:
0x6e, 0x00, 0xa5, 0xfa, 0x6a, 0xed, 0x83, 0x05, 0xb3, 0x9c, 0xf8, 0x9c, 0x08, 0x67, 0x57, 0xc3, 
步骤6ii:
S:
0x6e, 0x00, 0xa5, 0xfa, 0x6a, 0xed, 0x83, 0x05, 0xb3, 0x9c, 0xf8, 0x9c, 0x08, 0x67, 0x57, 0xc3, 
步骤6iv:
y:
0x6e, 0x00, 0xa5, 0xfa, 0x6a, 0xed, 0x83, 0x05, 
步骤6vi:
c:
0x08, 
步骤6vii:
C:
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
##############################################################
loop 4
##############################################################
步骤6i:
Q:
0x31, 0x33, 0x32, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 
步骤6ii:
R:
0xfa, 0x1d, 0x07, 0xb9, 0x63, 0x6f, 0xe7, 0xf5, 0x91, 0x58, 0x42, 0x75, 0xe7, 0x03, 0x97, 0xcb, 
步骤6ii:
S:
0xfa, 0x1d, 0x07, 0xb9, 0x63, 0x6f, 0xe7, 0xf5, 0x91, 0x58, 0x42, 0x75, 0xe7, 0x03, 0x97, 0xcb, 
步骤6iv:
y:
0xfa, 0x1d, 0x07, 0xb9, 0x63, 0x6f, 0xe7, 0xf5, 
步骤6vi:
c:
0x4d, 
步骤6vii:
C:
0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 
##############################################################
loop 5
##############################################################
步骤6i:
Q:
0x31, 0x33, 0x32, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x4d, 
步骤6ii:
R:
0x26, 0xe3, 0x7e, 0xd3, 0xf8, 0x79, 0x8f, 0x29, 0xf2, 0xcc, 0xc8, 0x64, 0xc9, 0x59, 0x89, 0x14, 
步骤6ii:
S:
0x26, 0xe3, 0x7e, 0xd3, 0xf8, 0x79, 0x8f, 0x29, 0xf2, 0xcc, 0xc8, 0x64, 0xc9, 0x59, 0x89, 0x14, 
步骤6iv:
y:
0x26, 0xe3, 0x7e, 0xd3, 0xf8, 0x79, 0x8f, 0x29, 
步骤6vi:
c:
0x5d, 
步骤6vii:
C:
0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 
##############################################################
loop 6
##############################################################
步骤6i:
Q:
0x31, 0x33, 0x32, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x5d, 
步骤6ii:
R:
0xaa, 0xaf, 0x90, 0x82, 0xc8, 0x2b, 0x30, 0x6b, 0x6d, 0x68, 0xdc, 0x13, 0x6f, 0x76, 0xef, 0xa0, 
步骤6ii:
S:
0xaa, 0xaf, 0x90, 0x82, 0xc8, 0x2b, 0x30, 0x6b, 0x6d, 0x68, 0xdc, 0x13, 0x6f, 0x76, 0xef, 0xa0, 
步骤6iv:
y:
0xaa, 0xaf, 0x90, 0x82, 0xc8, 0x2b, 0x30, 0x6b, 
步骤6vi:
c:
0x40, 
步骤6vii:
C:
0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
##############################################################
loop 7
##############################################################
步骤6i:
Q:
0x31, 0x33, 0x32, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x40, 
步骤6ii:
R:
0x4f, 0x13, 0x35, 0xe8, 0x2b, 0xe5, 0x80, 0x7f, 0x95, 0xd1, 0x42, 0xf7, 0x8b, 0x8f, 0x21, 0xf8, 
步骤6ii:
S:
0x4f, 0x13, 0x35, 0xe8, 0x2b, 0xe5, 0x80, 0x7f, 0x95, 0xd1, 0x42, 0xf7, 0x8b, 0x8f, 0x21, 0xf8, 
步骤6iv:
y:
0x4f, 0x13, 0x35, 0xe8, 0x2b, 0xe5, 0x80, 0x7f, 
步骤6vi:
c:
0x1c, 
步骤6vii:
C:
0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
##############################################################
loop 8
##############################################################
步骤6i:
Q:
0x31, 0x33, 0x32, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 
步骤6ii:
R:
0x7b, 0xf4, 0x22, 0x21, 0xa7, 0x16, 0x7f, 0x69, 0xef, 0x51, 0x3a, 0x47, 0xdb, 0xbd, 0x82, 0x46, 
步骤6ii:
S:
0x7b, 0xf4, 0x22, 0x21, 0xa7, 0x16, 0x7f, 0x69, 0xef, 0x51, 0x3a, 0x47, 0xdb, 0xbd, 0x82, 0x46, 
步骤6iv:
y:
0x7b, 0xf4, 0x22, 0x21, 0xa7, 0x16, 0x7f, 0x69, 
步骤6vi:
c:
0x59, 
步骤6vii:
C:
0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 
##############################################################
loop 9
##############################################################
步骤6i:
Q:
0x31, 0x33, 0x32, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x59, 
步骤6ii:
R:
0x7a, 0x80, 0x65, 0x7e, 0xca, 0x91, 0x0d, 0x10, 0x96, 0x01, 0x59, 0x53, 0xe9, 0x11, 0xc6, 0x09, 
步骤6ii:
S:
0x7a, 0x80, 0x65, 0x7e, 0xca, 0x91, 0x0d, 0x10, 0x96, 0x01, 0x59, 0x53, 0xe9, 0x11, 0xc6, 0x09, 
步骤6iv:
y:
0x7a, 0x80, 0x65, 0x7e, 0xca, 0x91, 0x0d, 0x10, 
步骤6vi:
c:
0x38, 
步骤6vii:
C:
0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 
加密结果:
FPEEncrypt Out(4 char):8956

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

viqjeee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值