代码之家  ›  专栏  ›  技术社区  ›  Vitomakes

生成一组可在不保留白名单的情况下进行验证的唯一密钥

  •  1
  • Vitomakes  · 技术社区  · 6 年前

    我需要生成一组10字节的唯一ID。这些集合可能相当大(即10000个值),并由有限的内存设备检查其有效性。因此,有人在设备中输入一个ID,设备应该能够识别该ID是否真实(由我生成)。
    基本的方法是将同一组ID存储在设备的内存中,并对照列表进行检查,但我不能使用所有的内存。
    我认为第二种方法是使用CRC或哈希函数:例如,所有CRC为X的ID都被启用。这里的问题是,我应该遍历所有可能的ID组合,以找到提供正确CRC的ID。
    理想情况下,我希望找到一个或两个这样工作的函数:

     uint8_t * generate_ID(uint16_t index);
     bool is_valid validate_key(uint8_t * ID);
     //optional
     uint16_t index find_index(uint8_t * ID);
    
     //example
     //generate id value from index 0
     uint8_t ID[10] = generate_ID(0)
     //id is now {0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b}
    
     bool is_valid = validate_key(ID);
     //is_valid is True
     uint16_t index = find_index(ID);
     //index is now 0
     ID[0] = 0xff; //change ID with random value
     is_valid = validate_key(ID);
     //is_valid is now False
    
     //BONUS: use also a "seed" value, so that I can differentiate through sets of ids:
     uint8_t * generate_ID(uint16_t index, uint16_t seed);
     bool is_valid validate_key(uint8_t * ID, uint16_t seed);
    

    find_index()是可选的,因为一旦我知道键是有效的,我就可以简单地遍历所有索引以找到匹配的索引。

    2 回复  |  直到 6 年前
        1
  •  2
  •   Barmak Shemirani    6 年前

    一个10字节的密钥不足以保证任何安全。

    您需要一个安全的哈希函数,如SHA2-256,其输出长度为32字节。SHA2可以很容易地在大多数系统上实现。

    [text + hash]
    

    第一部分类似于“用户名”,第二部分类似于“密码”

    您还需要一个“密钥”。此密钥是存储在软件中的字节数组。然后将“密钥”添加到“用户名”中。查找结果字符串的SHA2哈希。现在您有了一个输出,即原始文本的长度+32字节的散列。

    您可以将此密钥用作唯一的可验证ID。

    如果保密性和唯一性不是一个大问题,那么您可以使用输出为16字节的MD5。将纯文本更改为二进制,这样它可以在更少的字节中存储更多信息,而您的最终密钥将只有20个字节。您可以将其减少一点,但不建议减少到10字节。


    https://github.com/B-Con/crypto-algorithms (我不确定它是否在big endian机器上工作)

    void sha2(BYTE* dst, const BYTE* src, int len)
    {
        SHA256_CTX ctx;
        sha256_init(&ctx);
        sha256_update(&ctx, (const BYTE*)src, len);
        sha256_final(&ctx, (BYTE*)dst);
    }
    
    void create_verifiable_id(const BYTE* source, BYTE *uid)
    {
        BYTE hash[32];
        sha2(hash, source, ID_SIZE);
    
        //combine source + hash
        memcpy(uid, source, ID_SIZE);
        memcpy(uid + ID_SIZE, hash, 32);
    }
    
    int test_verfiable_id(const BYTE *uid)
    {
        BYTE hash[32];
        sha2(hash, uid, ID_SIZE);
    
        //hash should match the second part of uid
        return memcmp(hash, uid + ID_SIZE, 32) == 0;
    }
    
    int main(void)
    {
        //use a number from 0 to 0xFFFFFFFF, store in buf (4 bytes)
        //this is the "plain text" portion
        int number = 0x12345678;
        BYTE buf[ID_SIZE];
        for(int i = 0; i < sizeof(buf); i++)
        {
            buf[i] = number & 0xFF;
            number >>= 8;
        }
    
        //add sha2 to "plain text" to make verifiable id
        BYTE verifiable_id[32 + ID_SIZE];
        create_verifiable_id(buf, verifiable_id);
    
        printf("UID as hex string:\n");
        for(int i = 0; i < 32 + ID_SIZE; i++)
            printf("%02X", verifiable_id[i] & 0xFF);
        printf("\n");
    
        printf("Test (should succeed): %d\n", test_verfiable_id(verifiable_id));
    
        //change verifiable_id and test it again
        verifiable_id[0]++;
        printf("Test (should fail): %d\n", test_verfiable_id(verifiable_id));
        return 0;
    }
    
        2
  •  1
  •   Jim Mischel    6 年前

    一种非常简单的方法是使用模乘逆,正如我在博客中所描述的: http://blog.mischel.com/2017/06/20/how-to-generate-random-looking-keys/

    其思想是将数字从1映射到某个数字x,以便每个数字在同一范围内生成唯一的值。例如,映射可能是:

    1 -> 9875
    2 -> 362
    3 -> 5247
    ...
    

    这是一个可逆的计算。如果f(1)=>9875,然后g(9875)=>1.

    在博客文章中显示的代码中,您可以通过修改 x m

    如果希望键为字母数字,则需要在生成整数后对其进行编码。然后,在用户输入后,在您尝试验证之前,您必须将它们解码回整数。

    M 非常多。设置 x 适当地,优选大于的素数 M x M

    您可以将其扩展到多个设备,只需为每个设备提供其认为有效的起始值和结束值。无论如何,反向关键点计算是相同的。

    x 值,并知道设备设置为接受的数字范围,然后他可以生成密钥来击败系统。这是否是个问题只有你能回答。有人试图击败您的系统的风险是什么?如果他们成功,成本是多少?