每次为给定数字生成相同的随机唯一数字
Generating same random unique number every time for a given number
我们有一组组,每个组都有数十亿条记录。每个组将由一个 id 唯一标识,组中的每条记录将由一个 id 唯一标识。通过组合这两个 ids(concat(groupid, recordid)) 我们可以识别跨组的记录。
现在我们正在尝试更改这些 id(串联)值,在我们的报告中,我们不想与我们维护的客户共享直接 id,而是希望将 id 转换为其他一些 uniqueid并与客户分享,这样他们就很难识别组。
我们已经尝试为此生成哈希值(hmac256 哈希),但这对我们的客户不起作用,因为它大大增加了他们的存储空间。如果当前 id 的长度为 20 位,则生成 45 个字符的哈希值将不起作用。因此,寻找更好的选择来生成 20 digit/string 或至少 25 digit/string 个不会发生冲突的唯一值。
正在寻找一些关于此的信息。
我们已经尝试为此生成哈希值(hmac256 哈希),但这对我们的客户不起作用,因为它大大增加了他们的存储空间。如果当前id的长度是20位,生成45个字符的hash是不行的。
示例:
groupId=910612349078
recordId=6234091234
对于以上两个值,系统截至今天生成的唯一 ID 如下所示:
uniqueId=9106123490786234091234 (concat(groupId, recordId))
唯一 ID 的预期行为将是某个 random/hash 值:
newUniqueId = 一些散列或一些随机数
我会尝试使用像样的快速安全散列函数 - ChaCha20.
ChaCha20 生成每个 64 字节的伪随机块,这些块与要加密或解密的数据进行异或运算。它需要 256 位密钥和 64 位随机数。假设你所有的 ID 都适合 64 位(毕竟 20 位数字非常接近 264-1,即 18,446,744,073,709,551,615),设置你的密钥,使用组 ID 作为随机数,并加密你记录 Id XORing ChaCha20 输出。
你没有提到平台,所以没有代码,但是 C/C++/Go/Rust/...
中有很多好的实现
更新
您可以尝试使用ChaCha20 作为计数器模式的分组密码。那么您的输入将是键,64 位组 ID 作为随机数,64 位记录 ID 作为计数器。输出将是您发送给客户的 64 位块。
好的,我玩过 Monocypher 实现(link 在 ChaCha20 页面的底部),并按照上面的建议将其用作分组密码。到目前为止,它对我来说看起来不错 - 不同的 groupId/recordId 产生了截然不同的加扰值。代码使用 C++,使用 LLVM 8 和 MSVC++19.2 编译。我把整个项目放在 Github here.
uint64_t scramble(const uint64_t groupId,
const uint64_t recordId,
const char* text_key = nullptr)
{
uint8_t key[32] = {0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a,
0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0,
0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09,
0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0}; // same key as in example 3 below
if (text_key != nullptr) {
hex2byte(text_key, key);
}
const uint8_t* nonce = reinterpret_cast<const uint8_t*>(&groupId); // nonce would be our group id
crypto_chacha_ctx ctx;
crypto_chacha20_init(&ctx, key, nonce); // initialize ChaCha20
crypto_chacha20_set_ctr(&ctx, recordId); // block counter is our record Id
uint64_t input = 0x0000000000000000; // Just get the block out. Chacha will make random block and XOR it with input text.
// XOR with zeroes preserve Chacha block.
// Or 0xFFFFFFFFFFFFFFFF to get it iverted
uint64_t output;
crypto_chacha20_encrypt(&ctx,
reinterpret_cast<uint8_t*>(&output),
reinterpret_cast<const uint8_t*>(&input),
sizeof(input));
return output;
}
int main()
{
// Test values from http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04#appendix-A.2
srand(123); //Test results will be consistent
test_ietf_chacha20("0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000", "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586", 0, 1);
test_ietf_chacha20("0000000000000000000000000000000000000000000000000000000000000001", "0000000000000002", "416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e7472696275746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e20224945544620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c2073746174656d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c656374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207768696368206172652061646472657373656420746f", "a3fbf07df3fa2fde4f376ca23e82737041605d9f4f4f57bd8cff2c1d4b7955ec2a97948bd3722915c8f3d337f7d370050e9e96d647b7c39f56e031ca5eb6250d4042e02785ececfa4b4bb5e8ead0440e20b6e8db09d881a7c6132f420e52795042bdfa7773d8a9051447b3291ce1411c680465552aa6c405b7764d5e87bea85ad00f8449ed8f72d0d662ab052691ca66424bc86d2df80ea41f43abf937d3259dc4b2d0dfb48a6c9139ddd7f76966e928e635553ba76c5c879d7b35d49eb2e62b0871cdac638939e25e8a1e0ef9d5280fa8ca328b351c3c765989cbcf3daa8b6ccc3aaf9f3979c92b3720fc88dc95ed84a1be059c6499b9fda236e7e818b04b0bc39c1e876b193bfe5569753f88128cc08aaa9b63d1a16f80ef2554d7189c411f5869ca52c5b83fa36ff216b9c1d30062bebcfd2dc5bce0911934fda79a86f6e698ced759c3ff9b6477338f3da4f9cd8514ea9982ccafb341b2384dd902f3d1ab7ac61dd29c6f21ba5b862f3730e37cfdc4fd806c22f221", 1, 2);
test_ietf_chacha20("1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", "0000000000000002", "2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e642067696d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e6420746865206d6f6d65207261746873206f757467726162652e", "62e6347f95ed87a45ffae7426f27a1df5fb69110044c0d73118effa95b01e5cf166d3df2d721caf9b21e5fb14c616871fd84c54f9d65b283196c7fe4f60553ebf39c6402c42234e32a356b3e764312a61a5532055716ead6962568f87d3f3f7704c6a8d1bcd1bf4d50d6154b6da731b187b58dfd728afa36757a797ac188d1", 42, 3);
std::cout << '\n';
uint64_t scrambled{0ULL};
scrambled = scramble(10, 12345);
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(100, 12345);
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(11, 12345); // group id differ by 1
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(10, 12346); // record id differ by 1
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(0, 0);
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(0, 1);
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(1, 0);
std::cout << "0x" << std::hex << scrambled << '\n';
return 0;
}
最后 7 行的输出是
0x6321d1e43d4ab340
0x7dd7e1cfab075076
0x1e8483e0081fa6ee
0xb6084d3151900667
0x7794b6c405fbf46
0x115ddf32dffd75df
0x87a199dff4e4326a
更新二
对不起,我打错了,可能误导了你(块的大小为 64 位,当它是 64 字节,现在更正)。
是的,块大小是 64 字节(实际上是 32 位中的 16 uint32_t)。而 ChaCha20 能够产生 264 个这样的块。所以你可以让你的输入大小为 12 个字节,输出将是块的前 12 个字节。请检查回购中的更新代码。我已经反转打印 12 字节块以显示它与以前相同(字节顺序)。您可以将任何乱序大小放入 64。
我们有一组组,每个组都有数十亿条记录。每个组将由一个 id 唯一标识,组中的每条记录将由一个 id 唯一标识。通过组合这两个 ids(concat(groupid, recordid)) 我们可以识别跨组的记录。
现在我们正在尝试更改这些 id(串联)值,在我们的报告中,我们不想与我们维护的客户共享直接 id,而是希望将 id 转换为其他一些 uniqueid并与客户分享,这样他们就很难识别组。
我们已经尝试为此生成哈希值(hmac256 哈希),但这对我们的客户不起作用,因为它大大增加了他们的存储空间。如果当前 id 的长度为 20 位,则生成 45 个字符的哈希值将不起作用。因此,寻找更好的选择来生成 20 digit/string 或至少 25 digit/string 个不会发生冲突的唯一值。
正在寻找一些关于此的信息。
我们已经尝试为此生成哈希值(hmac256 哈希),但这对我们的客户不起作用,因为它大大增加了他们的存储空间。如果当前id的长度是20位,生成45个字符的hash是不行的。
示例: groupId=910612349078 recordId=6234091234
对于以上两个值,系统截至今天生成的唯一 ID 如下所示: uniqueId=9106123490786234091234 (concat(groupId, recordId))
唯一 ID 的预期行为将是某个 random/hash 值: newUniqueId = 一些散列或一些随机数
我会尝试使用像样的快速安全散列函数 - ChaCha20.
ChaCha20 生成每个 64 字节的伪随机块,这些块与要加密或解密的数据进行异或运算。它需要 256 位密钥和 64 位随机数。假设你所有的 ID 都适合 64 位(毕竟 20 位数字非常接近 264-1,即 18,446,744,073,709,551,615),设置你的密钥,使用组 ID 作为随机数,并加密你记录 Id XORing ChaCha20 输出。
你没有提到平台,所以没有代码,但是 C/C++/Go/Rust/...
中有很多好的实现更新
您可以尝试使用ChaCha20 作为计数器模式的分组密码。那么您的输入将是键,64 位组 ID 作为随机数,64 位记录 ID 作为计数器。输出将是您发送给客户的 64 位块。
好的,我玩过 Monocypher 实现(link 在 ChaCha20 页面的底部),并按照上面的建议将其用作分组密码。到目前为止,它对我来说看起来不错 - 不同的 groupId/recordId 产生了截然不同的加扰值。代码使用 C++,使用 LLVM 8 和 MSVC++19.2 编译。我把整个项目放在 Github here.
uint64_t scramble(const uint64_t groupId,
const uint64_t recordId,
const char* text_key = nullptr)
{
uint8_t key[32] = {0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a,
0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0,
0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09,
0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0}; // same key as in example 3 below
if (text_key != nullptr) {
hex2byte(text_key, key);
}
const uint8_t* nonce = reinterpret_cast<const uint8_t*>(&groupId); // nonce would be our group id
crypto_chacha_ctx ctx;
crypto_chacha20_init(&ctx, key, nonce); // initialize ChaCha20
crypto_chacha20_set_ctr(&ctx, recordId); // block counter is our record Id
uint64_t input = 0x0000000000000000; // Just get the block out. Chacha will make random block and XOR it with input text.
// XOR with zeroes preserve Chacha block.
// Or 0xFFFFFFFFFFFFFFFF to get it iverted
uint64_t output;
crypto_chacha20_encrypt(&ctx,
reinterpret_cast<uint8_t*>(&output),
reinterpret_cast<const uint8_t*>(&input),
sizeof(input));
return output;
}
int main()
{
// Test values from http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04#appendix-A.2
srand(123); //Test results will be consistent
test_ietf_chacha20("0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000", "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586", 0, 1);
test_ietf_chacha20("0000000000000000000000000000000000000000000000000000000000000001", "0000000000000002", "416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e7472696275746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e20224945544620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c2073746174656d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c656374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207768696368206172652061646472657373656420746f", "a3fbf07df3fa2fde4f376ca23e82737041605d9f4f4f57bd8cff2c1d4b7955ec2a97948bd3722915c8f3d337f7d370050e9e96d647b7c39f56e031ca5eb6250d4042e02785ececfa4b4bb5e8ead0440e20b6e8db09d881a7c6132f420e52795042bdfa7773d8a9051447b3291ce1411c680465552aa6c405b7764d5e87bea85ad00f8449ed8f72d0d662ab052691ca66424bc86d2df80ea41f43abf937d3259dc4b2d0dfb48a6c9139ddd7f76966e928e635553ba76c5c879d7b35d49eb2e62b0871cdac638939e25e8a1e0ef9d5280fa8ca328b351c3c765989cbcf3daa8b6ccc3aaf9f3979c92b3720fc88dc95ed84a1be059c6499b9fda236e7e818b04b0bc39c1e876b193bfe5569753f88128cc08aaa9b63d1a16f80ef2554d7189c411f5869ca52c5b83fa36ff216b9c1d30062bebcfd2dc5bce0911934fda79a86f6e698ced759c3ff9b6477338f3da4f9cd8514ea9982ccafb341b2384dd902f3d1ab7ac61dd29c6f21ba5b862f3730e37cfdc4fd806c22f221", 1, 2);
test_ietf_chacha20("1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", "0000000000000002", "2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e642067696d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e6420746865206d6f6d65207261746873206f757467726162652e", "62e6347f95ed87a45ffae7426f27a1df5fb69110044c0d73118effa95b01e5cf166d3df2d721caf9b21e5fb14c616871fd84c54f9d65b283196c7fe4f60553ebf39c6402c42234e32a356b3e764312a61a5532055716ead6962568f87d3f3f7704c6a8d1bcd1bf4d50d6154b6da731b187b58dfd728afa36757a797ac188d1", 42, 3);
std::cout << '\n';
uint64_t scrambled{0ULL};
scrambled = scramble(10, 12345);
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(100, 12345);
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(11, 12345); // group id differ by 1
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(10, 12346); // record id differ by 1
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(0, 0);
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(0, 1);
std::cout << "0x" << std::hex << scrambled << '\n';
scrambled = scramble(1, 0);
std::cout << "0x" << std::hex << scrambled << '\n';
return 0;
}
最后 7 行的输出是
0x6321d1e43d4ab340
0x7dd7e1cfab075076
0x1e8483e0081fa6ee
0xb6084d3151900667
0x7794b6c405fbf46
0x115ddf32dffd75df
0x87a199dff4e4326a
更新二
对不起,我打错了,可能误导了你(块的大小为 64 位,当它是 64 字节,现在更正)。
是的,块大小是 64 字节(实际上是 32 位中的 16 uint32_t)。而 ChaCha20 能够产生 264 个这样的块。所以你可以让你的输入大小为 12 个字节,输出将是块的前 12 个字节。请检查回购中的更新代码。我已经反转打印 12 字节块以显示它与以前相同(字节顺序)。您可以将任何乱序大小放入 64。