Hidden Number Problem

在最近的很多比赛都遇到了这个Hidden Number Problem(HNP),所以抽个时间来仔细学习一下,然后马上要HGAME2023了,正好准备一下题目给新生写。

Introduce

HNP问题第一次被提出是在这篇论文中 “Hardness of computing the most significant bits of secret keys in Diffie-Hellman and related schemes” by Dan Boneh and Ramarathnam Venkatesan。HNP问题本来是用来研究Diffie-Hellman共享密钥中的最高有效位是否与整个秘密一样难以计算?并且D. Boneh和R. Venkatesan还展示了一种有效的算法,用于在有足够大的位泄漏的情况下恢复密钥。

这个方法用到了格和格基规约的算法,一开始学习格密码时把重点放在了基于格的密钥系统的学习上,但格终究是数学上的东西,数学说白了就是一个工具,那么格自然也是一个工具,只不过我们把格这个有力的工具用在了密码分析上而已。

How is the HNP defined

论文中首先提出了most significant bits(MSB)的定义。首先令\(p\)是一个素数,\(n\)\(p\)的二进制位数,即\(n=log_2(p)\),用\(x\ mod\ p\)来表示一个定义在有限域的数\(a \in GF(p)\),即\(x \equiv a \pmod{p}\)。定义\(MSB_k(x)\)的值为\(t\)并且 \((t-1)\cdot p/2^k\leq x <t \cdot p/2^k\),或者更简单的定义

\[MSB_k(x)=z,\quad |x-z|< p/2^k \]

About the \(MSB_k(x)\)

一看到most signficant bits可能很多人都会想当然的认为是\(x\)的最高\(k\)位。其实不然,根据定义,可以写一个小demo

from random import randint
from sage.all import *
from Crypto.Util.number import getPrime
# Some parameters of the game, chosen for simplicity.

# p - A prime number for our field.
p = getPrime(128)

# n - The number of bits in `p`.
n = ceil(log(p, 2))

# k - The number of significant bits revealed by the oracle.
# Using parameters from Thereom 1.
k = ceil(sqrt(n)) + ceil(log(n, 2))
print(f"The number of significant bits = {k}")

def msb(query):
    """Returns the MSB of query based on the global paramters p, k.
    """
    while True:
        z = randint(1, p-1)
        answer = abs(query - z)
        if answer < p / 2**(k+1):
            break
    return z

x = randint(1, p-1)
print(f"x = {x}")
print(f"MSB's output = {msb(x)}")
print(f"most k bit of x = {x >> (n - k)}")

'''
The number of significant bits = 19
x = 54914319491231438995398843041850226262
MSB's output = 54914493779494223381640832877194242316
|MSB(x) - x| = 124182961411716441204715130169529
most k bit of x = 84608
'''

如何去理解这个\(MSB_k(x)\)呢,不难得出以下几点:

  • 根据\(MSB_k(x)\)的定义,有\(u=x\)是一定满足\(|x-u|\leq\frac{p}{2^{k+1}}\)的,那么\(u=x\)一定是\(MSB\)的一个输出,为什么是一个呢,因为这个不等式肯定是多解的。

  • \(k\)增大时,不等式的右边\(\frac{p}{2^{k+1}}\)和会快速的缩减,意味着不等式左边的\(z\)也就是\(MSB\)的输出要接近\(x\)\(k\)越大,\(MSB_k(x)\)越接近\(x\)

由于\(k\)越大,\(MSB_k(x)\)越接近\(x\),也就是说,对于越大的\(k\),我们得到的数就泄漏了越多的\(x\)的有效位。

然后定义Hidden Number Problem(HNP):确定\(p\)\(k\),已知\(MSB_k(\alpha g^x\ mod \ p)\)\(\alpha\)

论文中提到如果可以请求到正确的\(MSB_k(\alpha\cdot t\ mod\ p)\),这里\(t=g^x\),那么恢复\(\alpha\)是一件简单的事情。

Main Results

这一节主要讲的是多大的k可以来解决HNP问题。直接上定理。

Theorem 1

\(\alpha\)是定义在有限域\(GF(p)\)的一个数, \(k=\lceil\sqrt{n}\rceil+\lceil\log n\rceil\)\(\mathcal{O}(t)=\operatorname{MSB}_{k}(\alpha t \bmod p)\)。存在一种算法A可以在多项式时间内解决HNP。

\[\underset{t_{1}, \ldots, t_{d}}{\operatorname{Pr}}\left[A\left(t_{1}, \ldots, t_{d}, \mathcal{O}\left(t_{1}\right), \ldots, \mathcal{O}\left(t_{d}\right)\right)=\alpha\right] \geq \frac{1}{2} \]

其中\(d=2\sqrt{n}\)\(t_1,\cdots,t_d\)是均匀的,独立的随机从\(\mathbb{Z}_{p}^{*}\)中选择。

这里均匀的,独立的随机选择应该是为了保证数据的不相关性。

Theorem 2

\(k=\lceil\sqrt{n}\rceil+\lceil\log{n}\rceil\) ,给出一个有效的算法从\(g^a, g^b\)计算\(MSB_k(g^{ab})\),存在一个算法可以在多项式时间内计算\(g^{ab}\)。也就是已知\(MSB_k(g^{ab})\)\(g^{ab}\)

Proof

首先回顾一下格这个东西。格可以这个样子定义

\[L=\left\{y: y=\sum_{i=1}^{d} t_{i} b_{i}, t_{i} \in \mathbb{Z}\right\} \]

\(d\)是格\(L\)的阶,\(\{b_i\}={b_1,\cdots,c_d}\)\(L\)的一组线性无关向量,被称为\(L\)的基,\(\sum^{d}_{i=1}t_ib_i\)是这个基的一个线性组合。利用LLL格基规约算法可以从一个给定的格\(L\)和一个点\(v\)(不必要是格上的点),找到一个最靠近\(v\)的格点。

接下来证明Theorem 1。

\(d=2\lceil\sqrt{n}\rceil\)\(k=k=\lceil\sqrt{n}\rceil+\lceil\log n\rceil\),然后假设我们已经获得了\(d\)个正确的MSB oracle \(a_1, \cdots, a_d\),对应的输入为\(t_1,\cdots,t_d\)。根据MSB的定义,我们有对于所有的a和t

\[|\alpha t_i\ mod\ p-a_i|\leq p/2^k \]

构造格\(L\)

\[\left(\begin{array}{cccccc} p & 0 & 0 & \ldots & 0 & 0 \\ 0 & p & 0 & \ldots & 0 & 0 \\ & \vdots & & & & \vdots \\ 0 & 0 & 0 & \ldots & p & 0 \\ t_{1} & t_{2} & t_{3} & \ldots & t_{d} & 1 / p \end{array}\right) \]

注意对于最后一行乘\(\alpha\)后模\(p\)可以得到这样一个向量

\[v_{\alpha}=(r_1,\cdots,r_d,\alpha/p) \]

说明\(v_{\alpha}\)确实存在与格\(L\)中,其中 \(r_i=\alpha t_i\ mod\ p\),于是

\[|r_i - a_i|<p/2^k\quad i\in[1,d] \]

定义向量\(u=(a_1,\cdots,a_d,0)\),那么向量\(u\)\(v_{\alpha}\)的距离,即\(||u-v_{\alpha}||\)

\[||u-v_{\alpha}||<\sqrt{\sum^{d-1}_{i=1}(a_i-r_i)^2+(\frac{\alpha}{p})^2} \le \sqrt{d+1}p/2^k \]

那么我们就可以用LLL和Babai算法来寻找到\(v_{\alpha}\),对\(v_{\alpha}\)的最后一个元素乘\(p\)就求出了\(\alpha\)

Example & Demo

demo from kel.

from random import randint
from sage.all import *
from Crypto.Util.number import getPrime
# Some parameters of the game, chosen for simplicity.

# p - A prime number for our field.
p = getPrime(128)

# n - The number of bits in `p`.
n = ceil(log(p, 2))

# k - The number of significant bits revealed by the oracle.
# Using parameters from Thereom 1.
k = ceil(sqrt(n)) + ceil(log(n, 2))
print(f"The number of significant bits = {k}")
d = 2*ceil(sqrt(n))

def msb(query):
    """Returns the MSB of query based on the global paramters p, k.
    """
    while True:
        z = randint(1, p-1)
        answer = abs(query - z)
        if answer < p / 2**(k+1):
            break
    return z

def create_oracle(alpha):
    """Returns a randomized MSB oracle using the specified alpha value.
    """
    alpha = alpha
    def oracle():
        random_t = randint(1, p-1)
        return random_t, msb((alpha * random_t) % p)
    return oracle

def build_basis(oracle_inputs):
    """Returns a basis using the HNP game parameters and inputs to our oracle
    """
    basis_vectors = []
    for i in range(d):
        p_vector = [0] * (d+1)
        p_vector[i] = p
        basis_vectors.append(p_vector)
    basis_vectors.append(list(oracle_inputs) + [QQ(1)/QQ(p)])
    return Matrix(QQ, basis_vectors)

def approximate_closest_vector(basis, v):
    """Returns an approximate CVP solution using Babai's nearest plane algorithm.
    """
    BL = basis.LLL()
    G, _ = BL.gram_schmidt()
    _, n = BL.dimensions()
    small = vector(ZZ, v)
    for i in reversed(range(n)):
        c = QQ(small * G[i]) / QQ(G[i] * G[i])
        c = c.round()
        small -= BL[i] * c
    return (v - small).coefficients()

# Hidden alpha scalar
alpha = randint(1, p-1)

# Create a MSB oracle using the secret alpha scalar
oracle = create_oracle(alpha)

# Using terminology from the paper: inputs = `t` values, answers = `a` values
inputs, answers = zip(*[ oracle() for _ in range(d) ])

# Build a basis using our oracle inputs
lattice = build_basis(inputs)
print("Solving CVP using lattice with basis:\n%s\n" % str(lattice))

# The non-lattice vector based on the oracle's answers
u = vector(ZZ, list(answers) + [0])
print("Vector of MSB oracle answers:\n%s\n" % str(u))

# Solve an approximate CVP to find a vector v which is likely to reveal alpha.
v = approximate_closest_vector(lattice, u)
print("Closest lattice vector:\n%s\n" % str(v))

# Confirm the recovered value of alpha matches the expected value of alpha.
recovered_alpha = (v[-1] * p) % p
assert recovered_alpha == alpha
print("Recovered alpha! Alpha is %d" % recovered_alpha)

Refer to

  • The Hidden Number Problem
  • Hardness of computing the most significant bits of secret keys in Diffie-Hellman and related schemes” by Dan Boneh and Ramarathnam Venkatesan
posted @ 2023-02-12 17:22  tr0uble  阅读(488)  评论(1编辑  收藏  举报