Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unnecessary ruling out of zero in (CS)PRNG #107

Open
scrovy opened this issue Dec 22, 2022 · 6 comments
Open

Unnecessary ruling out of zero in (CS)PRNG #107

scrovy opened this issue Dec 22, 2022 · 6 comments

Comments

@scrovy
Copy link

scrovy commented Dec 22, 2022

Thanks for your implementation. Since this is audited, I'm using it as a basis for a (simplified) Python implementation of my own. Nevertheless I don't understand one thing: why do you restrict the cryptographically secure pseudo-random number generator to spit out non-zero coefficients for the polynomials? This reduces the search space slightly by 1/2^bits for each coefficient (for large bits this is not an issue but it is for small bits (for bits=1 the algorithm would break down completely since the coefficients would always be equal to 1, but fortunately you only allow bits>=3)).

secrets.js/secrets.js

Lines 228 to 231 in 14a4b68

// return null so this result can be re-processed if the result is all 0's.
if ((str.match(/0/g) || []).length === str.length) {
return null
}

Here, the construct function returns null on all-zeros (i.e., the zero vector)

secrets.js/secrets.js

Lines 252 to 255 in 14a4b68

while (str === null) {
buf = crypto.randomBytes(bytes)
str = construct(bits, buf.toString("hex"), radix, size)
}

secrets.js/secrets.js

Lines 273 to 280 in 14a4b68

while (str === null) {
str = construct(
bits,
crypto.getRandomValues(new Uint32Array(elems)),
radix,
size
)
}

In these two, you keep generating new PRNG numbers until they are not the zero vector. For bits=3 this means that the only coefficients allowed are $1, 2, 3, \ldots, 7 \in GF(8)$, but not 0.

@jeffkitson-music
Copy link

@scrovy Did you ever get this solved? Do you have a working version in Python? Is available? Cross-compatibility is important!

@scrovy
Copy link
Author

scrovy commented Jan 6, 2024

As far as I know it's a non crucial issue, especially for a large number of bits. Typically bits=8 is used. But in principle yes, you should allow $0 \in GF(2^{bits})$ as well.

@poing
Copy link

poing commented Mar 25, 2024

Do you have a working version in Python? Is available? Cross-compatibility is important!

@jeffkitson-music I just finished working on my own cross-compatible port of secrets.js to Python. You can find it here.

@poing
Copy link

poing commented Mar 25, 2024

To answer the non-zero question... I am sure it was a conscious decision.

If random provided $0$ for both coefficients $a_1$ and $a_2$, it could potentially reveal the secret.

$q(x) = a_0 + a_1x + \dotsi + a_{k-1}x^{k-1}$

$1234 + (0\times 1) + (0\times 1^2) = 1234$
$1234 + (0\times 2) + (0\times 2^2) = 1234$
$1234 + (0\times 3) + (0\times 3^2) = 1234$
$1234 + (0\times x) + (0\times x^2) = 1234$

@scrovy
Copy link
Author

scrovy commented Mar 25, 2024

What you're saying is that if all the coefficients $a_1, a_2, \ldots, a_k$ are identically zero, then your polynomial is constant hence the share is equal to the secret for any abscissa. Shouldn't then the check be $(a_1, a_2, \ldots, a_k) = 0$, I.e. all the coefficients equal to zero, instead of any coefficient equal to zero? Or is there an additional weakness I'm not seeing where a polynomial of the form
$a_0 + a_1 x + a_2 x^2 + \ldots + a_{k-1} x^{k-1}$
with some of the $a_i, i\geq 1$ but not all of them zero, discloses any information on $a_0$?

@poing
Copy link

poing commented Mar 25, 2024

@scrovy

Dividing a secret into pieces, with the simplified method, without the galileo field $GF(p)$, every $0$ on the right would reduce $k$, reducing the number of shares needed to reconstruct a secret.

$q(x) = a_0 + a_1x + a_2x^2 + a_3x^3 + a_4x^4\implies k=5$
$q(x) = a_0 + a_1x + a_2x^2 + a_3x^3 + 0x^4\implies k=4$
$q(x) = a_0 + a_1x + a_2x^2 + 0x^3 + 0x^4\implies k=3$
$q(x) = a_0 + a_1x + 0x^2 + 0x^3 + 0x^4\implies k=2$
$q(x) = a_0 + 0_1x + 0x^2 + 0x^3 + 0x^4\implies a_0\implies secret$

Interpolation doesn't use $k$, it just derives the values from the shares. It's a reason why for $k=3$, you can combine $3$ or $30$ shares and get the same result. The $0$'s on the right would be canceled out.

With a galileo field $GF(p)$, having a $0$ might not be an issue.
Since the polynomials involved in $k=3$ reconstruction [47, 123, 67] might become a 0, but $GF(p)$ maintain $k=3$, since reconstruction can't determine the value is $0$ without the correct number of shares.

Technically, according to the original paper... the polynomials used for $a_1 + \dotsi + a_{k-1}$ are supposed to be randomly chosen from a uniform distribution over the integers in $[0,p)$. Which implies $0$ would be valid.

$f(x_0)\implies (x_0, y_0)\implies D_0\implies a_0\implies secret$

Because $0$ implies the $secret$, using $(1,p)$ for random integers seems to be a safer path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants